From cbacb4686e2e3093186abb82d07867aa98812a1f Mon Sep 17 00:00:00 2001 From: Disant Upadhyay Date: Mon, 2 Oct 2023 16:55:38 -0230 Subject: [PATCH] requirements.txt Django added --- server/requirements.txt | 1 + .../site-packages/django/apps/registry 3.py | 436 + .../conf/locale/ar/LC_MESSAGES/django 3.po | 1389 +++ .../conf/locale/ar_DZ/LC_MESSAGES/django 3.po | 1397 +++ .../conf/locale/cs/LC_MESSAGES/django 3.po | 1362 +++ .../conf/locale/dsb/LC_MESSAGES/django 3.po | 1373 +++ .../conf/locale/el/LC_MESSAGES/django 3.po | 1332 +++ .../conf/locale/es_AR/LC_MESSAGES/django 3.po | 1357 +++ .../conf/locale/fa/LC_MESSAGES/django 3.po | 1327 +++ .../conf/locale/fi/LC_MESSAGES/django 3.po | 1316 +++ .../conf/locale/gd/LC_MESSAGES/django 3.po | 1386 +++ .../conf/locale/hsb/LC_MESSAGES/django 3.po | 1365 +++ .../conf/locale/ja/LC_MESSAGES/django 3.po | 1313 +++ .../conf/locale/kab/LC_MESSAGES/django 3.po | 1211 +++ .../conf/locale/km/LC_MESSAGES/django 3.po | 1196 +++ .../conf/locale/ko/LC_MESSAGES/django 3.po | 1305 +++ .../conf/locale/lb/LC_MESSAGES/django 3.po | 1213 +++ .../conf/locale/ml/LC_MESSAGES/django 3.po | 1274 +++ .../conf/locale/my/LC_MESSAGES/django 3.po | 1197 +++ .../conf/locale/ne/LC_MESSAGES/django 3.po | 1253 +++ .../conf/locale/pl/LC_MESSAGES/django 3.po | 1402 +++ .../conf/locale/pt_BR/LC_MESSAGES/django 3.po | 1381 +++ .../conf/locale/ru/LC_MESSAGES/django 3.po | 1399 +++ .../conf/locale/sk/LC_MESSAGES/django 3.po | 1354 +++ .../conf/locale/sq/LC_MESSAGES/django 3.po | 1334 +++ .../conf/locale/ta/LC_MESSAGES/django 3.po | 1230 +++ .../conf/locale/uk/LC_MESSAGES/django 3.po | 1365 +++ .../django/contrib/admin/checks 3.py | 1350 +++ .../django/contrib/admin/filters 3.py | 550 ++ .../django/contrib/admin/helpers 3.py | 555 ++ .../admin/locale/af/LC_MESSAGES/django 3.po | 720 ++ .../locale/ar_DZ/LC_MESSAGES/django 3.po | 738 ++ .../admin/locale/az/LC_MESSAGES/django 3.po | 732 ++ .../admin/locale/bg/LC_MESSAGES/django 3.po | 744 ++ .../admin/locale/bn/LC_MESSAGES/django 3.po | 713 ++ .../admin/locale/ca/LC_MESSAGES/django 3.po | 750 ++ .../admin/locale/ckb/LC_MESSAGES/django 3.po | 759 ++ .../admin/locale/da/LC_MESSAGES/django 3.po | 755 ++ .../admin/locale/dsb/LC_MESSAGES/django 3.po | 760 ++ .../admin/locale/eo/LC_MESSAGES/django 3.po | 728 ++ .../admin/locale/et/LC_MESSAGES/django 3.po | 753 ++ .../admin/locale/eu/LC_MESSAGES/django 3.po | 732 ++ .../admin/locale/fa/LC_MESSAGES/django 3.po | 751 ++ .../admin/locale/ga/LC_MESSAGES/django 3.po | 715 ++ .../admin/locale/gd/LC_MESSAGES/django 3.po | 754 ++ .../admin/locale/he/LC_MESSAGES/django 3.po | 717 ++ .../admin/locale/hi/LC_MESSAGES/django 3.po | 706 ++ .../admin/locale/hsb/LC_MESSAGES/django 3.po | 759 ++ .../admin/locale/hu/LC_MESSAGES/django 3.po | 731 ++ .../admin/locale/hy/LC_MESSAGES/django 3.po | 708 ++ .../admin/locale/ja/LC_MESSAGES/django 3.po | 751 ++ .../admin/locale/kk/LC_MESSAGES/django 3.po | 695 ++ .../admin/locale/ko/LC_MESSAGES/django 3.po | 768 ++ .../admin/locale/mk/LC_MESSAGES/django 3.po | 726 ++ .../admin/locale/nb/LC_MESSAGES/django 3.po | 720 ++ .../admin/locale/ne/LC_MESSAGES/django 3.po | 688 ++ .../admin/locale/nl/LC_MESSAGES/django 3.po | 752 ++ .../admin/locale/pl/LC_MESSAGES/django 3.po | 784 ++ .../admin/locale/pt/LC_MESSAGES/django 3.po | 725 ++ .../locale/pt_BR/LC_MESSAGES/django 3.po | 788 ++ .../admin/locale/sl/LC_MESSAGES/django 3.po | 690 ++ .../admin/locale/sr/LC_MESSAGES/django 3.po | 719 ++ .../admin/locale/sw/LC_MESSAGES/django 3.po | 676 ++ .../admin/locale/ta/LC_MESSAGES/django 3.po | 643 ++ .../admin/locale/te/LC_MESSAGES/django 3.po | 640 ++ .../admin/locale/tg/LC_MESSAGES/django 3.po | 699 ++ .../admin/locale/tr/LC_MESSAGES/django 3.po | 762 ++ .../admin/locale/tt/LC_MESSAGES/django 3.po | 655 ++ .../admin/locale/ur/LC_MESSAGES/django 3.po | 661 ++ .../admin/locale/vi/LC_MESSAGES/django 3.po | 726 ++ .../locale/zh_Hans/LC_MESSAGES/django 3.po | 744 ++ .../locale/zh_Hant/LC_MESSAGES/django 3.po | 660 ++ .../django/contrib/admin/options 3.py | 2501 +++++ .../django/contrib/admin/sites 3.py | 606 ++ .../django/contrib/auth/forms 3.py | 510 + .../django/contrib/auth/models 3.py | 499 + .../contrib/gis/db/models/functions 3.py | 564 ++ .../django/core/mail/message 3.py | 493 + .../site-packages/django/core/validators 3.py | 623 ++ .../django/db/backends/base/schema 3.py | 1829 ++++ .../django/db/backends/mysql/operations 3.py | 464 + .../django/db/backends/oracle/creation 3.py | 464 + .../django/db/backends/postgresql/base 3.py | 487 + .../db/backends/sqlite3/operations 3.py | 434 + .../django/db/migrations/executor 3.py | 410 + .../django/db/models/fields/__init__ 3.py | 2812 ++++++ .../django/db/models/fields/files 3.py | 510 + .../db/models/fields/related_descriptors 3.py | 1506 +++ .../site-packages/django/db/models/query 3.py | 2631 +++++ .../django/db/models/sql/compiler 3.py | 2096 ++++ .../site-packages/django/forms/fields 3.py | 1391 +++ .../site-packages/django/forms/formsets 3.py | 579 ++ .../site-packages/django/forms/models 3.py | 1660 ++++ .../site-packages/django/forms/widgets 3.py | 1202 +++ .../django/template/defaulttags 3.py | 1493 +++ .../site-packages/django/test/client 3.py | 1269 +++ .../site-packages/django/test/utils 3.py | 1002 ++ .../site-packages/django/urls/resolvers 3.py | 828 ++ .../django/utils/translation/trans_real 3.py | 639 ++ .../google/protobuf/descriptor_pb2 3.py | 1925 ++++ .../internal/descriptor_pool_test 3.py | 1149 +++ .../protobuf/internal/descriptor_test 3.py | 1076 +++ .../protobuf/internal/json_format_test 3.py | 1285 +++ .../protobuf/internal/python_message 3.py | 1539 +++ .../google/protobuf/internal/test_util 3.py | 878 ++ .../protobuf/internal/type_checkers 3.py | 435 + .../internal/unknown_fields_test 3.py | 461 + .../internal/well_known_types_test 3.py | 1013 ++ .../google/protobuf/json_format 3.py | 912 ++ .../google/protobuf/text_format 3.py | 1842 ++++ .../mysql/connector/abstracts 3.py | 1806 ++++ .../mysql/connector/connection 3.py | 1741 ++++ .../mysql/connector/connection_cext 3.py | 1004 ++ .../site-packages/mysql/connector/cursor 3.py | 1756 ++++ .../mysql/connector/django/introspection 3.py | 461 + .../opentelemetry/instrumentation 3.py | 514 + .../authentication_kerberos_client 3.py | 462 + .../authentication_ldap_sasl_client 3.py | 496 + .../mysql/connector/pooling 3.py | 622 ++ .../site-packages/mysql/connector/utils 3.py | 636 ++ .../sdk/metrics/_internal/aggregation 3.py | 1034 ++ .../opentelemetry/sdk/trace/__init__ 3.py | 1206 +++ .../sdk/trace/export/__init__ 3.py | 506 + .../mysql/opentelemetry/trace/__init__ 3.py | 623 ++ .../LICENSE 3.txt | 4054 ++++++++ .../site-packages/mysqlx/errorcode 3.py | 1877 ++++ .../python3.9/site-packages/mysqlx/expr 3.py | 1366 +++ .../site-packages/mysqlx/protocol 3.py | 1214 +++ .../pip/_internal/index/package_finder 3.py | 1029 ++ .../pip/_internal/models/link 3.py | 581 ++ .../pip/_internal/network/auth 3.py | 561 ++ .../pip/_internal/req/req_file 3.py | 552 ++ .../site-packages/pip/_internal/vcs/git 3.py | 526 + .../pip/_internal/vcs/versioncontrol 3.py | 705 ++ .../pip/_vendor/certifi/cacert 3.pem | 4589 +++++++++ .../pip/_vendor/chardet/gb2312freq 3.py | 284 + .../pip/_vendor/chardet/johabfreq 3.py | 2382 +++++ .../pip/_vendor/chardet/langgreekmodel 3.py | 4397 +++++++++ .../pip/_vendor/chardet/langrussianmodel 3.py | 5725 +++++++++++ .../pip/_vendor/chardet/langthaimodel 3.py | 4380 +++++++++ .../pip/_vendor/chardet/langturkishmodel 3.py | 4380 +++++++++ .../pip/_vendor/distlib/database 3.py | 1350 +++ .../pip/_vendor/distlib/index 3.py | 508 + .../pip/_vendor/distlib/scripts 3.py | 437 + .../pip/_vendor/distlib/wheel 3.py | 1082 +++ .../pip/_vendor/distro/distro 3.py | 1399 +++ .../pip/_vendor/idna/idnadata 3.py | 2151 +++++ .../pip/_vendor/idna/uts46data 3.py | 8600 +++++++++++++++++ .../pip/_vendor/pkg_resources/__init__ 3.py | 3361 +++++++ .../_vendor/pygments/filters/__init__ 3.py | 940 ++ .../pip/_vendor/pygments/formatters/html 3.py | 989 ++ .../pip/_vendor/pygments/formatters/img 3.py | 645 ++ .../pip/_vendor/pygments/lexer 3.py | 943 ++ .../pip/_vendor/pygments/lexers/_mapping 3.py | 559 ++ .../pip/_vendor/pygments/lexers/python 3.py | 1198 +++ .../pip/_vendor/pygments/unistring 3.py | 153 + .../pip/_vendor/pyparsing/core 3.py | 6115 ++++++++++++ .../pip/_vendor/requests/cookies 3.py | 561 ++ .../pip/_vendor/requests/models 3.py | 1034 ++ .../pip/_vendor/requests/utils 3.py | 1094 +++ .../site-packages/pip/_vendor/rich/color 3.py | 622 ++ .../pip/_vendor/rich/pretty 3.py | 994 ++ .../pip/_vendor/rich/progress 3.py | 1702 ++++ .../pip/_vendor/rich/syntax 3.py | 948 ++ .../pip/_vendor/typing_extensions 3.py | 3072 ++++++ .../pip/_vendor/urllib3/connection 3.py | 572 ++ .../_vendor/urllib3/contrib/pyopenssl 3.py | 518 + .../urllib3/contrib/securetransport 3.py | 921 ++ .../pip/_vendor/urllib3/packages/six 3.py | 1076 +++ .../pip/_vendor/urllib3/poolmanager 3.py | 537 + .../python_dotenv-1.0.0.dist-info/METADATA 3 | 667 ++ .../setuptools/_distutils/cmd 3.py | 403 + .../_distutils/command/build_ext 3.py | 757 ++ .../_distutils/command/build_py 3.py | 392 + .../_vendor/more_itertools/more 3.py | 3825 ++++++++ .../_vendor/more_itertools/recipes 3.py | 620 ++ .../setuptools/package_index 3.py | 1119 +++ .../python3.9/site-packages/sqlparse/sql 3.py | 645 ++ .../site-packages/typing_extensions 3.py | 2892 ++++++ 179 files changed, 215207 insertions(+) create mode 100644 virt/lib/python3.9/site-packages/django/apps/registry 3.py create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/ar/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/ar_DZ/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/cs/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/dsb/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/el/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/es_AR/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/fa/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/fi/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/gd/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/hsb/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/ja/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/kab/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/km/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/ko/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/lb/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/ml/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/my/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/ne/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/pl/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/pt_BR/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/ru/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/sk/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/sq/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/ta/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/conf/locale/uk/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/checks 3.py create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/filters 3.py create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/helpers 3.py create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/af/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/ar_DZ/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/az/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/bn/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/ckb/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/da/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/dsb/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/eo/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/et/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/eu/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/fa/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/ga/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/hi/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/hsb/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/hu/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/hy/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/kk/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/nb/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/ne/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/nl/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/pl/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/pt/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/pt_BR/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/sl/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/sr/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/sw/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/ta/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/te/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/tg/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/tr/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/tt/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/ur/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/zh_Hans/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/locale/zh_Hant/LC_MESSAGES/django 3.po create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/options 3.py create mode 100644 virt/lib/python3.9/site-packages/django/contrib/admin/sites 3.py create mode 100644 virt/lib/python3.9/site-packages/django/contrib/auth/forms 3.py create mode 100644 virt/lib/python3.9/site-packages/django/contrib/auth/models 3.py create mode 100644 virt/lib/python3.9/site-packages/django/contrib/gis/db/models/functions 3.py create mode 100644 virt/lib/python3.9/site-packages/django/core/mail/message 3.py create mode 100644 virt/lib/python3.9/site-packages/django/core/validators 3.py create mode 100644 virt/lib/python3.9/site-packages/django/db/backends/base/schema 3.py create mode 100644 virt/lib/python3.9/site-packages/django/db/backends/mysql/operations 3.py create mode 100644 virt/lib/python3.9/site-packages/django/db/backends/oracle/creation 3.py create mode 100644 virt/lib/python3.9/site-packages/django/db/backends/postgresql/base 3.py create mode 100644 virt/lib/python3.9/site-packages/django/db/backends/sqlite3/operations 3.py create mode 100644 virt/lib/python3.9/site-packages/django/db/migrations/executor 3.py create mode 100644 virt/lib/python3.9/site-packages/django/db/models/fields/__init__ 3.py create mode 100644 virt/lib/python3.9/site-packages/django/db/models/fields/files 3.py create mode 100644 virt/lib/python3.9/site-packages/django/db/models/fields/related_descriptors 3.py create mode 100644 virt/lib/python3.9/site-packages/django/db/models/query 3.py create mode 100644 virt/lib/python3.9/site-packages/django/db/models/sql/compiler 3.py create mode 100644 virt/lib/python3.9/site-packages/django/forms/fields 3.py create mode 100644 virt/lib/python3.9/site-packages/django/forms/formsets 3.py create mode 100644 virt/lib/python3.9/site-packages/django/forms/models 3.py create mode 100644 virt/lib/python3.9/site-packages/django/forms/widgets 3.py create mode 100644 virt/lib/python3.9/site-packages/django/template/defaulttags 3.py create mode 100644 virt/lib/python3.9/site-packages/django/test/client 3.py create mode 100644 virt/lib/python3.9/site-packages/django/test/utils 3.py create mode 100644 virt/lib/python3.9/site-packages/django/urls/resolvers 3.py create mode 100644 virt/lib/python3.9/site-packages/django/utils/translation/trans_real 3.py create mode 100644 virt/lib/python3.9/site-packages/google/protobuf/descriptor_pb2 3.py create mode 100644 virt/lib/python3.9/site-packages/google/protobuf/internal/descriptor_pool_test 3.py create mode 100644 virt/lib/python3.9/site-packages/google/protobuf/internal/descriptor_test 3.py create mode 100644 virt/lib/python3.9/site-packages/google/protobuf/internal/json_format_test 3.py create mode 100644 virt/lib/python3.9/site-packages/google/protobuf/internal/python_message 3.py create mode 100644 virt/lib/python3.9/site-packages/google/protobuf/internal/test_util 3.py create mode 100644 virt/lib/python3.9/site-packages/google/protobuf/internal/type_checkers 3.py create mode 100644 virt/lib/python3.9/site-packages/google/protobuf/internal/unknown_fields_test 3.py create mode 100644 virt/lib/python3.9/site-packages/google/protobuf/internal/well_known_types_test 3.py create mode 100644 virt/lib/python3.9/site-packages/google/protobuf/json_format 3.py create mode 100644 virt/lib/python3.9/site-packages/google/protobuf/text_format 3.py create mode 100644 virt/lib/python3.9/site-packages/mysql/connector/abstracts 3.py create mode 100644 virt/lib/python3.9/site-packages/mysql/connector/connection 3.py create mode 100644 virt/lib/python3.9/site-packages/mysql/connector/connection_cext 3.py create mode 100644 virt/lib/python3.9/site-packages/mysql/connector/cursor 3.py create mode 100644 virt/lib/python3.9/site-packages/mysql/connector/django/introspection 3.py create mode 100644 virt/lib/python3.9/site-packages/mysql/connector/opentelemetry/instrumentation 3.py create mode 100644 virt/lib/python3.9/site-packages/mysql/connector/plugins/authentication_kerberos_client 3.py create mode 100644 virt/lib/python3.9/site-packages/mysql/connector/plugins/authentication_ldap_sasl_client 3.py create mode 100644 virt/lib/python3.9/site-packages/mysql/connector/pooling 3.py create mode 100644 virt/lib/python3.9/site-packages/mysql/connector/utils 3.py create mode 100644 virt/lib/python3.9/site-packages/mysql/opentelemetry/sdk/metrics/_internal/aggregation 3.py create mode 100644 virt/lib/python3.9/site-packages/mysql/opentelemetry/sdk/trace/__init__ 3.py create mode 100644 virt/lib/python3.9/site-packages/mysql/opentelemetry/sdk/trace/export/__init__ 3.py create mode 100644 virt/lib/python3.9/site-packages/mysql/opentelemetry/trace/__init__ 3.py create mode 100644 virt/lib/python3.9/site-packages/mysql_connector_python-8.1.0.dist-info/LICENSE 3.txt create mode 100644 virt/lib/python3.9/site-packages/mysqlx/errorcode 3.py create mode 100644 virt/lib/python3.9/site-packages/mysqlx/expr 3.py create mode 100644 virt/lib/python3.9/site-packages/mysqlx/protocol 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_internal/index/package_finder 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_internal/models/link 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_internal/network/auth 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_internal/req/req_file 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_internal/vcs/git 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_internal/vcs/versioncontrol 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/certifi/cacert 3.pem create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/chardet/gb2312freq 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/chardet/johabfreq 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/chardet/langgreekmodel 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/chardet/langrussianmodel 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/chardet/langthaimodel 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/chardet/langturkishmodel 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/distlib/database 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/distlib/index 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/distlib/scripts 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/distlib/wheel 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/distro/distro 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/idna/idnadata 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/idna/uts46data 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/pkg_resources/__init__ 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/pygments/filters/__init__ 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/pygments/formatters/html 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/pygments/formatters/img 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/pygments/lexer 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/pygments/lexers/_mapping 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/pygments/lexers/python 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/pygments/unistring 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/pyparsing/core 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/requests/cookies 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/requests/models 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/requests/utils 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/rich/color 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/rich/pretty 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/rich/progress 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/rich/syntax 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/typing_extensions 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/urllib3/connection 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/pyopenssl 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/securetransport 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/urllib3/packages/six 3.py create mode 100644 virt/lib/python3.9/site-packages/pip/_vendor/urllib3/poolmanager 3.py create mode 100644 virt/lib/python3.9/site-packages/python_dotenv-1.0.0.dist-info/METADATA 3 create mode 100644 virt/lib/python3.9/site-packages/setuptools/_distutils/cmd 3.py create mode 100644 virt/lib/python3.9/site-packages/setuptools/_distutils/command/build_ext 3.py create mode 100644 virt/lib/python3.9/site-packages/setuptools/_distutils/command/build_py 3.py create mode 100644 virt/lib/python3.9/site-packages/setuptools/_vendor/more_itertools/more 3.py create mode 100644 virt/lib/python3.9/site-packages/setuptools/_vendor/more_itertools/recipes 3.py create mode 100644 virt/lib/python3.9/site-packages/setuptools/package_index 3.py create mode 100644 virt/lib/python3.9/site-packages/sqlparse/sql 3.py create mode 100644 virt/lib/python3.9/site-packages/typing_extensions 3.py diff --git a/server/requirements.txt b/server/requirements.txt index e69de29b..3badf470 100644 --- a/server/requirements.txt +++ b/server/requirements.txt @@ -0,0 +1 @@ +django==4.2.5 \ No newline at end of file diff --git a/virt/lib/python3.9/site-packages/django/apps/registry 3.py b/virt/lib/python3.9/site-packages/django/apps/registry 3.py new file mode 100644 index 00000000..40ceca08 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/apps/registry 3.py @@ -0,0 +1,436 @@ +import functools +import sys +import threading +import warnings +from collections import Counter, defaultdict +from functools import partial + +from django.core.exceptions import AppRegistryNotReady, ImproperlyConfigured + +from .config import AppConfig + + +class Apps: + """ + A registry that stores the configuration of installed applications. + + It also keeps track of models, e.g. to provide reverse relations. + """ + + def __init__(self, installed_apps=()): + # installed_apps is set to None when creating the main registry + # because it cannot be populated at that point. Other registries must + # provide a list of installed apps and are populated immediately. + if installed_apps is None and hasattr(sys.modules[__name__], "apps"): + raise RuntimeError("You must supply an installed_apps argument.") + + # Mapping of app labels => model names => model classes. Every time a + # model is imported, ModelBase.__new__ calls apps.register_model which + # creates an entry in all_models. All imported models are registered, + # regardless of whether they're defined in an installed application + # and whether the registry has been populated. Since it isn't possible + # to reimport a module safely (it could reexecute initialization code) + # all_models is never overridden or reset. + self.all_models = defaultdict(dict) + + # Mapping of labels to AppConfig instances for installed apps. + self.app_configs = {} + + # Stack of app_configs. Used to store the current state in + # set_available_apps and set_installed_apps. + self.stored_app_configs = [] + + # Whether the registry is populated. + self.apps_ready = self.models_ready = self.ready = False + # For the autoreloader. + self.ready_event = threading.Event() + + # Lock for thread-safe population. + self._lock = threading.RLock() + self.loading = False + + # Maps ("app_label", "modelname") tuples to lists of functions to be + # called when the corresponding model is ready. Used by this class's + # `lazy_model_operation()` and `do_pending_operations()` methods. + self._pending_operations = defaultdict(list) + + # Populate apps and models, unless it's the main registry. + if installed_apps is not None: + self.populate(installed_apps) + + def populate(self, installed_apps=None): + """ + Load application configurations and models. + + Import each application module and then each model module. + + It is thread-safe and idempotent, but not reentrant. + """ + if self.ready: + return + + # populate() might be called by two threads in parallel on servers + # that create threads before initializing the WSGI callable. + with self._lock: + if self.ready: + return + + # An RLock prevents other threads from entering this section. The + # compare and set operation below is atomic. + if self.loading: + # Prevent reentrant calls to avoid running AppConfig.ready() + # methods twice. + raise RuntimeError("populate() isn't reentrant") + self.loading = True + + # Phase 1: initialize app configs and import app modules. + for entry in installed_apps: + if isinstance(entry, AppConfig): + app_config = entry + else: + app_config = AppConfig.create(entry) + if app_config.label in self.app_configs: + raise ImproperlyConfigured( + "Application labels aren't unique, " + "duplicates: %s" % app_config.label + ) + + self.app_configs[app_config.label] = app_config + app_config.apps = self + + # Check for duplicate app names. + counts = Counter( + app_config.name for app_config in self.app_configs.values() + ) + duplicates = [name for name, count in counts.most_common() if count > 1] + if duplicates: + raise ImproperlyConfigured( + "Application names aren't unique, " + "duplicates: %s" % ", ".join(duplicates) + ) + + self.apps_ready = True + + # Phase 2: import models modules. + for app_config in self.app_configs.values(): + app_config.import_models() + + self.clear_cache() + + self.models_ready = True + + # Phase 3: run ready() methods of app configs. + for app_config in self.get_app_configs(): + app_config.ready() + + self.ready = True + self.ready_event.set() + + def check_apps_ready(self): + """Raise an exception if all apps haven't been imported yet.""" + if not self.apps_ready: + from django.conf import settings + + # If "not ready" is due to unconfigured settings, accessing + # INSTALLED_APPS raises a more helpful ImproperlyConfigured + # exception. + settings.INSTALLED_APPS + raise AppRegistryNotReady("Apps aren't loaded yet.") + + def check_models_ready(self): + """Raise an exception if all models haven't been imported yet.""" + if not self.models_ready: + raise AppRegistryNotReady("Models aren't loaded yet.") + + def get_app_configs(self): + """Import applications and return an iterable of app configs.""" + self.check_apps_ready() + return self.app_configs.values() + + def get_app_config(self, app_label): + """ + Import applications and returns an app config for the given label. + + Raise LookupError if no application exists with this label. + """ + self.check_apps_ready() + try: + return self.app_configs[app_label] + except KeyError: + message = "No installed app with label '%s'." % app_label + for app_config in self.get_app_configs(): + if app_config.name == app_label: + message += " Did you mean '%s'?" % app_config.label + break + raise LookupError(message) + + # This method is performance-critical at least for Django's test suite. + @functools.lru_cache(maxsize=None) + def get_models(self, include_auto_created=False, include_swapped=False): + """ + Return a list of all installed models. + + By default, the following models aren't included: + + - auto-created models for many-to-many relations without + an explicit intermediate table, + - models that have been swapped out. + + Set the corresponding keyword argument to True to include such models. + """ + self.check_models_ready() + + result = [] + for app_config in self.app_configs.values(): + result.extend(app_config.get_models(include_auto_created, include_swapped)) + return result + + def get_model(self, app_label, model_name=None, require_ready=True): + """ + Return the model matching the given app_label and model_name. + + As a shortcut, app_label may be in the form .. + + model_name is case-insensitive. + + Raise LookupError if no application exists with this label, or no + model exists with this name in the application. Raise ValueError if + called with a single argument that doesn't contain exactly one dot. + """ + if require_ready: + self.check_models_ready() + else: + self.check_apps_ready() + + if model_name is None: + app_label, model_name = app_label.split(".") + + app_config = self.get_app_config(app_label) + + if not require_ready and app_config.models is None: + app_config.import_models() + + return app_config.get_model(model_name, require_ready=require_ready) + + def register_model(self, app_label, model): + # Since this method is called when models are imported, it cannot + # perform imports because of the risk of import loops. It mustn't + # call get_app_config(). + model_name = model._meta.model_name + app_models = self.all_models[app_label] + if model_name in app_models: + if ( + model.__name__ == app_models[model_name].__name__ + and model.__module__ == app_models[model_name].__module__ + ): + warnings.warn( + "Model '%s.%s' was already registered. Reloading models is not " + "advised as it can lead to inconsistencies, most notably with " + "related models." % (app_label, model_name), + RuntimeWarning, + stacklevel=2, + ) + else: + raise RuntimeError( + "Conflicting '%s' models in application '%s': %s and %s." + % (model_name, app_label, app_models[model_name], model) + ) + app_models[model_name] = model + self.do_pending_operations(model) + self.clear_cache() + + def is_installed(self, app_name): + """ + Check whether an application with this name exists in the registry. + + app_name is the full name of the app e.g. 'django.contrib.admin'. + """ + self.check_apps_ready() + return any(ac.name == app_name for ac in self.app_configs.values()) + + def get_containing_app_config(self, object_name): + """ + Look for an app config containing a given object. + + object_name is the dotted Python path to the object. + + Return the app config for the inner application in case of nesting. + Return None if the object isn't in any registered app config. + """ + self.check_apps_ready() + candidates = [] + for app_config in self.app_configs.values(): + if object_name.startswith(app_config.name): + subpath = object_name[len(app_config.name) :] + if subpath == "" or subpath[0] == ".": + candidates.append(app_config) + if candidates: + return sorted(candidates, key=lambda ac: -len(ac.name))[0] + + def get_registered_model(self, app_label, model_name): + """ + Similar to get_model(), but doesn't require that an app exists with + the given app_label. + + It's safe to call this method at import time, even while the registry + is being populated. + """ + model = self.all_models[app_label].get(model_name.lower()) + if model is None: + raise LookupError("Model '%s.%s' not registered." % (app_label, model_name)) + return model + + @functools.lru_cache(maxsize=None) + def get_swappable_settings_name(self, to_string): + """ + For a given model string (e.g. "auth.User"), return the name of the + corresponding settings name if it refers to a swappable model. If the + referred model is not swappable, return None. + + This method is decorated with lru_cache because it's performance + critical when it comes to migrations. Since the swappable settings don't + change after Django has loaded the settings, there is no reason to get + the respective settings attribute over and over again. + """ + to_string = to_string.lower() + for model in self.get_models(include_swapped=True): + swapped = model._meta.swapped + # Is this model swapped out for the model given by to_string? + if swapped and swapped.lower() == to_string: + return model._meta.swappable + # Is this model swappable and the one given by to_string? + if model._meta.swappable and model._meta.label_lower == to_string: + return model._meta.swappable + return None + + def set_available_apps(self, available): + """ + Restrict the set of installed apps used by get_app_config[s]. + + available must be an iterable of application names. + + set_available_apps() must be balanced with unset_available_apps(). + + Primarily used for performance optimization in TransactionTestCase. + + This method is safe in the sense that it doesn't trigger any imports. + """ + available = set(available) + installed = {app_config.name for app_config in self.get_app_configs()} + if not available.issubset(installed): + raise ValueError( + "Available apps isn't a subset of installed apps, extra apps: %s" + % ", ".join(available - installed) + ) + + self.stored_app_configs.append(self.app_configs) + self.app_configs = { + label: app_config + for label, app_config in self.app_configs.items() + if app_config.name in available + } + self.clear_cache() + + def unset_available_apps(self): + """Cancel a previous call to set_available_apps().""" + self.app_configs = self.stored_app_configs.pop() + self.clear_cache() + + def set_installed_apps(self, installed): + """ + Enable a different set of installed apps for get_app_config[s]. + + installed must be an iterable in the same format as INSTALLED_APPS. + + set_installed_apps() must be balanced with unset_installed_apps(), + even if it exits with an exception. + + Primarily used as a receiver of the setting_changed signal in tests. + + This method may trigger new imports, which may add new models to the + registry of all imported models. They will stay in the registry even + after unset_installed_apps(). Since it isn't possible to replay + imports safely (e.g. that could lead to registering listeners twice), + models are registered when they're imported and never removed. + """ + if not self.ready: + raise AppRegistryNotReady("App registry isn't ready yet.") + self.stored_app_configs.append(self.app_configs) + self.app_configs = {} + self.apps_ready = self.models_ready = self.loading = self.ready = False + self.clear_cache() + self.populate(installed) + + def unset_installed_apps(self): + """Cancel a previous call to set_installed_apps().""" + self.app_configs = self.stored_app_configs.pop() + self.apps_ready = self.models_ready = self.ready = True + self.clear_cache() + + def clear_cache(self): + """ + Clear all internal caches, for methods that alter the app registry. + + This is mostly used in tests. + """ + # Call expire cache on each model. This will purge + # the relation tree and the fields cache. + self.get_models.cache_clear() + if self.ready: + # Circumvent self.get_models() to prevent that the cache is refilled. + # This particularly prevents that an empty value is cached while cloning. + for app_config in self.app_configs.values(): + for model in app_config.get_models(include_auto_created=True): + model._meta._expire_cache() + + def lazy_model_operation(self, function, *model_keys): + """ + Take a function and a number of ("app_label", "modelname") tuples, and + when all the corresponding models have been imported and registered, + call the function with the model classes as its arguments. + + The function passed to this method must accept exactly n models as + arguments, where n=len(model_keys). + """ + # Base case: no arguments, just execute the function. + if not model_keys: + function() + # Recursive case: take the head of model_keys, wait for the + # corresponding model class to be imported and registered, then apply + # that argument to the supplied function. Pass the resulting partial + # to lazy_model_operation() along with the remaining model args and + # repeat until all models are loaded and all arguments are applied. + else: + next_model, *more_models = model_keys + + # This will be executed after the class corresponding to next_model + # has been imported and registered. The `func` attribute provides + # duck-type compatibility with partials. + def apply_next_model(model): + next_function = partial(apply_next_model.func, model) + self.lazy_model_operation(next_function, *more_models) + + apply_next_model.func = function + + # If the model has already been imported and registered, partially + # apply it to the function now. If not, add it to the list of + # pending operations for the model, where it will be executed with + # the model class as its sole argument once the model is ready. + try: + model_class = self.get_registered_model(*next_model) + except LookupError: + self._pending_operations[next_model].append(apply_next_model) + else: + apply_next_model(model_class) + + def do_pending_operations(self, model): + """ + Take a newly-prepared model and pass it to each function waiting for + it. This is called at the very end of Apps.register_model(). + """ + key = model._meta.app_label, model._meta.model_name + for function in self._pending_operations.pop(key, []): + function(model) + + +apps = Apps(installed_apps=None) diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/ar/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/ar/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..9bce2328 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/ar/LC_MESSAGES/django 3.po @@ -0,0 +1,1389 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Bashar Al-Abdulhadi, 2015-2016,2020-2021 +# Bashar Al-Abdulhadi, 2014 +# Eyad Toma , 2013-2014 +# Jannis Leidel , 2011 +# Mariusz Felisiak , 2021 +# Muaaz Alsaied, 2020 +# Omar Al-Ithawi , 2020 +# Ossama Khayat , 2011 +# Tony xD , 2020 +# صفا الفليج , 2020 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-09-21 10:22+0200\n" +"PO-Revision-Date: 2021-11-24 16:27+0000\n" +"Last-Translator: Mariusz Felisiak \n" +"Language-Team: Arabic (http://www.transifex.com/django/django/language/ar/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ar\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " +"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" + +msgid "Afrikaans" +msgstr "الإفريقية" + +msgid "Arabic" +msgstr "العربيّة" + +msgid "Algerian Arabic" +msgstr "عربي جزائري" + +msgid "Asturian" +msgstr "الأسترية" + +msgid "Azerbaijani" +msgstr "الأذربيجانية" + +msgid "Bulgarian" +msgstr "البلغاريّة" + +msgid "Belarusian" +msgstr "البيلاروسية" + +msgid "Bengali" +msgstr "البنغاليّة" + +msgid "Breton" +msgstr "البريتونية" + +msgid "Bosnian" +msgstr "البوسنيّة" + +msgid "Catalan" +msgstr "الكتلانيّة" + +msgid "Czech" +msgstr "التشيكيّة" + +msgid "Welsh" +msgstr "الويلز" + +msgid "Danish" +msgstr "الدنماركيّة" + +msgid "German" +msgstr "الألمانيّة" + +msgid "Lower Sorbian" +msgstr "الصربية السفلى" + +msgid "Greek" +msgstr "اليونانيّة" + +msgid "English" +msgstr "الإنجليزيّة" + +msgid "Australian English" +msgstr "الإنجليزية الإسترالية" + +msgid "British English" +msgstr "الإنجليزيّة البريطانيّة" + +msgid "Esperanto" +msgstr "الاسبرانتو" + +msgid "Spanish" +msgstr "الإسبانيّة" + +msgid "Argentinian Spanish" +msgstr "الأسبانية الأرجنتينية" + +msgid "Colombian Spanish" +msgstr "الكولومبية الإسبانية" + +msgid "Mexican Spanish" +msgstr "الأسبانية المكسيكية" + +msgid "Nicaraguan Spanish" +msgstr "الإسبانية النيكاراغوية" + +msgid "Venezuelan Spanish" +msgstr "الإسبانية الفنزويلية" + +msgid "Estonian" +msgstr "الإستونيّة" + +msgid "Basque" +msgstr "الباسك" + +msgid "Persian" +msgstr "الفارسيّة" + +msgid "Finnish" +msgstr "الفنلنديّة" + +msgid "French" +msgstr "الفرنسيّة" + +msgid "Frisian" +msgstr "الفريزيّة" + +msgid "Irish" +msgstr "الإيرلنديّة" + +msgid "Scottish Gaelic" +msgstr "الغيلية الأسكتلندية" + +msgid "Galician" +msgstr "الجليقيّة" + +msgid "Hebrew" +msgstr "العبريّة" + +msgid "Hindi" +msgstr "الهندية" + +msgid "Croatian" +msgstr "الكرواتيّة" + +msgid "Upper Sorbian" +msgstr "الصربية العليا" + +msgid "Hungarian" +msgstr "الهنغاريّة" + +msgid "Armenian" +msgstr "الأرمنية" + +msgid "Interlingua" +msgstr "اللغة الوسيطة" + +msgid "Indonesian" +msgstr "الإندونيسيّة" + +msgid "Igbo" +msgstr "الإيبو" + +msgid "Ido" +msgstr "ايدو" + +msgid "Icelandic" +msgstr "الآيسلنديّة" + +msgid "Italian" +msgstr "الإيطاليّة" + +msgid "Japanese" +msgstr "اليابانيّة" + +msgid "Georgian" +msgstr "الجورجيّة" + +msgid "Kabyle" +msgstr "القبائل" + +msgid "Kazakh" +msgstr "الكازاخستانية" + +msgid "Khmer" +msgstr "الخمر" + +msgid "Kannada" +msgstr "الهنديّة (كنّادا)" + +msgid "Korean" +msgstr "الكوريّة" + +msgid "Kyrgyz" +msgstr "قيرغيز" + +msgid "Luxembourgish" +msgstr "اللوكسمبرجية" + +msgid "Lithuanian" +msgstr "اللتوانيّة" + +msgid "Latvian" +msgstr "اللاتفيّة" + +msgid "Macedonian" +msgstr "المقدونيّة" + +msgid "Malayalam" +msgstr "المايالام" + +msgid "Mongolian" +msgstr "المنغوليّة" + +msgid "Marathi" +msgstr "المهاراتية" + +msgid "Malay" +msgstr "" + +msgid "Burmese" +msgstr "البورمية" + +msgid "Norwegian Bokmål" +msgstr "النرويجية" + +msgid "Nepali" +msgstr "النيبالية" + +msgid "Dutch" +msgstr "الهولنديّة" + +msgid "Norwegian Nynorsk" +msgstr "النينورسك نرويجيّة" + +msgid "Ossetic" +msgstr "الأوسيتيكية" + +msgid "Punjabi" +msgstr "البنجابيّة" + +msgid "Polish" +msgstr "البولنديّة" + +msgid "Portuguese" +msgstr "البرتغاليّة" + +msgid "Brazilian Portuguese" +msgstr "البرتغاليّة البرازيليّة" + +msgid "Romanian" +msgstr "الرومانيّة" + +msgid "Russian" +msgstr "الروسيّة" + +msgid "Slovak" +msgstr "السلوفاكيّة" + +msgid "Slovenian" +msgstr "السلوفانيّة" + +msgid "Albanian" +msgstr "الألبانيّة" + +msgid "Serbian" +msgstr "الصربيّة" + +msgid "Serbian Latin" +msgstr "اللاتينيّة الصربيّة" + +msgid "Swedish" +msgstr "السويديّة" + +msgid "Swahili" +msgstr "السواحلية" + +msgid "Tamil" +msgstr "التاميل" + +msgid "Telugu" +msgstr "التيلوغو" + +msgid "Tajik" +msgstr "طاجيك" + +msgid "Thai" +msgstr "التايلنديّة" + +msgid "Turkmen" +msgstr "تركمان" + +msgid "Turkish" +msgstr "التركيّة" + +msgid "Tatar" +msgstr "التتاريية" + +msgid "Udmurt" +msgstr "الأدمرتية" + +msgid "Ukrainian" +msgstr "الأكرانيّة" + +msgid "Urdu" +msgstr "الأوردو" + +msgid "Uzbek" +msgstr "الأوزبكي" + +msgid "Vietnamese" +msgstr "الفيتناميّة" + +msgid "Simplified Chinese" +msgstr "الصينيّة المبسطة" + +msgid "Traditional Chinese" +msgstr "الصينيّة التقليدية" + +msgid "Messages" +msgstr "الرسائل" + +msgid "Site Maps" +msgstr "خرائط الموقع" + +msgid "Static Files" +msgstr "الملفات الثابتة" + +msgid "Syndication" +msgstr "توظيف النشر" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "..." + +msgid "That page number is not an integer" +msgstr "رقم الصفحة هذا ليس عدداً طبيعياً" + +msgid "That page number is less than 1" +msgstr "رقم الصفحة أقل من 1" + +msgid "That page contains no results" +msgstr "هذه الصفحة لا تحتوي على نتائج" + +msgid "Enter a valid value." +msgstr "أدخِل قيمة صحيحة." + +msgid "Enter a valid URL." +msgstr "أدخِل رابطًا صحيحًا." + +msgid "Enter a valid integer." +msgstr "أدخِل عدداً طبيعياً." + +msgid "Enter a valid email address." +msgstr "أدخِل عنوان بريد إلكتروني صحيح." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "أدخل اختصار 'slug' صحيح يتكوّن من أحرف، أرقام، شرطات سفلية وعاديّة." + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"أدخل اختصار 'slug' صحيح يتكون من أحرف Unicode أو أرقام أو شرطات سفلية أو " +"واصلات." + +msgid "Enter a valid IPv4 address." +msgstr "أدخِل عنوان IPv4 صحيح." + +msgid "Enter a valid IPv6 address." +msgstr "أدخِل عنوان IPv6 صحيح." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "أدخِل عنوان IPv4 أو عنوان IPv6 صحيح." + +msgid "Enter only digits separated by commas." +msgstr "أدخِل فقط أرقامًا تفصلها الفواصل." + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "تحقق من أن هذه القيمة هي %(limit_value)s (إنها %(show_value)s)." + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "تحقق من أن تكون هذه القيمة أقل من %(limit_value)s أو مساوية لها." + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "تحقق من أن تكون هذه القيمة أكثر من %(limit_value)s أو مساوية لها." + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأقل (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[1] "" +"تأكد أن هذه القيمة تحتوي على حرف أو رمز %(limit_value)d على الأقل (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[2] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف و رمز على الأقل (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[3] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأقل (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[4] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأقل (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[5] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأقل (هي تحتوي " +"حالياً على %(show_value)d)." + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأكثر (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[1] "" +"تأكد أن هذه القيمة تحتوي على حرف أو رمز %(limit_value)d على الأكثر (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[2] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف و رمز على الأكثر (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[3] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأكثر (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[4] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأكثر (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[5] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأكثر (هي تحتوي " +"حالياً على %(show_value)d)." + +msgid "Enter a number." +msgstr "أدخل رقماً." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "تحقق من أن تدخل %(max)s أرقام لا أكثر." +msgstr[1] "تحقق من أن تدخل رقم %(max)s لا أكثر." +msgstr[2] "تحقق من أن تدخل %(max)s رقمين لا أكثر." +msgstr[3] "تحقق من أن تدخل %(max)s أرقام لا أكثر." +msgstr[4] "تحقق من أن تدخل %(max)s أرقام لا أكثر." +msgstr[5] "تحقق من أن تدخل %(max)s أرقام لا أكثر." + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "تحقق من أن تدخل %(max)s خانات عشرية لا أكثر." +msgstr[1] "تحقق من أن تدخل خانة %(max)s عشرية لا أكثر." +msgstr[2] "تحقق من أن تدخل %(max)s خانتين عشريتين لا أكثر." +msgstr[3] "تحقق من أن تدخل %(max)s خانات عشرية لا أكثر." +msgstr[4] "تحقق من أن تدخل %(max)s خانات عشرية لا أكثر." +msgstr[5] "تحقق من أن تدخل %(max)s خانات عشرية لا أكثر." + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "تحقق من أن تدخل %(max)s أرقام قبل الفاصل العشري لا أكثر." +msgstr[1] "تحقق من أن تدخل رقم %(max)s قبل الفاصل العشري لا أكثر." +msgstr[2] "تحقق من أن تدخل %(max)s رقمين قبل الفاصل العشري لا أكثر." +msgstr[3] "تحقق من أن تدخل %(max)s أرقام قبل الفاصل العشري لا أكثر." +msgstr[4] "تحقق من أن تدخل %(max)s أرقام قبل الفاصل العشري لا أكثر." +msgstr[5] "تحقق من أن تدخل %(max)s أرقام قبل الفاصل العشري لا أكثر." + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"امتداد الملف “%(extension)s” غير مسموح به. الامتدادات المسموح بها هي:" +"%(allowed_extensions)s." + +msgid "Null characters are not allowed." +msgstr "الأحرف الخالية غير مسموح بها." + +msgid "and" +msgstr "و" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "%(model_name)s بهذا %(field_labels)s موجود سلفاً." + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "القيمة %(value)r ليست خيارا صحيحاً." + +msgid "This field cannot be null." +msgstr "لا يمكن تعيين null كقيمة لهذا الحقل." + +msgid "This field cannot be blank." +msgstr "لا يمكن ترك هذا الحقل فارغاً." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "النموذج %(model_name)s والحقل %(field_label)s موجود مسبقاً." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. +#. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"%(field_label)s يجب أن يكون فريد لـ %(date_field_label)s %(lookup_type)s." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "حقل نوع: %(field_type)s" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "قيمة '%(value)s' يجب أن تكون True أو False." + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "قيمة “%(value)s” يجب أن تكون True , False أو None." + +msgid "Boolean (Either True or False)" +msgstr "ثنائي (إما True أو False)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "سلسلة نص (%(max_length)s كحد أقصى)" + +msgid "Comma-separated integers" +msgstr "أرقام صحيحة مفصولة بفواصل" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" +"قيمة '%(value)s' ليست من بُنية تاريخ صحيحة. القيمة يجب ان تكون من البُنية YYYY-" +"MM-DD." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "قيمة '%(value)s' من بُنية صحيحة (YYYY-MM-DD) لكنها تحوي تاريخ غير صحيح." + +msgid "Date (without time)" +msgstr "التاريخ (دون الوقت)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"قيمة '%(value)s' ليست من بُنية صحيحة. القيمة يجب ان تكون من البُنية YYYY-MM-DD " +"HH:MM[:ss[.uuuuuu]][TZ] ." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"قيمة '%(value)s' من بُنية صحيحة (YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) لكنها " +"تحوي وقت و تاريخ غير صحيحين." + +msgid "Date (with time)" +msgstr "التاريخ (مع الوقت)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "قيمة '%(value)s' يجب ان تكون عدد عشري." + +msgid "Decimal number" +msgstr "رقم عشري" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" +"قيمة '%(value)s' ليست بنسق صحيح. القيمة يجب ان تكون من التنسيق ([DD] " +"[[HH:]MM:]ss[.uuuuuu])" + +msgid "Duration" +msgstr "المدّة" + +msgid "Email address" +msgstr "عنوان بريد إلكتروني" + +msgid "File path" +msgstr "مسار الملف" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "قيمة '%(value)s' يجب ان تكون عدد تعويم." + +msgid "Floating point number" +msgstr "رقم فاصلة عائمة" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "قيمة '%(value)s' يجب ان تكون عدد طبيعي." + +msgid "Integer" +msgstr "عدد صحيح" + +msgid "Big (8 byte) integer" +msgstr "عدد صحيح كبير (8 بايت)" + +msgid "Small integer" +msgstr "عدد صحيح صغير" + +msgid "IPv4 address" +msgstr "عنوان IPv4" + +msgid "IP address" +msgstr "عنوان IP" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "قيمة '%(value)s' يجب ان تكون None أو True أو False." + +msgid "Boolean (Either True, False or None)" +msgstr "ثنائي (إما True أو False أو None)" + +msgid "Positive big integer" +msgstr "عدد صحيح موجب كبير" + +msgid "Positive integer" +msgstr "عدد صحيح موجب" + +msgid "Positive small integer" +msgstr "عدد صحيح صغير موجب" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "Slug (حتى %(max_length)s)" + +msgid "Text" +msgstr "نص" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" +"قيمة '%(value)s' ليست بنسق صحيح. القيمة يجب ان تكون من التنسيق\n" +"HH:MM[:ss[.uuuuuu]]" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" +"قيمة '%(value)s' من بُنية صحيحة (HH:MM[:ss[.uuuuuu]]) لكنها تحوي وقت غير صحيح." + +msgid "Time" +msgstr "وقت" + +msgid "URL" +msgstr "رابط" + +msgid "Raw binary data" +msgstr "البيانات الثنائية الخام" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "القيمة \"%(value)s\" ليست UUID صالح." + +msgid "Universally unique identifier" +msgstr "معرّف فريد عالمياً" + +msgid "File" +msgstr "ملف" + +msgid "Image" +msgstr "صورة" + +msgid "A JSON object" +msgstr "كائن JSON" + +msgid "Value must be valid JSON." +msgstr "يجب أن تكون قيمة JSON صالحة." + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "النموذج %(model)s ذو الحقل و القيمة %(field)s %(value)r غير موجود." + +msgid "Foreign Key (type determined by related field)" +msgstr "الحقل المرتبط (تم تحديد النوع وفقاً للحقل المرتبط)" + +msgid "One-to-one relationship" +msgstr "علاقة واحد إلى واحد" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "%(from)s-%(to)s علاقة" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "%(from)s-%(to)s علاقات" + +msgid "Many-to-many relationship" +msgstr "علاقة متعدد إلى متعدد" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "هذا الحقل مطلوب." + +msgid "Enter a whole number." +msgstr "أدخل رقما صحيحا." + +msgid "Enter a valid date." +msgstr "أدخل تاريخاً صحيحاً." + +msgid "Enter a valid time." +msgstr "أدخل وقتاً صحيحاً." + +msgid "Enter a valid date/time." +msgstr "أدخل تاريخاً/وقتاً صحيحاً." + +msgid "Enter a valid duration." +msgstr "أدخل مدّة صحيحة" + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "يجب أن يكون عدد الأيام بين {min_days} و {max_days}." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "لم يتم ارسال ملف، الرجاء التأكد من نوع ترميز الاستمارة." + +msgid "No file was submitted." +msgstr "لم يتم إرسال اي ملف." + +msgid "The submitted file is empty." +msgstr "الملف الذي قمت بإرساله فارغ." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"تأكد أن إسم هذا الملف يحتوي على %(max)d حرف على الأكثر (هو يحتوي الآن على " +"%(length)d حرف)." +msgstr[1] "" +"تأكد أن إسم هذا الملف يحتوي على حرف %(max)d على الأكثر (هو يحتوي الآن على " +"%(length)d حرف)." +msgstr[2] "" +"تأكد أن إسم هذا الملف يحتوي على %(max)d حرفين على الأكثر (هو يحتوي الآن على " +"%(length)d حرف)." +msgstr[3] "" +"تأكد أن إسم هذا الملف يحتوي على %(max)d حرف على الأكثر (هو يحتوي الآن على " +"%(length)d حرف)." +msgstr[4] "" +"تأكد أن إسم هذا الملف يحتوي على %(max)d حرف على الأكثر (هو يحتوي الآن على " +"%(length)d حرف)." +msgstr[5] "" +"تأكد أن إسم هذا الملف يحتوي على %(max)d حرف على الأكثر (هو يحتوي الآن على " +"%(length)d حرف)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "رجاءً أرسل ملف أو صح علامة صح عند مربع اختيار \"فارغ\"، وليس كلاهما." + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"قم برفع صورة صحيحة، الملف الذي قمت برفعه إما أنه ليس ملفا لصورة أو أنه ملف " +"معطوب." + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "انتق خياراً صحيحاً. %(value)s ليس أحد الخيارات المتاحة." + +msgid "Enter a list of values." +msgstr "أدخل قائمة من القيم." + +msgid "Enter a complete value." +msgstr "إدخال قيمة كاملة." + +msgid "Enter a valid UUID." +msgstr "أدخل قيمة UUID صحيحة." + +msgid "Enter a valid JSON." +msgstr "أدخل مدخل JSON صالح." + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(الحقل الخفي %(name)s) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" +"بيانات نموذج الإدارة مفقودة أو تم العبث بها. الحقول المفقودة: " +"%(field_names)s. قد تحتاج إلى تقديم تقرير خطأ إذا استمرت المشكلة." + +#, python-format +msgid "Please submit at most %d form." +msgid_plural "Please submit at most %d forms." +msgstr[0] "الرجاء إرسال %d إستمارة على الأكثر." +msgstr[1] "الرجاء إرسال %d إستمارة على الأكثر." +msgstr[2] "الرجاء إرسال %d إستمارة على الأكثر." +msgstr[3] "الرجاء إرسال %d إستمارة على الأكثر." +msgstr[4] "الرجاء إرسال %d إستمارة على الأكثر." +msgstr[5] "الرجاء إرسال %d إستمارة على الأكثر." + +#, python-format +msgid "Please submit at least %d form." +msgid_plural "Please submit at least %d forms." +msgstr[0] "الرجاء إرسال %d إستمارة على الأقل." +msgstr[1] "الرجاء إرسال %d إستمارة على الأقل." +msgstr[2] "الرجاء إرسال %d إستمارة على الأقل." +msgstr[3] "الرجاء إرسال %d إستمارة على الأقل." +msgstr[4] "الرجاء إرسال %d إستمارة على الأقل." +msgstr[5] "الرجاء إرسال %d إستمارة على الأقل." + +msgid "Order" +msgstr "الترتيب" + +msgid "Delete" +msgstr "احذف" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "رجاء صحّح بيانات %(field)s المتكررة." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "رجاء صحّح بيانات %(field)s المتكررة والتي يجب أن تكون مُميّزة." + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"رجاء صحّح بيانات %(field_name)s المتكررة والتي يجب أن تكون مُميّزة لـ%(lookup)s " +"في %(date_field)s." + +msgid "Please correct the duplicate values below." +msgstr "رجاءً صحّح القيم المُكرّرة أدناه." + +msgid "The inline value did not match the parent instance." +msgstr "لا تتطابق القيمة المضمنة مع المثيل الأصلي." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "انتق خياراً صحيحاً. اختيارك ليس أحد الخيارات المتاحة." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "\"%(pk)s\" ليست قيمة صالحة." + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"%(datetime)s لا يمكن تفسيرها في المنطقة الزمنية %(current_timezone)s; قد " +"تكون غامضة أو أنها غير موجودة." + +msgid "Clear" +msgstr "تفريغ" + +msgid "Currently" +msgstr "حالياً" + +msgid "Change" +msgstr "عدّل" + +msgid "Unknown" +msgstr "مجهول" + +msgid "Yes" +msgstr "نعم" + +msgid "No" +msgstr "لا" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "نعم,لا,ربما" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d بايت" +msgstr[1] "بايت واحد" +msgstr[2] "بايتان" +msgstr[3] "%(size)d بايتان" +msgstr[4] "%(size)d بايت" +msgstr[5] "%(size)d بايت" + +#, python-format +msgid "%s KB" +msgstr "%s ك.ب" + +#, python-format +msgid "%s MB" +msgstr "%s م.ب" + +#, python-format +msgid "%s GB" +msgstr "%s ج.ب" + +#, python-format +msgid "%s TB" +msgstr "%s ت.ب" + +#, python-format +msgid "%s PB" +msgstr "%s ب.ب" + +msgid "p.m." +msgstr "م" + +msgid "a.m." +msgstr "ص" + +msgid "PM" +msgstr "م" + +msgid "AM" +msgstr "ص" + +msgid "midnight" +msgstr "منتصف الليل" + +msgid "noon" +msgstr "ظهراً" + +msgid "Monday" +msgstr "الاثنين" + +msgid "Tuesday" +msgstr "الثلاثاء" + +msgid "Wednesday" +msgstr "الأربعاء" + +msgid "Thursday" +msgstr "الخميس" + +msgid "Friday" +msgstr "الجمعة" + +msgid "Saturday" +msgstr "السبت" + +msgid "Sunday" +msgstr "الأحد" + +msgid "Mon" +msgstr "إثنين" + +msgid "Tue" +msgstr "ثلاثاء" + +msgid "Wed" +msgstr "أربعاء" + +msgid "Thu" +msgstr "خميس" + +msgid "Fri" +msgstr "جمعة" + +msgid "Sat" +msgstr "سبت" + +msgid "Sun" +msgstr "أحد" + +msgid "January" +msgstr "يناير" + +msgid "February" +msgstr "فبراير" + +msgid "March" +msgstr "مارس" + +msgid "April" +msgstr "إبريل" + +msgid "May" +msgstr "مايو" + +msgid "June" +msgstr "يونيو" + +msgid "July" +msgstr "يوليو" + +msgid "August" +msgstr "أغسطس" + +msgid "September" +msgstr "سبتمبر" + +msgid "October" +msgstr "أكتوبر" + +msgid "November" +msgstr "نوفمبر" + +msgid "December" +msgstr "ديسمبر" + +msgid "jan" +msgstr "يناير" + +msgid "feb" +msgstr "فبراير" + +msgid "mar" +msgstr "مارس" + +msgid "apr" +msgstr "إبريل" + +msgid "may" +msgstr "مايو" + +msgid "jun" +msgstr "يونيو" + +msgid "jul" +msgstr "يوليو" + +msgid "aug" +msgstr "أغسطس" + +msgid "sep" +msgstr "سبتمبر" + +msgid "oct" +msgstr "أكتوبر" + +msgid "nov" +msgstr "نوفمبر" + +msgid "dec" +msgstr "ديسمبر" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "يناير" + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "فبراير" + +msgctxt "abbrev. month" +msgid "March" +msgstr "مارس" + +msgctxt "abbrev. month" +msgid "April" +msgstr "إبريل" + +msgctxt "abbrev. month" +msgid "May" +msgstr "مايو" + +msgctxt "abbrev. month" +msgid "June" +msgstr "يونيو" + +msgctxt "abbrev. month" +msgid "July" +msgstr "يوليو" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "أغسطس" + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "سبتمبر" + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "أكتوبر" + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "نوفمبر" + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "ديسمبر" + +msgctxt "alt. month" +msgid "January" +msgstr "يناير" + +msgctxt "alt. month" +msgid "February" +msgstr "فبراير" + +msgctxt "alt. month" +msgid "March" +msgstr "مارس" + +msgctxt "alt. month" +msgid "April" +msgstr "أبريل" + +msgctxt "alt. month" +msgid "May" +msgstr "مايو" + +msgctxt "alt. month" +msgid "June" +msgstr "يونيو" + +msgctxt "alt. month" +msgid "July" +msgstr "يوليو" + +msgctxt "alt. month" +msgid "August" +msgstr "أغسطس" + +msgctxt "alt. month" +msgid "September" +msgstr "سبتمبر" + +msgctxt "alt. month" +msgid "October" +msgstr "أكتوبر" + +msgctxt "alt. month" +msgid "November" +msgstr "نوفمبر" + +msgctxt "alt. month" +msgid "December" +msgstr "ديسمبر" + +msgid "This is not a valid IPv6 address." +msgstr "هذا ليس عنوان IPv6 صحيح." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "أو" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr "، " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "%(num)d سنة" +msgstr[1] "%(num)d سنة" +msgstr[2] "%(num)d سنتين" +msgstr[3] "%(num)d سنوات" +msgstr[4] "%(num)d سنوات" +msgstr[5] "%(num)d سنوات" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "%(num)d شهر" +msgstr[1] "%(num)d شهر" +msgstr[2] "%(num)d شهرين" +msgstr[3] "%(num)d أشهر" +msgstr[4] "%(num)d أشهر" +msgstr[5] "%(num)d أشهر" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "%(num)d أسبوع" +msgstr[1] "%(num)d أسبوع" +msgstr[2] "%(num)d أسبوعين" +msgstr[3] "%(num)d أسابيع" +msgstr[4] "%(num)d أسابيع" +msgstr[5] "%(num)d أسابيع" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "%(num)d يوم" +msgstr[1] "%(num)d يوم" +msgstr[2] "%(num)d يومين" +msgstr[3] "%(num)d أيام" +msgstr[4] "%(num)d يوم" +msgstr[5] "%(num)d أيام" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "%(num)d ساعة" +msgstr[1] "%(num)d ساعة" +msgstr[2] "%(num)d ساعتين" +msgstr[3] "%(num)d ساعات" +msgstr[4] "%(num)d ساعة" +msgstr[5] "%(num)d ساعات" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "%(num)d دقيقة" +msgstr[1] "%(num)d دقيقة" +msgstr[2] "%(num)d دقيقتين" +msgstr[3] "%(num)d دقائق" +msgstr[4] "%(num)d دقيقة" +msgstr[5] "%(num)d دقيقة" + +msgid "Forbidden" +msgstr "ممنوع" + +msgid "CSRF verification failed. Request aborted." +msgstr "تم الفشل للتحقق من CSRF. تم إنهاء الطلب." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" +"أنت ترى هذه الرسالة لأن موقع HTTPS هذا يتطلب إرسال “Referer header” بواسطة " +"متصفح الويب الخاص بك، ولكن لم يتم إرسال أي منها. هذا مطلوب لأسباب أمنية، " +"لضمان عدم اختطاف متصفحك من قبل أطراف ثالثة." + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"إذا قمت بتكوين المستعرض لتعطيل رؤوس “Referer” ، فيرجى إعادة تمكينها ، على " +"الأقل لهذا الموقع ، أو لاتصالات HTTPS ، أو لطلبات “same-origin”." + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"إذا كنت تستخدم العلامة أو " +"تضمين رأس “Referrer-Policy: no-referrer”، يرجى إزالتها. تتطلب حماية CSRF أن " +"يقوم رأس “Referer” بإجراء فحص صارم للمراجع. إذا كنت قلقًا بشأن الخصوصية ، " +"فاستخدم بدائل مثل للروابط إلى مواقع الجهات الخارجية." + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"أنت ترى هذه الرسالة لأن هذا الموقع يتطلب كعكة CSRF عند تقديم النماذج. ملف " +"الكعكة هذا مطلوب لأسباب أمنية في تعريف الإرتباط، لضمان أنه لم يتم اختطاف " +"المتصفح من قبل أطراف أخرى." + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" +"إذا قمت بضبط المتصفح لتعطيل الكوكيز الرجاء إعادة تغعيلها، على الأقل بالنسبة " +"لهذا الموقع، أو للطلبات من “same-origin”." + +msgid "More information is available with DEBUG=True." +msgstr "يتوفر مزيد من المعلومات عند ضبط الخيار DEBUG=True." + +msgid "No year specified" +msgstr "لم تحدد السنة" + +msgid "Date out of range" +msgstr "التاريخ خارج النطاق" + +msgid "No month specified" +msgstr "لم تحدد الشهر" + +msgid "No day specified" +msgstr "لم تحدد اليوم" + +msgid "No week specified" +msgstr "لم تحدد الأسبوع" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "لا يوجد %(verbose_name_plural)s" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"التاريخ بالمستقبل %(verbose_name_plural)s غير متوفر لأن قيمة %(class_name)s." +"allow_future هي False." + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "نسق تاريخ غير صحيح \"%(datestr)s\" محدد بالشكل ''%(format)s\"" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "لم يعثر على أي %(verbose_name)s مطابقة لهذا الإستعلام" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "الصفحة ليست \"الأخيرة\"، كما لا يمكن تحويل القيمة إلى رقم طبيعي." + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "صفحة خاطئة (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" +"قائمة فارغة و\n" +"\"%(class_name)s.allow_empty\"\n" +"قيمته False." + +msgid "Directory indexes are not allowed here." +msgstr "لا يسمح لفهارس الدليل هنا." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "”%(path)s“ غير موجود" + +#, python-format +msgid "Index of %(directory)s" +msgstr "فهرس لـ %(directory)s" + +msgid "The install worked successfully! Congratulations!" +msgstr "تمت عملية التنصيب بنجاح! تهانينا!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"استعراض ملاحظات الإصدار لجانغو %(version)s" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" +"تظهر لك هذه الصفحة لأن DEBUG=True في ملف settings خاصتك كما أنك لم تقم بإعداد الروابط URLs." + +msgid "Django Documentation" +msgstr "وثائق تعليمات جانغو" + +msgid "Topics, references, & how-to’s" +msgstr "المواضيع و المراجع و التعليمات" + +msgid "Tutorial: A Polling App" +msgstr "برنامج تعليمي: تطبيق تصويت" + +msgid "Get started with Django" +msgstr "إبدأ مع جانغو" + +msgid "Django Community" +msgstr "مجتمع جانغو" + +msgid "Connect, get help, or contribute" +msgstr "اتصل بنا أو احصل على مساعدة أو ساهم" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/ar_DZ/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/ar_DZ/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..ce2b1f87 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/ar_DZ/LC_MESSAGES/django 3.po @@ -0,0 +1,1397 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Jihad Bahmaid Al-Halki, 2022 +# Riterix , 2019-2020 +# Riterix , 2019 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:23-0500\n" +"PO-Revision-Date: 2022-07-25 06:49+0000\n" +"Last-Translator: Jihad Bahmaid Al-Halki\n" +"Language-Team: Arabic (Algeria) (http://www.transifex.com/django/django/" +"language/ar_DZ/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ar_DZ\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " +"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" + +msgid "Afrikaans" +msgstr "الإفريقية" + +msgid "Arabic" +msgstr "العربية" + +msgid "Algerian Arabic" +msgstr "العربية الجزائرية" + +msgid "Asturian" +msgstr "الأسترية" + +msgid "Azerbaijani" +msgstr "الأذربيجانية" + +msgid "Bulgarian" +msgstr "البلغارية" + +msgid "Belarusian" +msgstr "البيلاروسية" + +msgid "Bengali" +msgstr "البنغالية" + +msgid "Breton" +msgstr "البريتونية" + +msgid "Bosnian" +msgstr "البوسنية" + +msgid "Catalan" +msgstr "الكتلانية" + +msgid "Czech" +msgstr "التشيكية" + +msgid "Welsh" +msgstr "الويلز" + +msgid "Danish" +msgstr "الدنماركية" + +msgid "German" +msgstr "الألمانية" + +msgid "Lower Sorbian" +msgstr "الصربية السفلى" + +msgid "Greek" +msgstr "اليونانية" + +msgid "English" +msgstr "الإنجليزية" + +msgid "Australian English" +msgstr "الإنجليزية الإسترالية" + +msgid "British English" +msgstr "الإنجليزية البريطانية" + +msgid "Esperanto" +msgstr "الاسبرانتو" + +msgid "Spanish" +msgstr "الإسبانية" + +msgid "Argentinian Spanish" +msgstr "الأسبانية الأرجنتينية" + +msgid "Colombian Spanish" +msgstr "الكولومبية الإسبانية" + +msgid "Mexican Spanish" +msgstr "الأسبانية المكسيكية" + +msgid "Nicaraguan Spanish" +msgstr "الإسبانية النيكاراغوية" + +msgid "Venezuelan Spanish" +msgstr "الإسبانية الفنزويلية" + +msgid "Estonian" +msgstr "الإستونية" + +msgid "Basque" +msgstr "الباسك" + +msgid "Persian" +msgstr "الفارسية" + +msgid "Finnish" +msgstr "الفنلندية" + +msgid "French" +msgstr "الفرنسية" + +msgid "Frisian" +msgstr "الفريزية" + +msgid "Irish" +msgstr "الإيرلندية" + +msgid "Scottish Gaelic" +msgstr "الغيلية الأسكتلندية" + +msgid "Galician" +msgstr "الجليقية" + +msgid "Hebrew" +msgstr "العبرية" + +msgid "Hindi" +msgstr "الهندية" + +msgid "Croatian" +msgstr "الكرواتية" + +msgid "Upper Sorbian" +msgstr "الصربية العليا" + +msgid "Hungarian" +msgstr "الهنغارية" + +msgid "Armenian" +msgstr "الأرمنية" + +msgid "Interlingua" +msgstr "اللغة الوسيطة" + +msgid "Indonesian" +msgstr "الإندونيسية" + +msgid "Igbo" +msgstr "إيبو" + +msgid "Ido" +msgstr "ايدو" + +msgid "Icelandic" +msgstr "الآيسلندية" + +msgid "Italian" +msgstr "الإيطالية" + +msgid "Japanese" +msgstr "اليابانية" + +msgid "Georgian" +msgstr "الجورجية" + +msgid "Kabyle" +msgstr "القبائلية" + +msgid "Kazakh" +msgstr "الكازاخستانية" + +msgid "Khmer" +msgstr "الخمر" + +msgid "Kannada" +msgstr "الهندية (كنّادا)" + +msgid "Korean" +msgstr "الكورية" + +msgid "Kyrgyz" +msgstr "القيرغيزية" + +msgid "Luxembourgish" +msgstr "اللوكسمبرجية" + +msgid "Lithuanian" +msgstr "اللتوانية" + +msgid "Latvian" +msgstr "اللاتفية" + +msgid "Macedonian" +msgstr "المقدونية" + +msgid "Malayalam" +msgstr "المايالام" + +msgid "Mongolian" +msgstr "المنغولية" + +msgid "Marathi" +msgstr "المهاراتية" + +msgid "Malay" +msgstr "ملاي" + +msgid "Burmese" +msgstr "البورمية" + +msgid "Norwegian Bokmål" +msgstr "النرويجية" + +msgid "Nepali" +msgstr "النيبالية" + +msgid "Dutch" +msgstr "الهولندية" + +msgid "Norwegian Nynorsk" +msgstr "النينورسك نرويجية" + +msgid "Ossetic" +msgstr "الأوسيتيكية" + +msgid "Punjabi" +msgstr "البنجابية" + +msgid "Polish" +msgstr "البولندية" + +msgid "Portuguese" +msgstr "البرتغالية" + +msgid "Brazilian Portuguese" +msgstr "البرتغالية البرازيلية" + +msgid "Romanian" +msgstr "الرومانية" + +msgid "Russian" +msgstr "الروسية" + +msgid "Slovak" +msgstr "السلوفاكية" + +msgid "Slovenian" +msgstr "السلوفانية" + +msgid "Albanian" +msgstr "الألبانية" + +msgid "Serbian" +msgstr "الصربية" + +msgid "Serbian Latin" +msgstr "اللاتينية الصربية" + +msgid "Swedish" +msgstr "السويدية" + +msgid "Swahili" +msgstr "السواحلية" + +msgid "Tamil" +msgstr "التاميل" + +msgid "Telugu" +msgstr "التيلوغو" + +msgid "Tajik" +msgstr "الطاجيكية" + +msgid "Thai" +msgstr "التايلندية" + +msgid "Turkmen" +msgstr "" + +msgid "Turkish" +msgstr "التركية" + +msgid "Tatar" +msgstr "التتاريية" + +msgid "Udmurt" +msgstr "الأدمرتية" + +msgid "Ukrainian" +msgstr "الأكرانية" + +msgid "Urdu" +msgstr "الأوردو" + +msgid "Uzbek" +msgstr "الأوزبكية" + +msgid "Vietnamese" +msgstr "الفيتنامية" + +msgid "Simplified Chinese" +msgstr "الصينية المبسطة" + +msgid "Traditional Chinese" +msgstr "الصينية التقليدية" + +msgid "Messages" +msgstr "الرسائل" + +msgid "Site Maps" +msgstr "خرائط الموقع" + +msgid "Static Files" +msgstr "الملفات الثابتة" + +msgid "Syndication" +msgstr "توظيف النشر" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "" + +msgid "That page number is not an integer" +msgstr "رقم الصفحة ليس عددًا صحيحًا" + +msgid "That page number is less than 1" +msgstr "رقم الصفحة أقل من 1" + +msgid "That page contains no results" +msgstr "هذه الصفحة لا تحتوي على نتائج" + +msgid "Enter a valid value." +msgstr "أدخل قيمة صحيحة." + +msgid "Enter a valid URL." +msgstr "أدخل رابطاً صحيحاً." + +msgid "Enter a valid integer." +msgstr "أدخل رقم صالح." + +msgid "Enter a valid email address." +msgstr "أدخل عنوان بريد إلكتروني صحيح." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" +"أدخل “slug” صالحة تتكون من أحرف أو أرقام أو الشرطة السفلية أو الواصلات." + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"أدخل “slug” صالحة تتكون من أحرف Unicode أو الأرقام أو الشرطة السفلية أو " +"الواصلات." + +msgid "Enter a valid IPv4 address." +msgstr "أدخل عنوان IPv4 صحيح." + +msgid "Enter a valid IPv6 address." +msgstr "أدخل عنوان IPv6 صحيح." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "أدخل عنوان IPv4 أو عنوان IPv6 صحيح." + +msgid "Enter only digits separated by commas." +msgstr "أدخل أرقاما فقط مفصول بينها بفواصل." + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "تحقق من أن هذه القيمة هي %(limit_value)s (إنها %(show_value)s)." + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "تحقق من أن تكون هذه القيمة أقل من %(limit_value)s أو مساوية لها." + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "تحقق من أن تكون هذه القيمة أكثر من %(limit_value)s أو مساوية لها." + +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "" + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأقل (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[1] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأقل (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[2] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأقل (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[3] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأقل (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[4] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأقل (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[5] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأقل (هي تحتوي " +"حالياً على %(show_value)d)." + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأكثر (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[1] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأكثر (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[2] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأكثر (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[3] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأكثر (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[4] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأكثر (هي تحتوي " +"حالياً على %(show_value)d)." +msgstr[5] "" +"تأكد أن هذه القيمة تحتوي على %(limit_value)d حرف أو رمز على الأكثر (هي تحتوي " +"حالياً على %(show_value)d)." + +msgid "Enter a number." +msgstr "أدخل رقماً." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "تحقق من أن تدخل %(max)s أرقام لا أكثر." +msgstr[1] "تحقق من أن تدخل رقم %(max)s لا أكثر." +msgstr[2] "تحقق من أن تدخل %(max)s رقمين لا أكثر." +msgstr[3] "تحقق من أن تدخل %(max)s أرقام لا أكثر." +msgstr[4] "تحقق من أن تدخل %(max)s أرقام لا أكثر." +msgstr[5] "تحقق من أن تدخل %(max)s أرقام لا أكثر." + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "تحقق من أن تدخل %(max)s خانات عشرية لا أكثر." +msgstr[1] "تحقق من أن تدخل %(max)s خانات عشرية لا أكثر." +msgstr[2] "تحقق من أن تدخل %(max)s خانات عشرية لا أكثر." +msgstr[3] "تحقق من أن تدخل %(max)s خانات عشرية لا أكثر." +msgstr[4] "تحقق من أن تدخل %(max)s خانات عشرية لا أكثر." +msgstr[5] "تحقق من أن تدخل %(max)s خانات عشرية لا أكثر." + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "تحقق من أن تدخل %(max)s أرقام قبل الفاصل العشري لا أكثر." +msgstr[1] "تحقق من أن تدخل %(max)s أرقام قبل الفاصل العشري لا أكثر." +msgstr[2] "تحقق من أن تدخل %(max)s أرقام قبل الفاصل العشري لا أكثر." +msgstr[3] "تحقق من أن تدخل %(max)s أرقام قبل الفاصل العشري لا أكثر." +msgstr[4] "تحقق من أن تدخل %(max)s أرقام قبل الفاصل العشري لا أكثر." +msgstr[5] "تحقق من أن تدخل %(max)s أرقام قبل الفاصل العشري لا أكثر." + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"امتداد الملف “%(extension)s” غير مسموح به. الامتدادات المسموح بها هي:" +"%(allowed_extensions)s." + +msgid "Null characters are not allowed." +msgstr "لا يُسمح بالأحرف الخالية." + +msgid "and" +msgstr "و" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "%(model_name)s بهذا %(field_labels)s موجود سلفاً." + +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "" + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "القيمة %(value)r ليست خيارا صحيحاً." + +msgid "This field cannot be null." +msgstr "لا يمكن ترك هذا الحقل خالي." + +msgid "This field cannot be blank." +msgstr "لا يمكن ترك هذا الحقل فارغاً." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "النموذج %(model_name)s والحقل %(field_label)s موجود مسبقاً." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"%(field_label)s يجب أن يكون فريد لـ %(date_field_label)s %(lookup_type)s." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "حقل نوع: %(field_type)s" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "يجب أن تكون القيمة “%(value)s” إما True أو False." + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "يجب أن تكون القيمة “%(value)s” إما True أو False أو None." + +msgid "Boolean (Either True or False)" +msgstr "ثنائي (إما True أو False)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "سلسلة نص (%(max_length)s كحد أقصى)" + +msgid "Comma-separated integers" +msgstr "أرقام صحيحة مفصولة بفواصل" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" +"تحتوي القيمة “%(value)s” على تنسيق تاريخ غير صالح. يجب أن يكون بتنسيق YYYY-" +"MM-DD." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" +"تحتوي القيمة “%(value)s” على التنسيق الصحيح (YYYY-MM-DD) ولكنه تاريخ غير " +"صالح." + +msgid "Date (without time)" +msgstr "التاريخ (دون الوقت)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"تحتوي القيمة “%(value)s” على تنسيق غير صالح. يجب أن يكون بتنسيق YYYY-MM-DD " +"HH: MM [: ss [.uuuuuu]] [TZ]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"تحتوي القيمة “%(value)s” على التنسيق الصحيح (YYYY-MM-DD HH: MM [: ss [." +"uuuuuu]] [TZ]) ولكنها تعد تاريخًا / وقتًا غير صالحين." + +msgid "Date (with time)" +msgstr "التاريخ (مع الوقت)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "يجب أن تكون القيمة “%(value)s” رقمًا عشريًا." + +msgid "Decimal number" +msgstr "رقم عشري" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" +"تحتوي القيمة “%(value)s” على تنسيق غير صالح. يجب أن يكون بتنسيق [DD] [[HH:] " +"MM:] ss [.uuuuuu]." + +msgid "Duration" +msgstr "المدّة" + +msgid "Email address" +msgstr "عنوان بريد إلكتروني" + +msgid "File path" +msgstr "مسار الملف" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "يجب أن تكون القيمة “%(value)s” قيمة عائمة." + +msgid "Floating point number" +msgstr "رقم فاصلة عائمة" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "يجب أن تكون القيمة “%(value)s” عددًا صحيحًا." + +msgid "Integer" +msgstr "عدد صحيح" + +msgid "Big (8 byte) integer" +msgstr "عدد صحيح كبير (8 بايت)" + +msgid "Small integer" +msgstr "عدد صحيح صغير" + +msgid "IPv4 address" +msgstr "عنوان IPv4" + +msgid "IP address" +msgstr "عنوان IP" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "يجب أن تكون القيمة “%(value)s” إما None أو True أو False." + +msgid "Boolean (Either True, False or None)" +msgstr "ثنائي (إما True أو False أو None)" + +msgid "Positive big integer" +msgstr "عدد صحيح كبير موجب" + +msgid "Positive integer" +msgstr "عدد صحيح موجب" + +msgid "Positive small integer" +msgstr "عدد صحيح صغير موجب" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "Slug (حتى %(max_length)s)" + +msgid "Text" +msgstr "نص" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" +"تحتوي القيمة “%(value)s” على تنسيق غير صالح. يجب أن يكون بتنسيق HH: MM [: ss " +"[.uuuuuu]]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" +"تحتوي القيمة “%(value)s” على التنسيق الصحيح (HH: MM [: ss [.uuuuuu]]) ولكنه " +"وقت غير صالح." + +msgid "Time" +msgstr "وقت" + +msgid "URL" +msgstr "رابط" + +msgid "Raw binary data" +msgstr "البيانات الثنائية الخام" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "“%(value)s” ليس UUID صالحًا." + +msgid "Universally unique identifier" +msgstr "المعرف الفريد العالمي (UUID)" + +msgid "File" +msgstr "ملف" + +msgid "Image" +msgstr "صورة" + +msgid "A JSON object" +msgstr "كائن JSON" + +msgid "Value must be valid JSON." +msgstr "يجب أن تكون قيمة JSON صالحة." + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "النموذج %(model)s ذو الحقل و القيمة %(field)s %(value)r غير موجود." + +msgid "Foreign Key (type determined by related field)" +msgstr "الحقل المرتبط (تم تحديد النوع وفقاً للحقل المرتبط)" + +msgid "One-to-one relationship" +msgstr "علاقة واحد إلى واحد" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "%(from)s-%(to)s علاقة" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "%(from)s-%(to)s علاقات" + +msgid "Many-to-many relationship" +msgstr "علاقة متعدد إلى متعدد" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "هذا الحقل مطلوب." + +msgid "Enter a whole number." +msgstr "أدخل رقما صحيحا." + +msgid "Enter a valid date." +msgstr "أدخل تاريخاً صحيحاً." + +msgid "Enter a valid time." +msgstr "أدخل وقتاً صحيحاً." + +msgid "Enter a valid date/time." +msgstr "أدخل تاريخاً/وقتاً صحيحاً." + +msgid "Enter a valid duration." +msgstr "أدخل مدّة صحيحة" + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "يجب أن يتراوح عدد الأيام بين {min_days} و {max_days}." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "لم يتم ارسال ملف، الرجاء التأكد من نوع ترميز الاستمارة." + +msgid "No file was submitted." +msgstr "لم يتم إرسال اي ملف." + +msgid "The submitted file is empty." +msgstr "الملف الذي قمت بإرساله فارغ." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"تأكد أن إسم هذا الملف يحتوي على %(max)d حرف على الأكثر (هو يحتوي الآن على " +"%(length)d حرف)." +msgstr[1] "" +"تأكد أن إسم هذا الملف يحتوي على %(max)d حرف على الأكثر (هو يحتوي الآن على " +"%(length)d حرف)." +msgstr[2] "" +"تأكد أن إسم هذا الملف يحتوي على %(max)d حرف على الأكثر (هو يحتوي الآن على " +"%(length)d حرف)." +msgstr[3] "" +"تأكد أن إسم هذا الملف يحتوي على %(max)d حرف على الأكثر (هو يحتوي الآن على " +"%(length)d حرف)." +msgstr[4] "" +"تأكد أن إسم هذا الملف يحتوي على %(max)d حرف على الأكثر (هو يحتوي الآن على " +"%(length)d حرف)." +msgstr[5] "" +"تأكد أن إسم هذا الملف يحتوي على %(max)d حرف على الأكثر (هو يحتوي الآن على " +"%(length)d حرف)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" +"رجاءً أرسل ملف أو صح علامة صح عند مربع اختيار \\\"فارغ\\\"، وليس كلاهما." + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"قم برفع صورة صحيحة، الملف الذي قمت برفعه إما أنه ليس ملفا لصورة أو أنه ملف " +"معطوب." + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "انتق خياراً صحيحاً. %(value)s ليس أحد الخيارات المتاحة." + +msgid "Enter a list of values." +msgstr "أدخل قائمة من القيم." + +msgid "Enter a complete value." +msgstr "إدخال قيمة كاملة." + +msgid "Enter a valid UUID." +msgstr "أدخل قيمة UUID صحيحة." + +msgid "Enter a valid JSON." +msgstr "ادخل كائن JSON صالح." + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(الحقل الخفي %(name)s) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" +"نموذج بيانات الإدارة مفقود أو تم العبث به. %(field_names)sمن الحقول مفقود. " +"قد تحتاج إلى رفع تقرير بالمشكلة إن استمرت الحالة." + +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +msgid "Order" +msgstr "الترتيب" + +msgid "Delete" +msgstr "احذف" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "رجاء صحّح بيانات %(field)s المتكررة." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "رجاء صحّح بيانات %(field)s المتكررة والتي يجب أن تكون مُميّزة." + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"رجاء صحّح بيانات %(field_name)s المتكررة والتي يجب أن تكون مُميّزة لـ%(lookup)s " +"في %(date_field)s." + +msgid "Please correct the duplicate values below." +msgstr "رجاءً صحّح القيم المُكرّرة أدناه." + +msgid "The inline value did not match the parent instance." +msgstr "القيمة المضمنة لا تتطابق مع المثيل الأصلي." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "انتق خياراً صحيحاً. اختيارك ليس أحد الخيارات المتاحة." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "“%(pk)s” ليست قيمة صالحة." + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"لا يمكن تفسير٪ %(datetime)s في المنطقة الزمنية٪ %(current_timezone)s؛ قد " +"تكون غامضة أو غير موجودة." + +msgid "Clear" +msgstr "تفريغ" + +msgid "Currently" +msgstr "حالياً" + +msgid "Change" +msgstr "عدّل" + +msgid "Unknown" +msgstr "مجهول" + +msgid "Yes" +msgstr "نعم" + +msgid "No" +msgstr "لا" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "نعم,لا,ربما" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d بايت" +msgstr[1] "%(size)d بايت واحد " +msgstr[2] "%(size)d بايتان" +msgstr[3] "%(size)d بايت" +msgstr[4] "%(size)d بايت" +msgstr[5] "%(size)d بايت" + +#, python-format +msgid "%s KB" +msgstr "%s ك.ب" + +#, python-format +msgid "%s MB" +msgstr "%s م.ب" + +#, python-format +msgid "%s GB" +msgstr "%s ج.ب" + +#, python-format +msgid "%s TB" +msgstr "%s ت.ب" + +#, python-format +msgid "%s PB" +msgstr "%s ب.ب" + +msgid "p.m." +msgstr "م" + +msgid "a.m." +msgstr "ص" + +msgid "PM" +msgstr "م" + +msgid "AM" +msgstr "ص" + +msgid "midnight" +msgstr "منتصف الليل" + +msgid "noon" +msgstr "ظهراً" + +msgid "Monday" +msgstr "الاثنين" + +msgid "Tuesday" +msgstr "الثلاثاء" + +msgid "Wednesday" +msgstr "الأربعاء" + +msgid "Thursday" +msgstr "الخميس" + +msgid "Friday" +msgstr "الجمعة" + +msgid "Saturday" +msgstr "السبت" + +msgid "Sunday" +msgstr "الأحد" + +msgid "Mon" +msgstr "إثنين" + +msgid "Tue" +msgstr "ثلاثاء" + +msgid "Wed" +msgstr "أربعاء" + +msgid "Thu" +msgstr "خميس" + +msgid "Fri" +msgstr "جمعة" + +msgid "Sat" +msgstr "سبت" + +msgid "Sun" +msgstr "أحد" + +msgid "January" +msgstr "جانفي" + +msgid "February" +msgstr "فيفري" + +msgid "March" +msgstr "مارس" + +msgid "April" +msgstr "أفريل" + +msgid "May" +msgstr "ماي" + +msgid "June" +msgstr "جوان" + +msgid "July" +msgstr "جويليه" + +msgid "August" +msgstr "أوت" + +msgid "September" +msgstr "سبتمبر" + +msgid "October" +msgstr "أكتوبر" + +msgid "November" +msgstr "نوفمبر" + +msgid "December" +msgstr "ديسمبر" + +msgid "jan" +msgstr "جانفي" + +msgid "feb" +msgstr "فيفري" + +msgid "mar" +msgstr "مارس" + +msgid "apr" +msgstr "أفريل" + +msgid "may" +msgstr "ماي" + +msgid "jun" +msgstr "جوان" + +msgid "jul" +msgstr "جويليه" + +msgid "aug" +msgstr "أوت" + +msgid "sep" +msgstr "سبتمبر" + +msgid "oct" +msgstr "أكتوبر" + +msgid "nov" +msgstr "نوفمبر" + +msgid "dec" +msgstr "ديسمبر" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "جانفي" + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "فيفري" + +msgctxt "abbrev. month" +msgid "March" +msgstr "مارس" + +msgctxt "abbrev. month" +msgid "April" +msgstr "أفريل" + +msgctxt "abbrev. month" +msgid "May" +msgstr "ماي" + +msgctxt "abbrev. month" +msgid "June" +msgstr "جوان" + +msgctxt "abbrev. month" +msgid "July" +msgstr "جويليه" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "أوت" + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "سبتمبر" + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "أكتوبر" + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "نوفمبر" + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "ديسمبر" + +msgctxt "alt. month" +msgid "January" +msgstr "جانفي" + +msgctxt "alt. month" +msgid "February" +msgstr "فيفري" + +msgctxt "alt. month" +msgid "March" +msgstr "مارس" + +msgctxt "alt. month" +msgid "April" +msgstr "أفريل" + +msgctxt "alt. month" +msgid "May" +msgstr "ماي" + +msgctxt "alt. month" +msgid "June" +msgstr "جوان" + +msgctxt "alt. month" +msgid "July" +msgstr "جويليه" + +msgctxt "alt. month" +msgid "August" +msgstr "أوت" + +msgctxt "alt. month" +msgid "September" +msgstr "سبتمبر" + +msgctxt "alt. month" +msgid "October" +msgstr "أكتوبر" + +msgctxt "alt. month" +msgid "November" +msgstr "نوفمبر" + +msgctxt "alt. month" +msgid "December" +msgstr "ديسمبر" + +msgid "This is not a valid IPv6 address." +msgstr "هذا ليس عنوان IPv6 صحيح." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "أو" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr "، " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +msgid "Forbidden" +msgstr "ممنوع" + +msgid "CSRF verification failed. Request aborted." +msgstr "تم الفشل للتحقق من CSRF. تم إنهاء الطلب." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" +"أنت ترى هذه الرسالة لأن موقع HTTPS هذا يتطلب \"عنوان مرجعي\" ليتم إرساله " +"بواسطة متصفح الويب الخاص بك ، ولكن لم يتم إرسال أي شيء. هذا العنوان مطلوب " +"لأسباب أمنية ، لضمان عدم اختراق متصفحك من قبل أطراف أخرى." + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"إذا قمت بتكوين المستعرض الخاص بك لتعطيل رؤوس “Referer” ، فالرجاء إعادة " +"تمكينها ، على الأقل لهذا الموقع ، أو لاتصالات HTTPS ، أو لطلبات “same-" +"origin”." + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"إذا كنت تستخدم العلامة أو تتضمن رأس “Referrer-Policy: no-referrer” ، فيرجى إزالتها. تتطلب حماية " +"CSRF رأس “Referer” القيام بالتحقق من “strict referer”. إذا كنت مهتمًا " +"بالخصوصية ، فاستخدم بدائل مثل للروابط إلى مواقع " +"الجهات الخارجية." + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"تشاهد هذه الرسالة لأن هذا الموقع يتطلب ملف تعريف ارتباط CSRF Cookie عند " +"إرسال النماذج. ملف تعريف ارتباط Cookie هذا مطلوب لأسباب أمنية ، لضمان عدم " +"اختطاف متصفحك من قبل أطراف ثالثة." + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" +"إذا قمت بتكوين المستعرض الخاص بك لتعطيل ملفات تعريف الارتباط Cookies ، يرجى " +"إعادة تمكينها ، على الأقل لهذا الموقع ، أو لطلبات “same-origin”." + +msgid "More information is available with DEBUG=True." +msgstr "يتوفر مزيد من المعلومات عند ضبط الخيار DEBUG=True." + +msgid "No year specified" +msgstr "لم تحدد السنة" + +msgid "Date out of range" +msgstr "تاريخ خارج النطاق" + +msgid "No month specified" +msgstr "لم تحدد الشهر" + +msgid "No day specified" +msgstr "لم تحدد اليوم" + +msgid "No week specified" +msgstr "لم تحدد الأسبوع" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "لا يوجد %(verbose_name_plural)s" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"التاريخ بالمستقبل %(verbose_name_plural)s غير متوفر لأن قيمة %(class_name)s." +"allow_future هي False." + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "سلسلة تاريخ غير صالحة “%(datestr)s” شكل معين “%(format)s”" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "لم يعثر على أي %(verbose_name)s مطابقة لهذا الإستعلام" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "الصفحة ليست \"الأخيرة\" ، ولا يمكن تحويلها إلى عدد صحيح." + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "صفحة خاطئة (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "القائمة فارغة و “%(class_name)s.allow_empty” هي False." + +msgid "Directory indexes are not allowed here." +msgstr "لا يسمح لفهارس الدليل هنا." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "“%(path)s” غير موجود" + +#, python-format +msgid "Index of %(directory)s" +msgstr "فهرس لـ %(directory)s" + +msgid "The install worked successfully! Congratulations!" +msgstr "تمَّت عملية التثبيت بنجاح! تهانينا!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"عرض ملاحظات الإصدار ل جانغو " +"%(version)s" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" +"تشاهد هذه الصفحة لأن DEBUG = True موجود في ملف الإعدادات الخاص بك ولم تقم بتكوين أي " +"عناوين URL." + +msgid "Django Documentation" +msgstr "توثيق جانغو" + +msgid "Topics, references, & how-to’s" +msgstr "الموضوعات ، المراجع، & الكيفية" + +msgid "Tutorial: A Polling App" +msgstr "البرنامج التعليمي: تطبيق الاقتراع" + +msgid "Get started with Django" +msgstr "الخطوات الأولى مع جانغو" + +msgid "Django Community" +msgstr "مجتمع جانغو" + +msgid "Connect, get help, or contribute" +msgstr "الاتصال، الحصول على المساعدة أو المساهمة" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/cs/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/cs/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..80086ef6 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/cs/LC_MESSAGES/django 3.po @@ -0,0 +1,1362 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Claude Paroz , 2020 +# Jannis Leidel , 2011 +# Jan Papež , 2012 +# trendspotter , 2022 +# Jirka Vejrazka , 2011 +# trendspotter , 2020 +# Tomáš Ehrlich , 2015 +# Vláďa Macek , 2012-2014 +# Vláďa Macek , 2015-2022 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:23-0500\n" +"PO-Revision-Date: 2022-07-25 06:49+0000\n" +"Last-Translator: trendspotter \n" +"Language-Team: Czech (http://www.transifex.com/django/django/language/cs/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: cs\n" +"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n " +"<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" + +msgid "Afrikaans" +msgstr "afrikánsky" + +msgid "Arabic" +msgstr "arabsky" + +msgid "Algerian Arabic" +msgstr "alžírskou arabštinou" + +msgid "Asturian" +msgstr "asturštinou" + +msgid "Azerbaijani" +msgstr "ázerbájdžánsky" + +msgid "Bulgarian" +msgstr "bulharsky" + +msgid "Belarusian" +msgstr "bělorusky" + +msgid "Bengali" +msgstr "bengálsky" + +msgid "Breton" +msgstr "bretonsky" + +msgid "Bosnian" +msgstr "bosensky" + +msgid "Catalan" +msgstr "katalánsky" + +msgid "Czech" +msgstr "česky" + +msgid "Welsh" +msgstr "velšsky" + +msgid "Danish" +msgstr "dánsky" + +msgid "German" +msgstr "německy" + +msgid "Lower Sorbian" +msgstr "dolnolužickou srbštinou" + +msgid "Greek" +msgstr "řecky" + +msgid "English" +msgstr "anglicky" + +msgid "Australian English" +msgstr "australskou angličtinou" + +msgid "British English" +msgstr "britskou angličtinou" + +msgid "Esperanto" +msgstr "esperantsky" + +msgid "Spanish" +msgstr "španělsky" + +msgid "Argentinian Spanish" +msgstr "argentinskou španělštinou" + +msgid "Colombian Spanish" +msgstr "kolumbijskou španělštinou" + +msgid "Mexican Spanish" +msgstr "mexickou španělštinou" + +msgid "Nicaraguan Spanish" +msgstr "nikaragujskou španělštinou" + +msgid "Venezuelan Spanish" +msgstr "venezuelskou španělštinou" + +msgid "Estonian" +msgstr "estonsky" + +msgid "Basque" +msgstr "baskicky" + +msgid "Persian" +msgstr "persky" + +msgid "Finnish" +msgstr "finsky" + +msgid "French" +msgstr "francouzsky" + +msgid "Frisian" +msgstr "frísky" + +msgid "Irish" +msgstr "irsky" + +msgid "Scottish Gaelic" +msgstr "skotskou keltštinou" + +msgid "Galician" +msgstr "galicijsky" + +msgid "Hebrew" +msgstr "hebrejsky" + +msgid "Hindi" +msgstr "hindsky" + +msgid "Croatian" +msgstr "chorvatsky" + +msgid "Upper Sorbian" +msgstr "hornolužickou srbštinou" + +msgid "Hungarian" +msgstr "maďarsky" + +msgid "Armenian" +msgstr "arménštinou" + +msgid "Interlingua" +msgstr "interlingua" + +msgid "Indonesian" +msgstr "indonésky" + +msgid "Igbo" +msgstr "igboštinou" + +msgid "Ido" +msgstr "idem" + +msgid "Icelandic" +msgstr "islandsky" + +msgid "Italian" +msgstr "italsky" + +msgid "Japanese" +msgstr "japonsky" + +msgid "Georgian" +msgstr "gruzínštinou" + +msgid "Kabyle" +msgstr "kabylštinou" + +msgid "Kazakh" +msgstr "kazašsky" + +msgid "Khmer" +msgstr "khmersky" + +msgid "Kannada" +msgstr "kannadsky" + +msgid "Korean" +msgstr "korejsky" + +msgid "Kyrgyz" +msgstr "kyrgyzštinou" + +msgid "Luxembourgish" +msgstr "lucembursky" + +msgid "Lithuanian" +msgstr "litevsky" + +msgid "Latvian" +msgstr "lotyšsky" + +msgid "Macedonian" +msgstr "makedonsky" + +msgid "Malayalam" +msgstr "malajálamsky" + +msgid "Mongolian" +msgstr "mongolsky" + +msgid "Marathi" +msgstr "marathi" + +msgid "Malay" +msgstr "malajštinou" + +msgid "Burmese" +msgstr "barmštinou" + +msgid "Norwegian Bokmål" +msgstr "bokmål norštinou" + +msgid "Nepali" +msgstr "nepálsky" + +msgid "Dutch" +msgstr "nizozemsky" + +msgid "Norwegian Nynorsk" +msgstr "norsky (Nynorsk)" + +msgid "Ossetic" +msgstr "osetštinou" + +msgid "Punjabi" +msgstr "paňdžábsky" + +msgid "Polish" +msgstr "polsky" + +msgid "Portuguese" +msgstr "portugalsky" + +msgid "Brazilian Portuguese" +msgstr "brazilskou portugalštinou" + +msgid "Romanian" +msgstr "rumunsky" + +msgid "Russian" +msgstr "rusky" + +msgid "Slovak" +msgstr "slovensky" + +msgid "Slovenian" +msgstr "slovinsky" + +msgid "Albanian" +msgstr "albánsky" + +msgid "Serbian" +msgstr "srbsky" + +msgid "Serbian Latin" +msgstr "srbsky (latinkou)" + +msgid "Swedish" +msgstr "švédsky" + +msgid "Swahili" +msgstr "svahilsky" + +msgid "Tamil" +msgstr "tamilsky" + +msgid "Telugu" +msgstr "telužsky" + +msgid "Tajik" +msgstr "Tádžik" + +msgid "Thai" +msgstr "thajsky" + +msgid "Turkmen" +msgstr "turkmenštinou" + +msgid "Turkish" +msgstr "turecky" + +msgid "Tatar" +msgstr "tatarsky" + +msgid "Udmurt" +msgstr "udmurtsky" + +msgid "Ukrainian" +msgstr "ukrajinsky" + +msgid "Urdu" +msgstr "urdsky" + +msgid "Uzbek" +msgstr "uzbecky" + +msgid "Vietnamese" +msgstr "vietnamsky" + +msgid "Simplified Chinese" +msgstr "čínsky (zjednodušeně)" + +msgid "Traditional Chinese" +msgstr "čínsky (tradičně)" + +msgid "Messages" +msgstr "Zprávy" + +msgid "Site Maps" +msgstr "Mapy webu" + +msgid "Static Files" +msgstr "Statické soubory" + +msgid "Syndication" +msgstr "Syndikace" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "…" + +msgid "That page number is not an integer" +msgstr "Číslo stránky není celé číslo." + +msgid "That page number is less than 1" +msgstr "Číslo stránky je menší než 1" + +msgid "That page contains no results" +msgstr "Stránka je bez výsledků" + +msgid "Enter a valid value." +msgstr "Zadejte platnou hodnotu." + +msgid "Enter a valid URL." +msgstr "Zadejte platnou adresu URL." + +msgid "Enter a valid integer." +msgstr "Zadejte platné celé číslo." + +msgid "Enter a valid email address." +msgstr "Zadejte platnou e-mailovou adresu." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" +"Vložte platný identifikátor složený pouze z písmen, čísel, podtržítek a " +"pomlček." + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"Zadejte platný identifikátor složený pouze z písmen, čísel, podtržítek a " +"pomlček typu Unicode." + +msgid "Enter a valid IPv4 address." +msgstr "Zadejte platnou adresu typu IPv4." + +msgid "Enter a valid IPv6 address." +msgstr "Zadejte platnou adresu typu IPv6." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "Zadejte platnou adresu typu IPv4 nebo IPv6." + +msgid "Enter only digits separated by commas." +msgstr "Zadejte pouze číslice oddělené čárkami." + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "Hodnota musí být %(limit_value)s (nyní je %(show_value)s)." + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "Hodnota musí být menší nebo rovna %(limit_value)s." + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "Hodnota musí být větší nebo rovna %(limit_value)s." + +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "" +"Ujistěte se, že tato hodnota je násobkem velikosti kroku %(limit_value)s." + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Tato hodnota má mít nejméně %(limit_value)d znak (nyní má %(show_value)d)." +msgstr[1] "" +"Tato hodnota má mít nejméně %(limit_value)d znaky (nyní má %(show_value)d)." +msgstr[2] "" +"Tato hodnota má mít nejméně %(limit_value)d znaku (nyní má %(show_value)d)." +msgstr[3] "" +"Tato hodnota má mít nejméně %(limit_value)d znaků (nyní má %(show_value)d)." + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Tato hodnota má mít nejvýše %(limit_value)d znak (nyní má %(show_value)d)." +msgstr[1] "" +"Tato hodnota má mít nejvýše %(limit_value)d znaky (nyní má %(show_value)d)." +msgstr[2] "" +"Tato hodnota má mít nejvýše %(limit_value)d znaků (nyní má %(show_value)d)." +msgstr[3] "" +"Tato hodnota má mít nejvýše %(limit_value)d znaků (nyní má %(show_value)d)." + +msgid "Enter a number." +msgstr "Zadejte číslo." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "Ujistěte se, že pole neobsahuje celkem více než %(max)s číslici." +msgstr[1] "Ujistěte se, že pole neobsahuje celkem více než %(max)s číslice." +msgstr[2] "Ujistěte se, že pole neobsahuje celkem více než %(max)s číslic." +msgstr[3] "Ujistěte se, že pole neobsahuje celkem více než %(max)s číslic." + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "Ujistěte se, že pole neobsahuje více než %(max)s desetinné místo." +msgstr[1] "Ujistěte se, že pole neobsahuje více než %(max)s desetinná místa." +msgstr[2] "Ujistěte se, že pole neobsahuje více než %(max)s desetinných míst." +msgstr[3] "Ujistěte se, že pole neobsahuje více než %(max)s desetinných míst." + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +"Ujistěte se, že hodnota neobsahuje více než %(max)s místo před desetinnou " +"čárkou (tečkou)." +msgstr[1] "" +"Ujistěte se, že hodnota neobsahuje více než %(max)s místa před desetinnou " +"čárkou (tečkou)." +msgstr[2] "" +"Ujistěte se, že hodnota neobsahuje více než %(max)s míst před desetinnou " +"čárkou (tečkou)." +msgstr[3] "" +"Ujistěte se, že hodnota neobsahuje více než %(max)s míst před desetinnou " +"čárkou (tečkou)." + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"Přípona souboru \"%(extension)s\" není povolena. Povolené jsou tyto: " +"%(allowed_extensions)s." + +msgid "Null characters are not allowed." +msgstr "Nulové znaky nejsou povoleny." + +msgid "and" +msgstr "a" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "" +"Položka %(model_name)s s touto kombinací hodnot v polích %(field_labels)s " +"již existuje." + +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "Omezení \"%(name)s\" je porušeno." + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "Hodnota %(value)r není platná možnost." + +msgid "This field cannot be null." +msgstr "Pole nemůže být null." + +msgid "This field cannot be blank." +msgstr "Pole nemůže být prázdné." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "" +"Položka %(model_name)s s touto hodnotou v poli %(field_label)s již existuje." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"Pole %(field_label)s musí být unikátní testem %(lookup_type)s pro pole " +"%(date_field_label)s." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "Pole typu: %(field_type)s" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "Hodnota \"%(value)s\" musí být buď True nebo False." + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "Hodnota \"%(value)s\" musí být buď True, False nebo None." + +msgid "Boolean (Either True or False)" +msgstr "Pravdivost (buď Ano (True), nebo Ne (False))" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "Řetězec (max. %(max_length)s znaků)" + +msgid "Comma-separated integers" +msgstr "Celá čísla oddělená čárkou" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "Hodnota \"%(value)s\" není platné datum. Musí být ve tvaru RRRR-MM-DD." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" +"Ačkoli hodnota \"%(value)s\" je ve správném tvaru (RRRR-MM-DD), jde o " +"neplatné datum." + +msgid "Date (without time)" +msgstr "Datum (bez času)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"Hodnota \"%(value)s\" je v neplatném tvaru, který má být RRRR-MM-DD HH:MM[:" +"SS[.uuuuuu]][TZ]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"Ačkoli hodnota \"%(value)s\" je ve správném tvaru (RRRR-MM-DD HH:MM[:SS[." +"uuuuuu]][TZ]), jde o neplatné datum a čas." + +msgid "Date (with time)" +msgstr "Datum (s časem)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "Hodnota \"%(value)s\" musí být desítkové číslo." + +msgid "Decimal number" +msgstr "Desetinné číslo" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" +"Hodnota \"%(value)s\" je v neplatném tvaru, který má být [DD] [HH:[MM:]]ss[." +"uuuuuu]." + +msgid "Duration" +msgstr "Doba trvání" + +msgid "Email address" +msgstr "E-mailová adresa" + +msgid "File path" +msgstr "Cesta k souboru" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "Hodnota \"%(value)s\" musí být reálné číslo." + +msgid "Floating point number" +msgstr "Číslo s pohyblivou řádovou čárkou" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "Hodnota \"%(value)s\" musí být celé číslo." + +msgid "Integer" +msgstr "Celé číslo" + +msgid "Big (8 byte) integer" +msgstr "Velké číslo (8 bajtů)" + +msgid "Small integer" +msgstr "Malé celé číslo" + +msgid "IPv4 address" +msgstr "Adresa IPv4" + +msgid "IP address" +msgstr "Adresa IP" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "Hodnota \"%(value)s\" musí být buď None, True nebo False." + +msgid "Boolean (Either True, False or None)" +msgstr "Pravdivost (buď Ano (True), Ne (False) nebo Nic (None))" + +msgid "Positive big integer" +msgstr "Velké kladné celé číslo" + +msgid "Positive integer" +msgstr "Kladné celé číslo" + +msgid "Positive small integer" +msgstr "Kladné malé celé číslo" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "Identifikátor (nejvýše %(max_length)s znaků)" + +msgid "Text" +msgstr "Text" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" +"Hodnota \"%(value)s\" je v neplatném tvaru, který má být HH:MM[:ss[.uuuuuu]]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" +"Ačkoli hodnota \"%(value)s\" je ve správném tvaru (HH:MM[:ss[.uuuuuu]]), jde " +"o neplatný čas." + +msgid "Time" +msgstr "Čas" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "Přímá binární data" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "\"%(value)s\" není platná hodnota typu UUID." + +msgid "Universally unique identifier" +msgstr "Všeobecně jedinečný identifikátor" + +msgid "File" +msgstr "Soubor" + +msgid "Image" +msgstr "Obrázek" + +msgid "A JSON object" +msgstr "Objekt typu JSON" + +msgid "Value must be valid JSON." +msgstr "Hodnota musí být platná struktura typu JSON." + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "" +"Položka typu %(model)s s hodnotou %(field)s rovnou %(value)r neexistuje." + +msgid "Foreign Key (type determined by related field)" +msgstr "Cizí klíč (typ určen pomocí souvisejícího pole)" + +msgid "One-to-one relationship" +msgstr "Vazba jedna-jedna" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "Vazba z %(from)s do %(to)s" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "Vazby z %(from)s do %(to)s" + +msgid "Many-to-many relationship" +msgstr "Vazba mnoho-mnoho" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?!" + +msgid "This field is required." +msgstr "Toto pole je třeba vyplnit." + +msgid "Enter a whole number." +msgstr "Zadejte celé číslo." + +msgid "Enter a valid date." +msgstr "Zadejte platné datum." + +msgid "Enter a valid time." +msgstr "Zadejte platný čas." + +msgid "Enter a valid date/time." +msgstr "Zadejte platné datum a čas." + +msgid "Enter a valid duration." +msgstr "Zadejte platnou délku trvání." + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "Počet dní musí být mezi {min_days} a {max_days}." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" +"Soubor nebyl odeslán. Zkontrolujte parametr \"encoding type\" formuláře." + +msgid "No file was submitted." +msgstr "Žádný soubor nebyl odeslán." + +msgid "The submitted file is empty." +msgstr "Odeslaný soubor je prázdný." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"Tento název souboru má mít nejvýše %(max)d znak (nyní má %(length)d)." +msgstr[1] "" +"Tento název souboru má mít nejvýše %(max)d znaky (nyní má %(length)d)." +msgstr[2] "" +"Tento název souboru má mít nejvýše %(max)d znaku (nyní má %(length)d)." +msgstr[3] "" +"Tento název souboru má mít nejvýše %(max)d znaků (nyní má %(length)d)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "Musíte vybrat cestu k souboru nebo vymazat výběr, ne obojí." + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Nahrajte platný obrázek. Odeslaný soubor buď nebyl obrázek nebo byl poškozen." + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "Vyberte platnou možnost, \"%(value)s\" není k dispozici." + +msgid "Enter a list of values." +msgstr "Zadejte seznam hodnot." + +msgid "Enter a complete value." +msgstr "Zadejte úplnou hodnotu." + +msgid "Enter a valid UUID." +msgstr "Zadejte platné UUID." + +msgid "Enter a valid JSON." +msgstr "Zadejte platnou strukturu typu JSON." + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(Skryté pole %(name)s) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" +"Data objektu ManagementForm chybí nebo s nimi bylo nedovoleně manipulováno. " +"Chybějící pole: %(field_names)s. Pokud problém přetrvává, budete možná muset " +"problém ohlásit." + +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +msgid "Order" +msgstr "Pořadí" + +msgid "Delete" +msgstr "Odstranit" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "Opravte duplicitní data v poli %(field)s." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "Opravte duplicitní data v poli %(field)s, které musí být unikátní." + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"Opravte duplicitní data v poli %(field_name)s, které musí být unikátní " +"testem %(lookup)s pole %(date_field)s." + +msgid "Please correct the duplicate values below." +msgstr "Odstraňte duplicitní hodnoty níže." + +msgid "The inline value did not match the parent instance." +msgstr "Hodnota typu inline neodpovídá rodičovské položce." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "Vyberte platnou možnost. Tato není k dispozici." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "\"%(pk)s\" není platná hodnota." + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"Hodnotu %(datetime)s nelze interpretovat v časové zóně %(current_timezone)s; " +"může být nejednoznačná nebo nemusí existovat." + +msgid "Clear" +msgstr "Zrušit" + +msgid "Currently" +msgstr "Aktuálně" + +msgid "Change" +msgstr "Změnit" + +msgid "Unknown" +msgstr "Neznámé" + +msgid "Yes" +msgstr "Ano" + +msgid "No" +msgstr "Ne" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "ano,ne,možná" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d bajt" +msgstr[1] "%(size)d bajty" +msgstr[2] "%(size)d bajtů" +msgstr[3] "%(size)d bajtů" + +#, python-format +msgid "%s KB" +msgstr "%s KB" + +#, python-format +msgid "%s MB" +msgstr "%s MB" + +#, python-format +msgid "%s GB" +msgstr "%s GB" + +#, python-format +msgid "%s TB" +msgstr "%s TB" + +#, python-format +msgid "%s PB" +msgstr "%s PB" + +msgid "p.m." +msgstr "odp." + +msgid "a.m." +msgstr "dop." + +msgid "PM" +msgstr "odp." + +msgid "AM" +msgstr "dop." + +msgid "midnight" +msgstr "půlnoc" + +msgid "noon" +msgstr "poledne" + +msgid "Monday" +msgstr "pondělí" + +msgid "Tuesday" +msgstr "úterý" + +msgid "Wednesday" +msgstr "středa" + +msgid "Thursday" +msgstr "čtvrtek" + +msgid "Friday" +msgstr "pátek" + +msgid "Saturday" +msgstr "sobota" + +msgid "Sunday" +msgstr "neděle" + +msgid "Mon" +msgstr "po" + +msgid "Tue" +msgstr "út" + +msgid "Wed" +msgstr "st" + +msgid "Thu" +msgstr "čt" + +msgid "Fri" +msgstr "pá" + +msgid "Sat" +msgstr "so" + +msgid "Sun" +msgstr "ne" + +msgid "January" +msgstr "leden" + +msgid "February" +msgstr "únor" + +msgid "March" +msgstr "březen" + +msgid "April" +msgstr "duben" + +msgid "May" +msgstr "květen" + +msgid "June" +msgstr "červen" + +msgid "July" +msgstr "červenec" + +msgid "August" +msgstr "srpen" + +msgid "September" +msgstr "září" + +msgid "October" +msgstr "říjen" + +msgid "November" +msgstr "listopad" + +msgid "December" +msgstr "prosinec" + +msgid "jan" +msgstr "led" + +msgid "feb" +msgstr "úno" + +msgid "mar" +msgstr "bře" + +msgid "apr" +msgstr "dub" + +msgid "may" +msgstr "kvě" + +msgid "jun" +msgstr "čen" + +msgid "jul" +msgstr "čec" + +msgid "aug" +msgstr "srp" + +msgid "sep" +msgstr "zář" + +msgid "oct" +msgstr "říj" + +msgid "nov" +msgstr "lis" + +msgid "dec" +msgstr "pro" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "Led." + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "Úno." + +msgctxt "abbrev. month" +msgid "March" +msgstr "Bře." + +msgctxt "abbrev. month" +msgid "April" +msgstr "Dub." + +msgctxt "abbrev. month" +msgid "May" +msgstr "Kvě." + +msgctxt "abbrev. month" +msgid "June" +msgstr "Čer." + +msgctxt "abbrev. month" +msgid "July" +msgstr "Čec." + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "Srp." + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "Zář." + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "Říj." + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "Lis." + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "Pro." + +msgctxt "alt. month" +msgid "January" +msgstr "ledna" + +msgctxt "alt. month" +msgid "February" +msgstr "února" + +msgctxt "alt. month" +msgid "March" +msgstr "března" + +msgctxt "alt. month" +msgid "April" +msgstr "dubna" + +msgctxt "alt. month" +msgid "May" +msgstr "května" + +msgctxt "alt. month" +msgid "June" +msgstr "června" + +msgctxt "alt. month" +msgid "July" +msgstr "července" + +msgctxt "alt. month" +msgid "August" +msgstr "srpna" + +msgctxt "alt. month" +msgid "September" +msgstr "září" + +msgctxt "alt. month" +msgid "October" +msgstr "října" + +msgctxt "alt. month" +msgid "November" +msgstr "listopadu" + +msgctxt "alt. month" +msgid "December" +msgstr "prosince" + +msgid "This is not a valid IPv6 address." +msgstr "Toto není platná adresa typu IPv6." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "nebo" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "%(num)d rok" +msgstr[1] "%(num)d roky" +msgstr[2] "%(num)d roku" +msgstr[3] "%(num)d let" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "%(num)d měsíc" +msgstr[1] "%(num)d měsíce" +msgstr[2] "%(num)d měsíců" +msgstr[3] "%(num)d měsíců" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "%(num)d týden" +msgstr[1] "%(num)d týdny" +msgstr[2] "%(num)d týdne" +msgstr[3] "%(num)d týdnů" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "%(num)d den" +msgstr[1] "%(num)d dny" +msgstr[2] "%(num)d dní" +msgstr[3] "%(num)d dní" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "%(num)d hodina" +msgstr[1] "%(num)d hodiny" +msgstr[2] "%(num)d hodiny" +msgstr[3] "%(num)d hodin" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "%(num)d minuta" +msgstr[1] "%(num)d minuty" +msgstr[2] "%(num)d minut" +msgstr[3] "%(num)d minut" + +msgid "Forbidden" +msgstr "Nepřístupné (Forbidden)" + +msgid "CSRF verification failed. Request aborted." +msgstr "Selhalo ověření typu CSRF. Požadavek byl zadržen." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" +"Tuto zprávu vidíte, protože tento web na protokolu HTTPS vyžaduje, aby váš " +"prohlížeč zaslal v požadavku záhlaví \"Referer\", k čemuž nedošlo. Záhlaví " +"je požadováno z bezpečnostních důvodů pro kontrolu toho, že prohlížeče se " +"nezmocnila třetí strana." + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"Pokud má váš prohlížeč záhlaví \"Referer\" vypnuté, žádáme vás o jeho " +"zapnutí, alespoň pro tento web nebo pro spojení typu HTTPS nebo pro " +"požadavky typu \"stejný původ\" (same origin)." + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"Pokud používáte značku nebo " +"záhlaví \"Referrer-Policy: no-referrer\", odeberte je. Ochrana typu CSRF " +"vyžaduje, aby záhlaví zajišťovalo striktní hlídání refereru. Pokud je pro " +"vás soukromí důležité, použijte k odkazům na cizí weby alternativní možnosti " +"jako například ." + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"Tato zpráva se zobrazuje, protože tento web při odesílání formulářů požaduje " +"v souboru cookie údaj CSRF, a to z bezpečnostních důvodů, aby se zajistilo, " +"že se vašeho prohlížeče nezmocnil někdo další." + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" +"Pokud má váš prohlížeč soubory cookie vypnuté, žádáme vás o jejich zapnutí, " +"alespoň pro tento web nebo pro požadavky typu \"stejný původ\" (same origin)." + +msgid "More information is available with DEBUG=True." +msgstr "V případě zapnutí volby DEBUG=True bude k dispozici více informací." + +msgid "No year specified" +msgstr "Nebyl specifikován rok" + +msgid "Date out of range" +msgstr "Datum je mimo rozsah" + +msgid "No month specified" +msgstr "Nebyl specifikován měsíc" + +msgid "No day specified" +msgstr "Nebyl specifikován den" + +msgid "No week specified" +msgstr "Nebyl specifikován týden" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "%(verbose_name_plural)s nejsou k dispozici" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"%(verbose_name_plural)s s budoucím datem nejsou k dipozici protoze " +"%(class_name)s.allow_future je False" + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "Datum \"%(datestr)s\" neodpovídá formátu \"%(format)s\"" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "Nepodařilo se nalézt žádný objekt %(verbose_name)s" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" +"Požadavek na stránku nemohl být konvertován na celé číslo, ani není \"last\"." + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Neplatná stránka (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "List je prázdný a \"%(class_name)s.allow_empty\" je nastaveno na False" + +msgid "Directory indexes are not allowed here." +msgstr "Indexy adresářů zde nejsou povoleny." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "Cesta \"%(path)s\" neexistuje" + +#, python-format +msgid "Index of %(directory)s" +msgstr "Index adresáře %(directory)s" + +msgid "The install worked successfully! Congratulations!" +msgstr "Instalace proběhla úspěšně, gratulujeme!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"Zobrazit poznámky k vydání frameworku Django " +"%(version)s" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" +"Tuto zprávu vidíte, protože máte v nastavení Djanga zapnutý vývojový režim " +"DEBUG=True a zatím nemáte " +"nastavena žádná URL." + +msgid "Django Documentation" +msgstr "Dokumentace frameworku Django" + +msgid "Topics, references, & how-to’s" +msgstr "Témata, odkazy & how-to" + +msgid "Tutorial: A Polling App" +msgstr "Tutoriál: Hlasovací aplikace" + +msgid "Get started with Django" +msgstr "Začínáme s frameworkem Django" + +msgid "Django Community" +msgstr "Komunita kolem frameworku Django" + +msgid "Connect, get help, or contribute" +msgstr "Propojte se, získejte pomoc, podílejte se" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/dsb/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/dsb/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..b1e0b249 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/dsb/LC_MESSAGES/django 3.po @@ -0,0 +1,1373 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Michael Wolf , 2016-2023 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 06:49+0000\n" +"Last-Translator: Michael Wolf , 2016-2023\n" +"Language-Team: Lower Sorbian (http://www.transifex.com/django/django/" +"language/dsb/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: dsb\n" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || " +"n%100==4 ? 2 : 3);\n" + +msgid "Afrikaans" +msgstr "Afrikaanšćina" + +msgid "Arabic" +msgstr "Arabšćina" + +msgid "Algerian Arabic" +msgstr "Algeriska arabšćina" + +msgid "Asturian" +msgstr "Asturišćina" + +msgid "Azerbaijani" +msgstr "Azerbajdžanišćina" + +msgid "Bulgarian" +msgstr "Bulgaršćina" + +msgid "Belarusian" +msgstr "Běłorušćina" + +msgid "Bengali" +msgstr "Bengalšćina" + +msgid "Breton" +msgstr "Bretońšćina" + +msgid "Bosnian" +msgstr "Bosnišćina" + +msgid "Catalan" +msgstr "Katalańšćina" + +msgid "Central Kurdish (Sorani)" +msgstr "Centralna kurdišćina (Sorani)" + +msgid "Czech" +msgstr "Češćina" + +msgid "Welsh" +msgstr "Kymrišćina" + +msgid "Danish" +msgstr "Dańšćina" + +msgid "German" +msgstr "Nimšćina" + +msgid "Lower Sorbian" +msgstr "Dolnoserbšćina" + +msgid "Greek" +msgstr "Grichišćina" + +msgid "English" +msgstr "Engelšćina" + +msgid "Australian English" +msgstr "Awstralska engelšćina" + +msgid "British English" +msgstr "Britiska engelšćina" + +msgid "Esperanto" +msgstr "Esperanto" + +msgid "Spanish" +msgstr "Špańšćina" + +msgid "Argentinian Spanish" +msgstr "Argentinska špańšćina" + +msgid "Colombian Spanish" +msgstr "Kolumbiska špańšćina" + +msgid "Mexican Spanish" +msgstr "Mexikańska špańšćina" + +msgid "Nicaraguan Spanish" +msgstr "Nikaraguaska špańšćina" + +msgid "Venezuelan Spanish" +msgstr "Venezolaniska špańšćina" + +msgid "Estonian" +msgstr "Estnišćina" + +msgid "Basque" +msgstr "Baskišćina" + +msgid "Persian" +msgstr "Persišćina" + +msgid "Finnish" +msgstr "Finšćina" + +msgid "French" +msgstr "Francojšćina" + +msgid "Frisian" +msgstr "Frizišćina" + +msgid "Irish" +msgstr "Iršćina" + +msgid "Scottish Gaelic" +msgstr "Šotiska gelišćina" + +msgid "Galician" +msgstr "Galicišćina" + +msgid "Hebrew" +msgstr "Hebrejšćina" + +msgid "Hindi" +msgstr "Hindišćina" + +msgid "Croatian" +msgstr "Chorwatšćina" + +msgid "Upper Sorbian" +msgstr "Górnoserbšćina" + +msgid "Hungarian" +msgstr "Hungoršćina" + +msgid "Armenian" +msgstr "Armeńšćina" + +msgid "Interlingua" +msgstr "Interlingua" + +msgid "Indonesian" +msgstr "Indonešćina" + +msgid "Igbo" +msgstr "Igbo" + +msgid "Ido" +msgstr "Ido" + +msgid "Icelandic" +msgstr "Islandšćina" + +msgid "Italian" +msgstr "Italšćina" + +msgid "Japanese" +msgstr "Japańšćina" + +msgid "Georgian" +msgstr "Georgišćina" + +msgid "Kabyle" +msgstr "Kabylšćina" + +msgid "Kazakh" +msgstr "Kazachšćina" + +msgid "Khmer" +msgstr "Rěc Khmerow" + +msgid "Kannada" +msgstr "Kannadišćina" + +msgid "Korean" +msgstr "Korejańšćina" + +msgid "Kyrgyz" +msgstr "Kirgišćina" + +msgid "Luxembourgish" +msgstr "Luxemburgšćina" + +msgid "Lithuanian" +msgstr "Litawšćina" + +msgid "Latvian" +msgstr "Letišćina" + +msgid "Macedonian" +msgstr "Makedońšćina" + +msgid "Malayalam" +msgstr "Malajalam" + +msgid "Mongolian" +msgstr "Mongolšćina" + +msgid "Marathi" +msgstr "Marathi" + +msgid "Malay" +msgstr "Malayzišćina" + +msgid "Burmese" +msgstr "Myanmaršćina" + +msgid "Norwegian Bokmål" +msgstr "Norwegski Bokmål" + +msgid "Nepali" +msgstr "Nepalšćina" + +msgid "Dutch" +msgstr "¨Nižozemšćina" + +msgid "Norwegian Nynorsk" +msgstr "Norwegski Nynorsk" + +msgid "Ossetic" +msgstr "Osetšćina" + +msgid "Punjabi" +msgstr "Pundžabi" + +msgid "Polish" +msgstr "Pólšćina" + +msgid "Portuguese" +msgstr "Portugišćina" + +msgid "Brazilian Portuguese" +msgstr "Brazilska portugišćina" + +msgid "Romanian" +msgstr "Rumunšćina" + +msgid "Russian" +msgstr "Rušćina" + +msgid "Slovak" +msgstr "Słowakšćina" + +msgid "Slovenian" +msgstr "Słowjeńšćina" + +msgid "Albanian" +msgstr "Albanšćina" + +msgid "Serbian" +msgstr "Serbišćina" + +msgid "Serbian Latin" +msgstr "Serbišćina, łatyńska" + +msgid "Swedish" +msgstr "Šwedšćina" + +msgid "Swahili" +msgstr "Suahelšćina" + +msgid "Tamil" +msgstr "Tamilšćina" + +msgid "Telugu" +msgstr "Telugu" + +msgid "Tajik" +msgstr "Tadźikišćina" + +msgid "Thai" +msgstr "Thaišćina" + +msgid "Turkmen" +msgstr "Turkmeńšćina" + +msgid "Turkish" +msgstr "Turkojšćina" + +msgid "Tatar" +msgstr "Tataršćina" + +msgid "Udmurt" +msgstr "Udmurtšćina" + +msgid "Ukrainian" +msgstr "Ukrainšćina" + +msgid "Urdu" +msgstr "Urdu" + +msgid "Uzbek" +msgstr "Uzbekšćina" + +msgid "Vietnamese" +msgstr "Vietnamšćina" + +msgid "Simplified Chinese" +msgstr "Zjadnorjona chinšćina" + +msgid "Traditional Chinese" +msgstr "Tradicionelna chinšćina" + +msgid "Messages" +msgstr "Powěsći" + +msgid "Site Maps" +msgstr "Wopśimjeśowy pśeglěd sedła" + +msgid "Static Files" +msgstr "Statiske dataje" + +msgid "Syndication" +msgstr "Syndikacija" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "…" + +msgid "That page number is not an integer" +msgstr "Toś ten numer boka njejo ceła licba" + +msgid "That page number is less than 1" +msgstr "Numer boka jo mjeńšy ako 1" + +msgid "That page contains no results" +msgstr "Toś ten bok njewopśimujo wuslědki" + +msgid "Enter a valid value." +msgstr "Zapódajśo płaśiwu gódnotu." + +msgid "Enter a valid URL." +msgstr "Zapódajśo płaśiwy URL." + +msgid "Enter a valid integer." +msgstr "Zapódajśo płaśiwu cełu licbu." + +msgid "Enter a valid email address." +msgstr "Zapódajśo płaśiwu e-mailowu adresu." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" +"Zapódajśo płaśiwe „adresowe mě“, kótarež jano wopśimujo pismiki, licby, " +"pódsmužki abo wězawki." + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"Zapódajśo płaśiwe „adresowe mě“, kótarež jano wopśimujo unicodowe pismiki, " +"licby, pódmužki abo wězawki." + +msgid "Enter a valid IPv4 address." +msgstr "Zapódajśo płaśiwu IPv4-adresu." + +msgid "Enter a valid IPv6 address." +msgstr "Zapódajśo płaśiwu IPv6-adresu." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "Zapódajśo płaśiwu IPv4- abo IPv6-adresu." + +msgid "Enter only digits separated by commas." +msgstr "Zapódajśo jano cyfry źělone pśez komy." + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "Zawěsććo toś tu gódnotu jo %(limit_value)s (jo %(show_value)s)." + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "" +"Zawěsććo, až toś ta gódnota jo mjeńša ako abo to samske ako %(limit_value)s." + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "" +"Zawěsććo, až toś ta gódnota jo wětša ako abo to samske ako %(limit_value)s." + +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "" +"Zawěsććo, až toś gódnota jo wjelesere kšacoweje wjelikosći %(limit_value)s." + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Zawěsććo, až toś ta gódnota ma nanejmjenjej %(limit_value)d znamuško (ma " +"%(show_value)d)." +msgstr[1] "" +"Zawěsććo, až toś ta gódnota ma nanejmjenjej %(limit_value)d znamušce (ma " +"%(show_value)d)." +msgstr[2] "" +"Zawěsććo, až toś ta gódnota ma nanejmjenjej %(limit_value)d znamuška (ma " +"%(show_value)d)." +msgstr[3] "" +"Zawěsććo, až toś ta gódnota ma nanejmjenjej %(limit_value)d znamuškow (ma " +"%(show_value)d)." + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Zawěććo, až toś ta gódnota ma maksimalnje %(limit_value)d znamuško (ma " +"%(show_value)d)." +msgstr[1] "" +"Zawěććo, až toś ta gódnota ma maksimalnje %(limit_value)d znamušce (ma " +"%(show_value)d)." +msgstr[2] "" +"Zawěććo, až toś ta gódnota ma maksimalnje %(limit_value)d znamuška (ma " +"%(show_value)d)." +msgstr[3] "" +"Zawěććo, až toś ta gódnota ma maksimalnje %(limit_value)d znamuškow (ma " +"%(show_value)d)." + +msgid "Enter a number." +msgstr "Zapódajśo licbu." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "Zawěsććo, až njejo wěcej ako %(max)s cyfry dogromady." +msgstr[1] "Zawěsććo, až njejo wěcej ako %(max)s cyfrowu dogromady." +msgstr[2] "Zawěsććo, až njejo wěcej ako %(max)s cyfrow dogromady." +msgstr[3] "Zawěsććo, až njejo wěcej ako %(max)s cyfrow dogromady." + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "Zawěsććo, až njejo wěcej ako %(max)s decimalnego městna." +msgstr[1] "Zawěsććo, až njejo wěcej ako %(max)s decimalneju městnowu." +msgstr[2] "Zawěsććo, až njejo wěcej ako %(max)s decimalnych městnow." +msgstr[3] "Zawěsććo, až njejo wěcej ako %(max)s decimalnych městnow." + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "Zawěsććo, až njejo wěcej ako %(max)s cyfry pśed decimalneju komu." +msgstr[1] "Zawěsććo, až njejo wěcej ako %(max)s cyfrowu pśed decimalneju komu." +msgstr[2] "Zawěsććo, až njejo wěcej ako %(max)s cyfrow pśed decimalneju komu." +msgstr[3] "Zawěsććo, až njejo wěcej ako %(max)s cyfrow pśed decimalneju komu." + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"Datajowy sufiks „%(extension)s“ njejo dowólony. Dowólone sufikse su: " +"%(allowed_extensions)s." + +msgid "Null characters are not allowed." +msgstr "Znamuška nul njejsu dowólone." + +msgid "and" +msgstr "a" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "%(model_name)s z toś tym %(field_labels)s južo eksistěrujo." + +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "Wobranicowanje \"%(name)s\" jo pśestupjone." + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "Gódnota %(value)r njejo płaśiwa wóleńska móžnosć." + +msgid "This field cannot be null." +msgstr "Toś to pólo njamóžo nul byś." + +msgid "This field cannot be blank." +msgstr "Toś to pólo njamóžo prozne byś." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "%(model_name)s z toś tym %(field_label)s južo eksistěrujo." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"%(field_label)s musy za %(date_field_label)s %(lookup_type)s jadnorazowy byś." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "Typ póla: %(field_type)s" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "Gódnota „%(value)s“ musy pak True pak False byś." + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "Gódnota „%(value)s“ musy pak True, False pak None byś." + +msgid "Boolean (Either True or False)" +msgstr "Boolean (pak True pak False)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "Znamuškowy rjeśazk (až %(max_length)s)" + +msgid "String (unlimited)" +msgstr "Znamuškowy rjeśazk (njewobgranicowany)" + +msgid "Comma-separated integers" +msgstr "Pśez komu źělone cełe licby" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" +"Gódnota „%(value)s“ ma njepłaśiwy datumowy format. Musy we formaśe DD.MM." +"YYYY byś." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" +"Gódnota „%(value)s“ ma korektny format (DD.MM.YYYY), ale jo njepłaśiwy datum." + +msgid "Date (without time)" +msgstr "Datum (bźez casa)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"Gódnota „%(value)s“ ma njepłaśiwy format. Musy w formaśe DD.MM.YYYY HH:MM[:" +"ss[.uuuuuu]][TZ] byś." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"Gódnota „%(value)s“ ma korektny format (DD.MM.YYYY HH:MM[:ss[.uuuuuu]][TZ]), " +"ale jo njepłaśiwy datum/cas." + +msgid "Date (with time)" +msgstr "Datum (z casom)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "Gódnota „%(value)s“ musy decimalna licba byś." + +msgid "Decimal number" +msgstr "Decimalna licba" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" +"Gódnota „%(value)s“ ma njepłaśiwy format. Musy we formaśe [DD] " +"[[HH:]MM:]ss[.uuuuuu] byś." + +msgid "Duration" +msgstr "Traśe" + +msgid "Email address" +msgstr "E-mailowa adresa" + +msgid "File path" +msgstr "Datajowa sćažka" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "Gódnota „%(value)s“ musy typ float měś." + +msgid "Floating point number" +msgstr "Licba běžeceje komy" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "Gódnota „%(value)s“ musy ceła licba byś." + +msgid "Integer" +msgstr "Integer" + +msgid "Big (8 byte) integer" +msgstr "Big (8 bajtow) integer" + +msgid "Small integer" +msgstr "Mała ceła licba" + +msgid "IPv4 address" +msgstr "IPv4-adresa" + +msgid "IP address" +msgstr "IP-adresa" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "Gódnota „%(value)s“ musy pak None, True pak False byś." + +msgid "Boolean (Either True, False or None)" +msgstr "Boolean (pak True, False pak None)" + +msgid "Positive big integer" +msgstr "Pozitiwna wjelika ceła licba" + +msgid "Positive integer" +msgstr "Pozitiwna ceła licba" + +msgid "Positive small integer" +msgstr "Pozitiwna mała ceła licba" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "Adresowe mě (až %(max_length)s)" + +msgid "Text" +msgstr "Tekst" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" +"Gódnota „%(value)s“ ma njepłaśiwy format. Musy w formaśe HH:MM[:ss[." +"uuuuuu]] byś." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" +"Gódnota „%(value)s“ ma korektny format (HH:MM[:ss[.uuuuuu]]), ale jo " +"njepłaśiwy cas." + +msgid "Time" +msgstr "Cas" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "Gropne binarne daty" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "„%(value)s“ njejo płaśiwy UUID." + +msgid "Universally unique identifier" +msgstr "Uniwerselnje jadnorazowy identifikator" + +msgid "File" +msgstr "Dataja" + +msgid "Image" +msgstr "Woraz" + +msgid "A JSON object" +msgstr "JSON-objekt" + +msgid "Value must be valid JSON." +msgstr "Gódnota musy płaśiwy JSON byś." + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "Instanca %(model)s z %(field)s %(value)r njeeksistěrujo." + +msgid "Foreign Key (type determined by related field)" +msgstr "Cuzy kluc (typ póstaja se pśez wótpowědne pólo)" + +msgid "One-to-one relationship" +msgstr "Póśěg jaden jaden" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "Póśěg %(from)s-%(to)s" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "Póśěgi %(from)s-%(to)s" + +msgid "Many-to-many relationship" +msgstr "Póśěg wjele wjele" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "Toś to pólo jo trěbne." + +msgid "Enter a whole number." +msgstr "Zapódajśo cełu licbu." + +msgid "Enter a valid date." +msgstr "Zapódajśo płaśiwy datum." + +msgid "Enter a valid time." +msgstr "Zapódajśo płaśiwy cas." + +msgid "Enter a valid date/time." +msgstr "Zapódajśo płaśiwy datum/cas." + +msgid "Enter a valid duration." +msgstr "Zapódaśe płaśiwe traśe." + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "Licba dnjow musy mjazy {min_days} a {max_days} byś." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" +"Dataja njejo se wótpósłała. Pśeglědujśo koděrowański typ na formularje. " + +msgid "No file was submitted." +msgstr "Žedna dataja jo se wótpósłała." + +msgid "The submitted file is empty." +msgstr "Wótpósłana dataja jo prozna." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"Zawěsććo, až toś to datajowe mě ma maksimalnje %(max)d znamuško (ma " +"%(length)d)." +msgstr[1] "" +"Zawěsććo, až toś to datajowe mě ma maksimalnje %(max)d znamušce (ma " +"%(length)d)." +msgstr[2] "" +"Zawěsććo, až toś to datajowe mě ma maksimalnje %(max)d znamuška (ma " +"%(length)d)." +msgstr[3] "" +"Zawěsććo, až toś to datajowe mě ma maksimalnje %(max)d znamuškow (ma " +"%(length)d)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" +"Pšosym pak wótpósćelśo dataju pak stajśo kokulku do kontrolnego kašćika, " +"njecyńśo wobej." + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Nagrajśo płaśiwy wobraz. Dataja, kótaruž sćo nagrał, pak njejo wobraz był " +"pak jo wobškóźony wobraz." + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" +"Wubjeŕśo płaśiwu wóleńsku móžnosć. %(value)s njejo jadna z k dispoziciji " +"stojecych wóleńskich móžnosćow." + +msgid "Enter a list of values." +msgstr "Zapódajśo lisćinu gódnotow." + +msgid "Enter a complete value." +msgstr "Zapódajśo dopołnu gódnotu." + +msgid "Enter a valid UUID." +msgstr "Zapódajśo płaśiwy UUID." + +msgid "Enter a valid JSON." +msgstr "Zapódajśo płaśiwy JSON." + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(Schowane pólo %(name)s) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" +"Daty ManagementForm feluju abo su wobškóźone. Felujuce póla: " +"%(field_names)s. Móžośo zmólkowu rozpšawu pisaś, jolic problem dalej " +"eksistěrujo." + +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "Pšosym wótposćelśo maksimalnje %(num)d formular." +msgstr[1] "Pšosym wótposćelśo maksimalnje %(num)d formulara." +msgstr[2] "Pšosym wótposćelśo maksimalnje %(num)d formulary." +msgstr[3] "Pšosym wótposćelśo maksimalnje %(num)d formularow." + +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "Pšosym wótposćelśo minimalnje %(num)d formular." +msgstr[1] "Pšosym wótposćelśo minimalnje %(num)d formulara." +msgstr[2] "Pšosym wótposćelśo minimalnje %(num)d formulary." +msgstr[3] "Pšosym wótposćelśo minimalnje %(num)d formularow." + +msgid "Order" +msgstr "Rěd" + +msgid "Delete" +msgstr "Lašowaś" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "Pšosym korigěrujśo dwójne daty za %(field)s." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" +"Pšosym korigěrujśo dwójne daty za %(field)s, kótarež muse jadnorazowe byś." + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"Pšosym korigěrujśo dwójne daty za %(field_name)s, kótarež muse za %(lookup)s " +"w %(date_field)s jadnorazowe byś." + +msgid "Please correct the duplicate values below." +msgstr "Pšosym korigěrujśo slědujuce dwójne gódnoty." + +msgid "The inline value did not match the parent instance." +msgstr "Gódnota inline nadrědowanej instance njewótpowědujo." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" +"Wubjeŕśo płaśiwu wóleńsku móžnosć. Toś ta wóleńska móžnosć njejo žedna z " +"wóleńskich móžnosćow." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "„%(pk)s“ njejo płaśiwa gódnota." + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"%(datetime)s njedajo se w casowej conje %(current_timezone)s " +"interpretěrowaś; jo dwójozmysłowy abo snaź njeeksistěrujo." + +msgid "Clear" +msgstr "Lašowaś" + +msgid "Currently" +msgstr "Tuchylu" + +msgid "Change" +msgstr "Změniś" + +msgid "Unknown" +msgstr "Njeznaty" + +msgid "Yes" +msgstr "Jo" + +msgid "No" +msgstr "Ně" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "jo,ně,snaź" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d bajt" +msgstr[1] "%(size)d bajta" +msgstr[2] "%(size)d bajty" +msgstr[3] "%(size)d bajtow" + +#, python-format +msgid "%s KB" +msgstr "%s KB" + +#, python-format +msgid "%s MB" +msgstr "%s MB" + +#, python-format +msgid "%s GB" +msgstr "%s GB" + +#, python-format +msgid "%s TB" +msgstr "%s TB" + +#, python-format +msgid "%s PB" +msgstr "%s PB" + +msgid "p.m." +msgstr "wótpołdnja" + +msgid "a.m." +msgstr "dopołdnja" + +msgid "PM" +msgstr "wótpołdnja" + +msgid "AM" +msgstr "dopołdnja" + +msgid "midnight" +msgstr "połnoc" + +msgid "noon" +msgstr "połdnjo" + +msgid "Monday" +msgstr "Pónjeźele" + +msgid "Tuesday" +msgstr "Wałtora" + +msgid "Wednesday" +msgstr "Srjoda" + +msgid "Thursday" +msgstr "Stwórtk" + +msgid "Friday" +msgstr "Pětk" + +msgid "Saturday" +msgstr "Sobota" + +msgid "Sunday" +msgstr "Njeźela" + +msgid "Mon" +msgstr "Pón" + +msgid "Tue" +msgstr "Wał" + +msgid "Wed" +msgstr "Srj" + +msgid "Thu" +msgstr "Stw" + +msgid "Fri" +msgstr "Pět" + +msgid "Sat" +msgstr "Sob" + +msgid "Sun" +msgstr "Nje" + +msgid "January" +msgstr "Januar" + +msgid "February" +msgstr "Februar" + +msgid "March" +msgstr "Měrc" + +msgid "April" +msgstr "Apryl" + +msgid "May" +msgstr "Maj" + +msgid "June" +msgstr "Junij" + +msgid "July" +msgstr "Julij" + +msgid "August" +msgstr "Awgust" + +msgid "September" +msgstr "September" + +msgid "October" +msgstr "Oktober" + +msgid "November" +msgstr "Nowember" + +msgid "December" +msgstr "December" + +msgid "jan" +msgstr "jan" + +msgid "feb" +msgstr "feb" + +msgid "mar" +msgstr "měr" + +msgid "apr" +msgstr "apr" + +msgid "may" +msgstr "maj" + +msgid "jun" +msgstr "jun" + +msgid "jul" +msgstr "jul" + +msgid "aug" +msgstr "awg" + +msgid "sep" +msgstr "sep" + +msgid "oct" +msgstr "okt" + +msgid "nov" +msgstr "now" + +msgid "dec" +msgstr "dec" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "Jan." + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "Feb." + +msgctxt "abbrev. month" +msgid "March" +msgstr "Měrc" + +msgctxt "abbrev. month" +msgid "April" +msgstr "Apryl" + +msgctxt "abbrev. month" +msgid "May" +msgstr "Maj" + +msgctxt "abbrev. month" +msgid "June" +msgstr "Junij" + +msgctxt "abbrev. month" +msgid "July" +msgstr "Julij" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "Awg." + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "Sept." + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "Okt." + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "Now." + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "Dec." + +msgctxt "alt. month" +msgid "January" +msgstr "Januar" + +msgctxt "alt. month" +msgid "February" +msgstr "Februar" + +msgctxt "alt. month" +msgid "March" +msgstr "Měrc" + +msgctxt "alt. month" +msgid "April" +msgstr "Apryl" + +msgctxt "alt. month" +msgid "May" +msgstr "Maj" + +msgctxt "alt. month" +msgid "June" +msgstr "Junij" + +msgctxt "alt. month" +msgid "July" +msgstr "Julij" + +msgctxt "alt. month" +msgid "August" +msgstr "Awgust" + +msgctxt "alt. month" +msgid "September" +msgstr "September" + +msgctxt "alt. month" +msgid "October" +msgstr "Oktober" + +msgctxt "alt. month" +msgid "November" +msgstr "Nowember" + +msgctxt "alt. month" +msgid "December" +msgstr "December" + +msgid "This is not a valid IPv6 address." +msgstr "To njejo płaśiwa IPv6-adresa." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "abo" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "%(num)d lěto" +msgstr[1] "%(num)d lěśe" +msgstr[2] "%(num)d lěta" +msgstr[3] "%(num)d lět" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "%(num)d mjasec" +msgstr[1] "%(num)d mjaseca" +msgstr[2] "%(num)d mjasece" +msgstr[3] "%(num)dmjasecow" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "%(num)d tyźeń" +msgstr[1] "%(num)d tyźenja" +msgstr[2] "%(num)d tyźenje" +msgstr[3] "%(num)d tyźenjow" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "%(num)d źeń " +msgstr[1] "%(num)d dnja" +msgstr[2] "%(num)d dny" +msgstr[3] "%(num)d dnjow" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "%(num)d góźina" +msgstr[1] "%(num)d góźinje" +msgstr[2] "%(num)d góźiny" +msgstr[3] "%(num)d góźin" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "%(num)d minuta" +msgstr[1] "%(num)d minuśe" +msgstr[2] "%(num)d minuty" +msgstr[3] "%(num)d minutow" + +msgid "Forbidden" +msgstr "Zakazany" + +msgid "CSRF verification failed. Request aborted." +msgstr "CSRF-pśeglědanje njejo se raźiło. Napšašowanje jo se pśetergnuło." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" +"Wiźiśo toś tu powěźeńku, dokulaž toś to HTTPS-sedło trjeba \"Referer " +"header\", aby se pśez waš webwobglědowak słało, ale žedna njejo se pósłała. " +"Toś ta głowa jo trěbna z pśicynow wěstoty, aby so zawěsćiło, až waš " +"wobglědowak njekaprujo se wót tśeśich." + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"Jolic sćo swój wobglědowak tak konfigurěrował, aby se głowy 'Referer' " +"znjemóžnili, zmóžniśo je pšosym zasej, nanejmjenjej za toś to sedło, za " +"HTTPS-zwiski abo za napšašowanja 'same-origin'." + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"Jolic woznamjenje wužywaśo " +"abo głowu „Referrer-Policy: no-referrer“ zapśimujośo, wótwónoźćo je. CSRF-" +"šćit pomina se głowu „Referer“, aby striktnu kontrolu referera pśewjasć. " +"Jolic se wó swóju priwatnosć staraśo, wužywajśo alternatiwy ako za wótkazy k sedłam tśeśich." + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"Wiźiśo toś tu powěźeńku, dokulaž toś to HTTPS-sedło trjeba CSRF-cookie, aby " +"formulary wótpósłało. Toś ten cookie jo trěbna z pśicynow wěstoty, aby so " +"zawěsćiło, až waš wobglědowak njekaprujo se wót tśeśich." + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" +"Jolic sćo swój wobglědowak tak konfigurěrował, aby cookieje znjemóžnili, " +"zmóžniśo je pšosym zasej, nanejmjenjej za toś to sedło abo za napšašowanja " +"„same-origin“." + +msgid "More information is available with DEBUG=True." +msgstr "Dalšne informacije su k dispoziciji z DEBUG=True." + +msgid "No year specified" +msgstr "Žedno lěto pódane" + +msgid "Date out of range" +msgstr "Datum zwenka wobcerka" + +msgid "No month specified" +msgstr "Žeden mjasec pódany" + +msgid "No day specified" +msgstr "Žeden źeń pódany" + +msgid "No week specified" +msgstr "Žeden tyźeń pódany" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "Žedne %(verbose_name_plural)s k dispoziciji" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"Pśichodne %(verbose_name_plural)s njejo k dispoziciji, dokulaž " +"%(class_name)s.allow_future jo False." + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" +"Njepłaśiwy „%(format)s“ za datumowy znamuškowy rjeśazk „%(datestr)s“ pódany" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "Žedno %(verbose_name)s namakane, kótarež wótpowědujo napšašowanjeju." + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "Bok njejo „last“, ani njedajo se do „int“ konwertěrowaś." + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Njepłaśiwy bok (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "Prozna lisćina a „%(class_name)s.allow_empty“ jo False." + +msgid "Directory indexes are not allowed here." +msgstr "Zapisowe indekse njejsu how dowólone." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "„%(path)s“ njeeksistěrujo" + +#, python-format +msgid "Index of %(directory)s" +msgstr "Indeks %(directory)s" + +msgid "The install worked successfully! Congratulations!" +msgstr "Instalacija jo była wuspěšna! Gratulacija!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"Wersijowe informacije za Django " +"%(version)s pokazaś" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not " +"configured any URLs." +msgstr "" +"Wiźiśo toś ten bok, dokulaž DEBUG=True jo w swójej dataji nastajenjow a njejsćo " +"konfigurěrował URL." + +msgid "Django Documentation" +msgstr "Dokumentacija Django" + +msgid "Topics, references, & how-to’s" +msgstr "Temy, reference a rozpokazanja" + +msgid "Tutorial: A Polling App" +msgstr "Rozpokazanje: Napšašowańske nałoženje" + +msgid "Get started with Django" +msgstr "Prědne kšace z Django" + +msgid "Django Community" +msgstr "Zgromaźeństwo Django" + +msgid "Connect, get help, or contribute" +msgstr "Zwězajśo, wobsarajśo se pomoc abo źěłajśo sobu" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/el/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/el/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..cbc66f9a --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/el/LC_MESSAGES/django 3.po @@ -0,0 +1,1332 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Apostolis Bessas , 2013 +# Dimitris Glezos , 2011,2013,2017 +# Fotis Athineos , 2021 +# Giannis Meletakis , 2015 +# Jannis Leidel , 2011 +# Nick Mavrakis , 2017-2020 +# Nikolas Demiridis , 2014 +# Nick Mavrakis , 2016 +# Pãnoș , 2014 +# Pãnoș , 2016 +# Serafeim Papastefanos , 2016 +# Stavros Korokithakis , 2014,2016 +# Yorgos Pagles , 2011-2012 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-09-21 10:22+0200\n" +"PO-Revision-Date: 2021-11-18 21:19+0000\n" +"Last-Translator: Transifex Bot <>\n" +"Language-Team: Greek (http://www.transifex.com/django/django/language/el/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: el\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Afrikaans" +msgstr "Αφρικάνς" + +msgid "Arabic" +msgstr "Αραβικά" + +msgid "Algerian Arabic" +msgstr "Αραβικά Αλγερίας" + +msgid "Asturian" +msgstr "Αστούριας" + +msgid "Azerbaijani" +msgstr "Γλώσσα Αζερμπαϊτζάν" + +msgid "Bulgarian" +msgstr "Βουλγαρικά" + +msgid "Belarusian" +msgstr "Λευκορώσικα" + +msgid "Bengali" +msgstr "Μπενγκάλι" + +msgid "Breton" +msgstr "Βρετονικά" + +msgid "Bosnian" +msgstr "Βοσνιακά" + +msgid "Catalan" +msgstr "Καταλανικά" + +msgid "Czech" +msgstr "Τσέχικα" + +msgid "Welsh" +msgstr "Ουαλικά" + +msgid "Danish" +msgstr "Δανέζικα" + +msgid "German" +msgstr "Γερμανικά" + +msgid "Lower Sorbian" +msgstr "Κάτω Σορβικά" + +msgid "Greek" +msgstr "Ελληνικά" + +msgid "English" +msgstr "Αγγλικά" + +msgid "Australian English" +msgstr "Αγγλικά Αυστραλίας" + +msgid "British English" +msgstr "Αγγλικά Βρετανίας" + +msgid "Esperanto" +msgstr "Εσπεράντο" + +msgid "Spanish" +msgstr "Ισπανικά" + +msgid "Argentinian Spanish" +msgstr "Ισπανικά Αργεντινής" + +msgid "Colombian Spanish" +msgstr "Ισπανικά Κολομβίας" + +msgid "Mexican Spanish" +msgstr "Μεξικανική διάλεκτος Ισπανικών" + +msgid "Nicaraguan Spanish" +msgstr "Ισπανικά Νικαράγουας " + +msgid "Venezuelan Spanish" +msgstr "Ισπανικά Βενεζουέλας" + +msgid "Estonian" +msgstr "Εσθονικά" + +msgid "Basque" +msgstr "Βάσκικα" + +msgid "Persian" +msgstr "Περσικά" + +msgid "Finnish" +msgstr "Φινλανδικά" + +msgid "French" +msgstr "Γαλλικά" + +msgid "Frisian" +msgstr "Frisian" + +msgid "Irish" +msgstr "Ιρλανδικά" + +msgid "Scottish Gaelic" +msgstr "Σκωτσέζικα Γαελικά" + +msgid "Galician" +msgstr "Γαελικά" + +msgid "Hebrew" +msgstr "Εβραϊκά" + +msgid "Hindi" +msgstr "Ινδικά" + +msgid "Croatian" +msgstr "Κροατικά" + +msgid "Upper Sorbian" +msgstr "Άνω Σορβικά" + +msgid "Hungarian" +msgstr "Ουγγρικά" + +msgid "Armenian" +msgstr "Αρμενικά" + +msgid "Interlingua" +msgstr "Ιντερλίνγκουα" + +msgid "Indonesian" +msgstr "Ινδονησιακά" + +msgid "Igbo" +msgstr "Ίγκμπο" + +msgid "Ido" +msgstr "Ίντο" + +msgid "Icelandic" +msgstr "Ισλανδικά" + +msgid "Italian" +msgstr "Ιταλικά" + +msgid "Japanese" +msgstr "Γιαπωνέζικα" + +msgid "Georgian" +msgstr "Γεωργιανά" + +msgid "Kabyle" +msgstr "Kabyle" + +msgid "Kazakh" +msgstr "Καζακστά" + +msgid "Khmer" +msgstr "Χμερ" + +msgid "Kannada" +msgstr "Κανάντα" + +msgid "Korean" +msgstr "Κορεάτικα" + +msgid "Kyrgyz" +msgstr "Κιργιζικά" + +msgid "Luxembourgish" +msgstr "Λουξεμβουργιανά" + +msgid "Lithuanian" +msgstr "Λιθουανικά" + +msgid "Latvian" +msgstr "Λεττονικά" + +msgid "Macedonian" +msgstr "Μακεδονικά" + +msgid "Malayalam" +msgstr "Μαλαγιαλάμ" + +msgid "Mongolian" +msgstr "Μογγολικά" + +msgid "Marathi" +msgstr "Μαράθι" + +msgid "Malay" +msgstr "" + +msgid "Burmese" +msgstr "Βιρμανικά" + +msgid "Norwegian Bokmål" +msgstr "Νορβηγικά Μποκμάλ" + +msgid "Nepali" +msgstr "Νεπαλέζικα" + +msgid "Dutch" +msgstr "Ολλανδικά" + +msgid "Norwegian Nynorsk" +msgstr "Νορβηγική διάλεκτος Nynorsk - Νεονορβηγική" + +msgid "Ossetic" +msgstr "Οσσετικά" + +msgid "Punjabi" +msgstr "Πουντζάμπι" + +msgid "Polish" +msgstr "Πολωνικά" + +msgid "Portuguese" +msgstr "Πορτογαλικά" + +msgid "Brazilian Portuguese" +msgstr "Πορτογαλικά - διάλεκτος Βραζιλίας" + +msgid "Romanian" +msgstr "Ρουμανικά" + +msgid "Russian" +msgstr "Ρωσικά" + +msgid "Slovak" +msgstr "Σλοβακικά" + +msgid "Slovenian" +msgstr "Σλοβενικά" + +msgid "Albanian" +msgstr "Αλβανικά" + +msgid "Serbian" +msgstr "Σερβικά" + +msgid "Serbian Latin" +msgstr "Σέρβικα Λατινικά" + +msgid "Swedish" +msgstr "Σουηδικά" + +msgid "Swahili" +msgstr "Σουαχίλι" + +msgid "Tamil" +msgstr "Διάλεκτος Ταμίλ" + +msgid "Telugu" +msgstr "Τελούγκου" + +msgid "Tajik" +msgstr "Τατζικικά" + +msgid "Thai" +msgstr "Ταϊλάνδης" + +msgid "Turkmen" +msgstr "Τουρκμενικά" + +msgid "Turkish" +msgstr "Τουρκικά" + +msgid "Tatar" +msgstr "Ταταρικά" + +msgid "Udmurt" +msgstr "Ουντμουρτικά" + +msgid "Ukrainian" +msgstr "Ουκρανικά" + +msgid "Urdu" +msgstr "Urdu" + +msgid "Uzbek" +msgstr "Ουζμπεκικά" + +msgid "Vietnamese" +msgstr "Βιετναμέζικα" + +msgid "Simplified Chinese" +msgstr "Απλοποιημένα Κινέζικα" + +msgid "Traditional Chinese" +msgstr "Παραδοσιακά Κινέζικα" + +msgid "Messages" +msgstr "Μηνύματα" + +msgid "Site Maps" +msgstr "Χάρτες Ιστότοπου" + +msgid "Static Files" +msgstr "Στατικά Αρχεία" + +msgid "Syndication" +msgstr "Syndication" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "" + +msgid "That page number is not an integer" +msgstr "Ο αριθμός αυτής της σελίδας δεν είναι ακέραιος" + +msgid "That page number is less than 1" +msgstr "Ο αριθμός αυτής της σελίδας είναι μικρότερος του 1" + +msgid "That page contains no results" +msgstr "Η σελίδα αυτή δεν περιέχει αποτελέσματα" + +msgid "Enter a valid value." +msgstr "Εισάγετε μια έγκυρη τιμή." + +msgid "Enter a valid URL." +msgstr "Εισάγετε ένα έγκυρο URL." + +msgid "Enter a valid integer." +msgstr "Εισάγετε έναν έγκυρο ακέραιο." + +msgid "Enter a valid email address." +msgstr "Εισάγετε μια έγκυρη διεύθυνση ηλ. ταχυδρομείου." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" +"Εισάγετε ένα 'slug' που να αποτελείται από γράμματα, αριθμούς, παύλες ή κάτω " +"παύλες." + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"Εισάγετε ένα 'slug' που να αποτελείται από Unicode γράμματα, παύλες ή κάτω " +"παύλες." + +msgid "Enter a valid IPv4 address." +msgstr "Εισάγετε μια έγκυρη IPv4 διεύθυνση." + +msgid "Enter a valid IPv6 address." +msgstr "Εισάγετε μία έγκυρη IPv6 διεύθυνση" + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "Εισάγετε μία έγκυρη IPv4 ή IPv6 διεύθυνση" + +msgid "Enter only digits separated by commas." +msgstr "Εισάγετε μόνο ψηφία χωρισμένα με κόμματα." + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" +"Βεβαιωθείτε ότι η τιμή είναι %(limit_value)s (η τιμή που καταχωρήσατε είναι " +"%(show_value)s)." + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "Βεβαιωθείτε ότι η τιμή είναι μικρότερη ή ίση από %(limit_value)s." + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "Βεβαιωθείτε ότι η τιμή είναι μεγαλύτερη ή ίση από %(limit_value)s." + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Βεβαιωθείται πως η τιμή αυτή έχει τουλάχιστον %(limit_value)d χαρακτήρες " +"(έχει %(show_value)d)." +msgstr[1] "" +"Βεβαιωθείτε πως η τιμή έχει τουλάχιστον %(limit_value)d χαρακτήρες (έχει " +"%(show_value)d)." + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Βεβαιωθείται πως η τιμή αυτή έχει τοπολύ %(limit_value)d χαρακτήρες (έχει " +"%(show_value)d)." +msgstr[1] "" +"Βεβαιωθείτε πως η τιμή έχει το πολύ %(limit_value)d χαρακτήρες (έχει " +"%(show_value)d)." + +msgid "Enter a number." +msgstr "Εισάγετε έναν αριθμό." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "" +"Σιγουρευτείτε οτι τα σύνολο των ψηφίων δεν είναι παραπάνω από %(max)s" +msgstr[1] "" +"Σιγουρευτείτε οτι τα σύνολο των ψηφίων δεν είναι παραπάνω από %(max)s" + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "Σιγουρευτείτε ότι το δεκαδικό ψηφίο δεν είναι παραπάνω από %(max)s." +msgstr[1] "Σιγουρευτείτε ότι τα δεκαδικά ψηφία δεν είναι παραπάνω από %(max)s." + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +"Βεβαιωθείτε ότι δεν υπάρχουν πάνω από %(max)s ψηφία πριν την υποδιαστολή." +msgstr[1] "" +"Βεβαιωθείτε ότι δεν υπάρχουν πάνω από %(max)s ψηφία πριν την υποδιαστολή." + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"Η επέκταση '%(extension)s' του αρχείου δεν επιτρέπεται. Οι επιτρεπόμενες " +"επεκτάσεις είναι: '%(allowed_extensions)s'." + +msgid "Null characters are not allowed." +msgstr "Δεν επιτρέπονται null (μηδενικοί) χαρακτήρες" + +msgid "and" +msgstr "και" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "%(model_name)s με αυτή την %(field_labels)s υπάρχει ήδη." + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "Η τιμή %(value)r δεν είναι έγκυρη επιλογή." + +msgid "This field cannot be null." +msgstr "Το πεδίο αυτό δεν μπορεί να είναι μηδενικό (null)." + +msgid "This field cannot be blank." +msgstr "Το πεδίο αυτό δεν μπορεί να είναι κενό." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "%(model_name)s με αυτό το %(field_label)s υπάρχει ήδη." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. +#. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"%(field_label)s πρέπει να είναι μοναδική για %(date_field_label)s " +"%(lookup_type)s." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "Πεδίο τύπου: %(field_type)s" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "Η τιμή '%(value)s' πρέπει να είναι True ή False." + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "Η τιμή '%(value)s' πρέπει να είναι True, False, ή None." + +msgid "Boolean (Either True or False)" +msgstr "Boolean (Είτε Αληθές ή Ψευδές)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "Συμβολοσειρά (μέχρι %(max_length)s)" + +msgid "Comma-separated integers" +msgstr "Ακέραιοι χωρισμένοι με κόμματα" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" +"Η τιμή του '%(value)s' έχει μια λανθασμένη μορφή ημερομηνίας. Η ημερομηνία " +"θα πρέπει να είναι στην μορφή YYYY-MM-DD." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" +"Η τιμή '%(value)s' είναι στην σωστή μορφή (YYYY-MM-DD) αλλά είναι μια " +"λανθασμένη ημερομηνία." + +msgid "Date (without time)" +msgstr "Ημερομηνία (χωρίς την ώρα)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"Η τιμή του '%(value)s' έχει μια λανθασμένη μορφή. Η ημερομηνία/ώρα θα πρέπει " +"να είναι στην μορφή YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"Η τιμή '%(value)s' έχει τη σωστή μορφή (YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) " +"αλλά δεν αντιστοιχεί σε σωστή ημερομηνία και ώρα." + +msgid "Date (with time)" +msgstr "Ημερομηνία (με ώρα)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "Η τιμή '%(value)s' πρέπει να είναι δεκαδικός αριθμός." + +msgid "Decimal number" +msgstr "Δεκαδικός αριθμός" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" +"Η τιμή '%(value)s' έχει εσφαλμένη μορφή. Πρέπει να είναι της μορφής [DD] " +"[[HH:]MM:]ss[.uuuuuu]." + +msgid "Duration" +msgstr "Διάρκεια" + +msgid "Email address" +msgstr "Ηλεκτρονική διεύθυνση" + +msgid "File path" +msgstr "Τοποθεσία αρχείου" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "Η '%(value)s' τιμή πρέπει να είναι δεκαδικός." + +msgid "Floating point number" +msgstr "Αριθμός κινητής υποδιαστολής" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "Η τιμή '%(value)s' πρέπει να είναι ακέραιος." + +msgid "Integer" +msgstr "Ακέραιος" + +msgid "Big (8 byte) integer" +msgstr "Μεγάλος ακέραιος - big integer (8 bytes)" + +msgid "Small integer" +msgstr "Μικρός ακέραιος" + +msgid "IPv4 address" +msgstr "Διεύθυνση IPv4" + +msgid "IP address" +msgstr "IP διεύθυνση" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "Η τιμή '%(value)s' πρέπει να είναι None, True ή False." + +msgid "Boolean (Either True, False or None)" +msgstr "Boolean (Αληθές, Ψευδές, ή τίποτα)" + +msgid "Positive big integer" +msgstr "Μεγάλος θετικός ακέραιος" + +msgid "Positive integer" +msgstr "Θετικός ακέραιος" + +msgid "Positive small integer" +msgstr "Θετικός μικρός ακέραιος" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "Slug (μέχρι %(max_length)s)" + +msgid "Text" +msgstr "Κείμενο" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" +"Η τιμή '%(value)s' έχει εσφαλμένη μορφή. Πρέπει να είναι της μορφής HH:MM[:" +"ss[.uuuuuu]]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" +"Η τιμή '%(value)s' έχει τη σωστή μορφή (HH:MM[:ss[.uuuuuu]]) αλλά δεν " +"αντιστοιχή σε σωστή ώρα." + +msgid "Time" +msgstr "Ώρα" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "Δυαδικά δεδομένα" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "'%(value)s' δεν είναι ένα έγκυρο UUID." + +msgid "Universally unique identifier" +msgstr "Καθολικά μοναδικό αναγνωριστικό" + +msgid "File" +msgstr "Αρχείο" + +msgid "Image" +msgstr "Εικόνα" + +msgid "A JSON object" +msgstr "Ένα αντικείμενο JSON" + +msgid "Value must be valid JSON." +msgstr "Η τιμή πρέπει να είναι έγκυρο JSON." + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "" +"Το μοντέλο %(model)s με την τιμή %(value)r του πεδίου %(field)s δεν υπάρχει." + +msgid "Foreign Key (type determined by related field)" +msgstr "Foreign Key (ο τύπος καθορίζεται από το πεδίο του συσχετισμού)" + +msgid "One-to-one relationship" +msgstr "Σχέση ένα-προς-ένα" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "σχέση %(from)s-%(to)s" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "σχέσεις %(from)s-%(to)s" + +msgid "Many-to-many relationship" +msgstr "Σχέση πολλά-προς-πολλά" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "Αυτό το πεδίο είναι απαραίτητο." + +msgid "Enter a whole number." +msgstr "Εισάγετε έναν ακέραιο αριθμό." + +msgid "Enter a valid date." +msgstr "Εισάγετε μια έγκυρη ημερομηνία." + +msgid "Enter a valid time." +msgstr "Εισάγετε μια έγκυρη ώρα." + +msgid "Enter a valid date/time." +msgstr "Εισάγετε μια έγκυρη ημερομηνία/ώρα." + +msgid "Enter a valid duration." +msgstr "Εισάγετε μια έγκυρη διάρκεια." + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "Ο αριθμός των ημερών πρέπει να είναι μεταξύ {min_days} και {max_days}." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" +"Δεν έχει υποβληθεί κάποιο αρχείο. Ελέγξτε τον τύπο κωδικοποίησης στη φόρμα." + +msgid "No file was submitted." +msgstr "Δεν υποβλήθηκε κάποιο αρχείο." + +msgid "The submitted file is empty." +msgstr "Το αρχείο που υποβλήθηκε είναι κενό." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"Βεβαιωθείται πως το όνομα του αρχείου έχει το πολύ %(max)d χαρακτήρα (το " +"παρόν έχει %(length)d)." +msgstr[1] "" +"Βεβαιωθείται πως το όνομα του αρχείου έχει το πολύ %(max)d χαρακτήρα (το " +"παρόν έχει %(length)d)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" +"Βεβαιωθείτε ότι είτε έχετε επιλέξει ένα αρχείο για αποστολή είτε έχετε " +"επιλέξει την εκκαθάριση του πεδίου. Δεν είναι δυνατή η επιλογή και των δύο " +"ταυτοχρόνως." + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Βεβαιωθείτε ότι το αρχείο που έχετε επιλέξει για αποστολή είναι αρχείο " +"εικόνας. Το τρέχον είτε δεν ήταν εικόνα είτε έχει υποστεί φθορά." + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" +"Βεβαιωθείτε ότι έχετε επιλέξει μία έγκυρη επιλογή. Η τιμή %(value)s δεν " +"είναι διαθέσιμη προς επιλογή." + +msgid "Enter a list of values." +msgstr "Εισάγετε μια λίστα τιμών." + +msgid "Enter a complete value." +msgstr "Εισάγετε μια πλήρης τιμή" + +msgid "Enter a valid UUID." +msgstr "Εισάγετε μια έγκυρη UUID." + +msgid "Enter a valid JSON." +msgstr "Εισάγετε ένα έγκυρο JSON." + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(Κρυφό πεδίο %(name)s) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" + +#, python-format +msgid "Please submit at most %d form." +msgid_plural "Please submit at most %d forms." +msgstr[0] "Παρακαλώ υποβάλλετε το πολύ %d φόρμα." +msgstr[1] "Παρακαλώ υποβάλλετε το πολύ %d φόρμες." + +#, python-format +msgid "Please submit at least %d form." +msgid_plural "Please submit at least %d forms." +msgstr[0] "Παρακαλώ υποβάλλετε τουλάχιστον %d φόρμα." +msgstr[1] "Παρακαλώ υποβάλλετε τουλάχιστον %d φόρμες." + +msgid "Order" +msgstr "Ταξινόμηση" + +msgid "Delete" +msgstr "Διαγραφή" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "Στο %(field)s έχετε ξαναεισάγει τα ίδια δεδομένα." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" +"Στο %(field)s έχετε ξαναεισάγει τα ίδια δεδομένα. Θα πρέπει να εμφανίζονται " +"μία φορά. " + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"Στο %(field_name)s έχετε ξαναεισάγει τα ίδια δεδομένα. Θα πρέπει να " +"εμφανίζονται μία φορά για το %(lookup)s στο %(date_field)s." + +msgid "Please correct the duplicate values below." +msgstr "Έχετε ξαναεισάγει την ίδια τιμη. Βεβαιωθείτε ότι είναι μοναδική." + +msgid "The inline value did not match the parent instance." +msgstr "Η τιμή δεν είναι ίση με την αντίστοιχη τιμή του γονικού object." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" +"Επιλέξτε μια έγκυρη επιλογή. Η επιλογή αυτή δεν είναι μία από τις διαθέσιμες " +"επιλογές." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "\"%(pk)s\" δεν είναι έγκυρη τιμή." + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"Η ημερομηνία %(datetime)s δεν μπόρεσε να μετατραπεί στην ζώνη ώρας " +"%(current_timezone)s. Ίσως να είναι ασαφής ή να μην υπάρχει." + +msgid "Clear" +msgstr "Εκκαθάριση" + +msgid "Currently" +msgstr "Τώρα" + +msgid "Change" +msgstr "Επεξεργασία" + +msgid "Unknown" +msgstr "Άγνωστο" + +msgid "Yes" +msgstr "Ναι" + +msgid "No" +msgstr "Όχι" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "ναι,όχι,ίσως" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d bytes" +msgstr[1] "%(size)d bytes" + +#, python-format +msgid "%s KB" +msgstr "%s KB" + +#, python-format +msgid "%s MB" +msgstr "%s MB" + +#, python-format +msgid "%s GB" +msgstr "%s GB" + +#, python-format +msgid "%s TB" +msgstr "%s TB" + +#, python-format +msgid "%s PB" +msgstr "%s PB" + +msgid "p.m." +msgstr "μμ." + +msgid "a.m." +msgstr "πμ." + +msgid "PM" +msgstr "ΜΜ" + +msgid "AM" +msgstr "ΠΜ" + +msgid "midnight" +msgstr "μεσάνυχτα" + +msgid "noon" +msgstr "μεσημέρι" + +msgid "Monday" +msgstr "Δευτέρα" + +msgid "Tuesday" +msgstr "Τρίτη" + +msgid "Wednesday" +msgstr "Τετάρτη" + +msgid "Thursday" +msgstr "Πέμπτη" + +msgid "Friday" +msgstr "Παρασκευή" + +msgid "Saturday" +msgstr "Σάββατο" + +msgid "Sunday" +msgstr "Κυριακή" + +msgid "Mon" +msgstr "Δευ" + +msgid "Tue" +msgstr "Τρί" + +msgid "Wed" +msgstr "Τετ" + +msgid "Thu" +msgstr "Πέμ" + +msgid "Fri" +msgstr "Παρ" + +msgid "Sat" +msgstr "Σαβ" + +msgid "Sun" +msgstr "Κυρ" + +msgid "January" +msgstr "Ιανουάριος" + +msgid "February" +msgstr "Φεβρουάριος" + +msgid "March" +msgstr "Μάρτιος" + +msgid "April" +msgstr "Απρίλιος" + +msgid "May" +msgstr "Μάιος" + +msgid "June" +msgstr "Ιούνιος" + +msgid "July" +msgstr "Ιούλιος" + +msgid "August" +msgstr "Αύγουστος" + +msgid "September" +msgstr "Σεπτέμβριος" + +msgid "October" +msgstr "Οκτώβριος" + +msgid "November" +msgstr "Νοέμβριος" + +msgid "December" +msgstr "Δεκέμβριος" + +msgid "jan" +msgstr "Ιαν" + +msgid "feb" +msgstr "Φεβ" + +msgid "mar" +msgstr "Μάρ" + +msgid "apr" +msgstr "Απρ" + +msgid "may" +msgstr "Μάι" + +msgid "jun" +msgstr "Ιούν" + +msgid "jul" +msgstr "Ιούλ" + +msgid "aug" +msgstr "Αύγ" + +msgid "sep" +msgstr "Σεπ" + +msgid "oct" +msgstr "Οκτ" + +msgid "nov" +msgstr "Νοέ" + +msgid "dec" +msgstr "Δεκ" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "Ιαν." + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "Φεβ." + +msgctxt "abbrev. month" +msgid "March" +msgstr "Μάρτιος" + +msgctxt "abbrev. month" +msgid "April" +msgstr "Απρίλ." + +msgctxt "abbrev. month" +msgid "May" +msgstr "Μάιος" + +msgctxt "abbrev. month" +msgid "June" +msgstr "Ιούν." + +msgctxt "abbrev. month" +msgid "July" +msgstr "Ιούλ." + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "Αύγ." + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "Σεπτ." + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "Οκτ." + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "Νοέμ." + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "Δεκ." + +msgctxt "alt. month" +msgid "January" +msgstr "Ιανουαρίου" + +msgctxt "alt. month" +msgid "February" +msgstr "Φεβρουαρίου" + +msgctxt "alt. month" +msgid "March" +msgstr "Μαρτίου" + +msgctxt "alt. month" +msgid "April" +msgstr "Απριλίου" + +msgctxt "alt. month" +msgid "May" +msgstr "Μαΐου" + +msgctxt "alt. month" +msgid "June" +msgstr "Ιουνίου" + +msgctxt "alt. month" +msgid "July" +msgstr "Ιουλίου" + +msgctxt "alt. month" +msgid "August" +msgstr "Αυγούστου" + +msgctxt "alt. month" +msgid "September" +msgstr "Σεπτεμβρίου" + +msgctxt "alt. month" +msgid "October" +msgstr "Οκτωβρίου" + +msgctxt "alt. month" +msgid "November" +msgstr "Νοεμβρίου" + +msgctxt "alt. month" +msgid "December" +msgstr "Δεκεμβρίου" + +msgid "This is not a valid IPv6 address." +msgstr "Αυτή δεν είναι έγκυρη διεύθυνση IPv6." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "ή" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "" +msgstr[1] "" + +msgid "Forbidden" +msgstr "Απαγορευμένο" + +msgid "CSRF verification failed. Request aborted." +msgstr "Η πιστοποίηση CSRF απέτυχε. Το αίτημα ματαιώθηκε." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"Αν οι 'Referer' headers είναι απενεργοποιημένοι στον browser σας από εσάς, " +"παρακαλούμε να τους ξανά-ενεργοποιήσετε, τουλάχιστον για αυτό το site ή για " +"τις συνδέσεις HTTPS ή για τα 'same-origin' requests." + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"Αν χρησιμοποιείτε την ετικέτα ή συμπεριλαμβάνετε την κεφαλίδα (header) 'Referrer-Policy: no-referrer', " +"παρακαλούμε αφαιρέστε τα. Η προστασία CSRF απαιτεί την κεφαλίδα 'Referer' να " +"κάνει αυστηρό έλεγχο στον referer. Αν κύριο μέλημα σας είναι η ιδιωτικότητα, " +"σκεφτείτε να χρησιμοποιήσετε εναλλακτικές μεθόδους όπως για συνδέσμους από άλλες ιστοσελίδες." + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"Βλέπετε αυτό το μήνυμα επειδή αυτή η σελίδα απαιτεί ένα CSRF cookie, όταν " +"κατατίθενται φόρμες. Αυτό το cookie είναι απαραίτητο για λόγους ασφαλείας, " +"για να εξασφαλιστεί ότι ο browser δεν έχει γίνει hijacked από τρίτους." + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" +"Αν τα cookies είναι απενεργοποιημένα στον browser σας από εσάς, παρακαλούμε " +"να τα ξανά-ενεργοποιήσετε, τουλάχιστον για αυτό το site ή για τα 'same-" +"origin' requests." + +msgid "More information is available with DEBUG=True." +msgstr "Περισσότερες πληροφορίες είναι διαθέσιμες με DEBUG=True." + +msgid "No year specified" +msgstr "Δεν έχει οριστεί χρονιά" + +msgid "Date out of range" +msgstr "Ημερομηνία εκτός εύρους" + +msgid "No month specified" +msgstr "Δεν έχει οριστεί μήνας" + +msgid "No day specified" +msgstr "Δεν έχει οριστεί μέρα" + +msgid "No week specified" +msgstr "Δεν έχει οριστεί εβδομάδα" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "Δεν υπάρχουν διαθέσιμα %(verbose_name_plural)s" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"Μελλοντικά %(verbose_name_plural)s δεν είναι διαθέσιμα διότι δεν έχει τεθεί " +"το %(class_name)s.allow_future." + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" +"Λανθασμένη μορφή ημερομηνίας '%(datestr)s' για την επιλεγμένη μορφή " +"'%(format)s'" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "Δεν βρέθηκαν %(verbose_name)s που να ικανοποιούν την αναζήτηση." + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" +"Η σελίδα δεν έχει την τιμή 'last' υποδηλώνοντας την τελευταία σελίδα, ούτε " +"μπορεί να μετατραπεί σε ακέραιο." + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Άκυρη σελίδα (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "Άδεια λίστα και το \"%(class_name)s.allow_empty\" είναι False." + +msgid "Directory indexes are not allowed here." +msgstr "Τα ευρετήρια καταλόγων δεν επιτρέπονται εδώ." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "Το \"%(path)s\" δεν υπάρχει" + +#, python-format +msgid "Index of %(directory)s" +msgstr "Ευρετήριο του %(directory)s" + +msgid "The install worked successfully! Congratulations!" +msgstr "Η εγκατάσταση δούλεψε με επιτυχία! Συγχαρητήρια!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"Δείτε τις σημειώσεις κυκλοφορίας για το " +"Django %(version)s" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" +"Βλέπετε αυτό το μήνυμα επειδή έχετε DEBUG=True στο αρχείο settings και δεν έχετε ρυθμίσει κανένα URL στο " +"αρχείο urls.py. Στρωθείτε στην δουλειά!" + +msgid "Django Documentation" +msgstr "Εγχειρίδιο Django" + +msgid "Topics, references, & how-to’s" +msgstr "Θέματα, αναφορές & \"πως να...\"" + +msgid "Tutorial: A Polling App" +msgstr "Εγχειρίδιο: Ένα App Ψηφοφορίας" + +msgid "Get started with Django" +msgstr "Ξεκινήστε με το Django" + +msgid "Django Community" +msgstr "Κοινότητα Django" + +msgid "Connect, get help, or contribute" +msgstr "Συνδεθείτε, λάβετε βοήθεια, ή συνεισφέρετε" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/es_AR/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/es_AR/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..2c8df094 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/es_AR/LC_MESSAGES/django 3.po @@ -0,0 +1,1357 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Jannis Leidel , 2011 +# lardissone , 2014 +# poli , 2014 +# Ramiro Morales, 2013-2022 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 06:49+0000\n" +"Last-Translator: Ramiro Morales, 2013-2022\n" +"Language-Team: Spanish (Argentina) (http://www.transifex.com/django/django/" +"language/es_AR/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: es_AR\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Afrikaans" +msgstr "afrikáans" + +msgid "Arabic" +msgstr "árabe" + +msgid "Algerian Arabic" +msgstr "Árabe de Argelia" + +msgid "Asturian" +msgstr "asturiano" + +msgid "Azerbaijani" +msgstr "azerbaiyán" + +msgid "Bulgarian" +msgstr "búlgaro" + +msgid "Belarusian" +msgstr "bielorruso" + +msgid "Bengali" +msgstr "bengalí" + +msgid "Breton" +msgstr "bretón" + +msgid "Bosnian" +msgstr "bosnio" + +msgid "Catalan" +msgstr "catalán" + +msgid "Central Kurdish (Sorani)" +msgstr "" + +msgid "Czech" +msgstr "checo" + +msgid "Welsh" +msgstr "galés" + +msgid "Danish" +msgstr "danés" + +msgid "German" +msgstr "alemán" + +msgid "Lower Sorbian" +msgstr "bajo sorabo" + +msgid "Greek" +msgstr "griego" + +msgid "English" +msgstr "inglés" + +msgid "Australian English" +msgstr "inglés australiano" + +msgid "British English" +msgstr "inglés británico" + +msgid "Esperanto" +msgstr "esperanto" + +msgid "Spanish" +msgstr "español" + +msgid "Argentinian Spanish" +msgstr "español (Argentina)" + +msgid "Colombian Spanish" +msgstr "español (Colombia)" + +msgid "Mexican Spanish" +msgstr "español (México)" + +msgid "Nicaraguan Spanish" +msgstr "español (Nicaragua)" + +msgid "Venezuelan Spanish" +msgstr "español (Venezuela)" + +msgid "Estonian" +msgstr "estonio" + +msgid "Basque" +msgstr "vasco" + +msgid "Persian" +msgstr "persa" + +msgid "Finnish" +msgstr "finlandés" + +msgid "French" +msgstr "francés" + +msgid "Frisian" +msgstr "frisón" + +msgid "Irish" +msgstr "irlandés" + +msgid "Scottish Gaelic" +msgstr "gaélico escocés" + +msgid "Galician" +msgstr "gallego" + +msgid "Hebrew" +msgstr "hebreo" + +msgid "Hindi" +msgstr "hindi" + +msgid "Croatian" +msgstr "croata" + +msgid "Upper Sorbian" +msgstr "alto sorabo" + +msgid "Hungarian" +msgstr "húngaro" + +msgid "Armenian" +msgstr "armenio" + +msgid "Interlingua" +msgstr "Interlingua" + +msgid "Indonesian" +msgstr "indonesio" + +msgid "Igbo" +msgstr "Igbo" + +msgid "Ido" +msgstr "ido" + +msgid "Icelandic" +msgstr "islandés" + +msgid "Italian" +msgstr "italiano" + +msgid "Japanese" +msgstr "japonés" + +msgid "Georgian" +msgstr "georgiano" + +msgid "Kabyle" +msgstr "cabilio" + +msgid "Kazakh" +msgstr "kazajo" + +msgid "Khmer" +msgstr "jémer" + +msgid "Kannada" +msgstr "canarés" + +msgid "Korean" +msgstr "coreano" + +msgid "Kyrgyz" +msgstr "kirguís" + +msgid "Luxembourgish" +msgstr "luxemburgués" + +msgid "Lithuanian" +msgstr "lituano" + +msgid "Latvian" +msgstr "letón" + +msgid "Macedonian" +msgstr "macedonio" + +msgid "Malayalam" +msgstr "malabar" + +msgid "Mongolian" +msgstr "mongol" + +msgid "Marathi" +msgstr "maratí" + +msgid "Malay" +msgstr "malayo" + +msgid "Burmese" +msgstr "burmés" + +msgid "Norwegian Bokmål" +msgstr "bokmål noruego" + +msgid "Nepali" +msgstr "nepalés" + +msgid "Dutch" +msgstr "holandés" + +msgid "Norwegian Nynorsk" +msgstr "nynorsk" + +msgid "Ossetic" +msgstr "osetio" + +msgid "Punjabi" +msgstr "panyabí" + +msgid "Polish" +msgstr "polaco" + +msgid "Portuguese" +msgstr "portugués" + +msgid "Brazilian Portuguese" +msgstr "portugués de Brasil" + +msgid "Romanian" +msgstr "rumano" + +msgid "Russian" +msgstr "ruso" + +msgid "Slovak" +msgstr "eslovaco" + +msgid "Slovenian" +msgstr "esloveno" + +msgid "Albanian" +msgstr "albanés" + +msgid "Serbian" +msgstr "serbio" + +msgid "Serbian Latin" +msgstr "latín de Serbia" + +msgid "Swedish" +msgstr "sueco" + +msgid "Swahili" +msgstr "suajili" + +msgid "Tamil" +msgstr "tamil" + +msgid "Telugu" +msgstr "telugu" + +msgid "Tajik" +msgstr "tayiko" + +msgid "Thai" +msgstr "tailandés" + +msgid "Turkmen" +msgstr "turcomano" + +msgid "Turkish" +msgstr "turco" + +msgid "Tatar" +msgstr "tártaro" + +msgid "Udmurt" +msgstr "udmurto" + +msgid "Ukrainian" +msgstr "ucraniano" + +msgid "Urdu" +msgstr "urdu" + +msgid "Uzbek" +msgstr "uzbeko" + +msgid "Vietnamese" +msgstr "vietnamita" + +msgid "Simplified Chinese" +msgstr "chino simplificado" + +msgid "Traditional Chinese" +msgstr "chino tradicional" + +msgid "Messages" +msgstr "Mensajes" + +msgid "Site Maps" +msgstr "Mapas de sitio" + +msgid "Static Files" +msgstr "Archivos estáticos" + +msgid "Syndication" +msgstr "Sindicación" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "…" + +msgid "That page number is not an integer" +msgstr "El número de página no es un entero" + +msgid "That page number is less than 1" +msgstr "El número de página es menor a 1" + +msgid "That page contains no results" +msgstr "Esa página no contiene resultados" + +msgid "Enter a valid value." +msgstr "Introduzca un valor válido." + +msgid "Enter a valid URL." +msgstr "Introduzca una URL válida." + +msgid "Enter a valid integer." +msgstr "Introduzca un valor numérico entero válido." + +msgid "Enter a valid email address." +msgstr "Introduzca una dirección de email válida." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "Introduzca un “slug” válido compuesto por letras, números o guiones." + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"Introduzca un “slug” compuesto por letras Unicode, números, guiones bajos o " +"guiones." + +msgid "Enter a valid IPv4 address." +msgstr "Introduzca una dirección IPv4 válida." + +msgid "Enter a valid IPv6 address." +msgstr "Introduzca una dirección IPv6 válida." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "Introduzca una dirección IPv4 o IPv6 válida." + +msgid "Enter only digits separated by commas." +msgstr "Introduzca sólo dígitos separados por comas." + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" +"Asegúrese de que este valor sea %(limit_value)s (actualmente es " +"%(show_value)s)." + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "Asegúrese de que este valor sea menor o igual a %(limit_value)s." + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "Asegúrese de que este valor sea mayor o igual a %(limit_value)s." + +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "Asegúrese de que este valor sea múltiplo de %(limit_value)s." + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Asegúrese de que este valor tenga como mínimo %(limit_value)d caracter " +"(tiene %(show_value)d)." +msgstr[1] "" +"Asegúrese de que este valor tenga como mínimo %(limit_value)d caracteres " +"(tiene %(show_value)d)." +msgstr[2] "" +"Asegúrese de que este valor tenga como mínimo %(limit_value)d caracteres " +"(tiene %(show_value)d)." + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Asegúrese de que este valor tenga como máximo %(limit_value)d caracter " +"(tiene %(show_value)d)." +msgstr[1] "" +"Asegúrese de que este valor tenga como máximo %(limit_value)d caracteres " +"(tiene %(show_value)d)." +msgstr[2] "" +"Asegúrese de que este valor tenga como máximo %(limit_value)d caracteres " +"(tiene %(show_value)d)." + +msgid "Enter a number." +msgstr "Introduzca un número." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "Asegúrese de que no exista en total mas de %(max)s dígito." +msgstr[1] "Asegúrese de que no existan en total mas de %(max)s dígitos." +msgstr[2] "Asegúrese de que no existan en total mas de %(max)s dígitos." + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "Asegúrese de que no exista mas de %(max)s lugar decimal." +msgstr[1] "Asegúrese de que no existan mas de %(max)s lugares decimales." +msgstr[2] "Asegúrese de que no existan mas de %(max)s lugares decimales." + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +"Asegúrese de que no exista mas de %(max)s dígito antes del punto decimal." +msgstr[1] "" +"Asegúrese de que no existan mas de %(max)s dígitos antes del punto decimal." +msgstr[2] "" +"Asegúrese de que no existan mas de %(max)s dígitos antes del punto decimal." + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"La extensión de archivo “%(extension)s” no está permitida. Las extensiones " +"aceptadas son: “%(allowed_extensions)s”." + +msgid "Null characters are not allowed." +msgstr "No se admiten caracteres nulos." + +msgid "and" +msgstr "y" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "Ya existe un/a %(model_name)s con este/a %(field_labels)s." + +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "No se cumple la restricción “%(name)s”." + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "El valor %(value)r no es una opción válida." + +msgid "This field cannot be null." +msgstr "Este campo no puede ser nulo." + +msgid "This field cannot be blank." +msgstr "Este campo no puede estar en blanco." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "Ya existe un/a %(model_name)s con este/a %(field_label)s." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"%(field_label)s debe ser único/a para un %(lookup_type)s " +"%(date_field_label)s determinado." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "Campo tipo: %(field_type)s" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "El valor de “%(value)s” debe ser Verdadero o Falso." + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "El valor de “%(value)s” debe ser Verdadero, Falso o None." + +msgid "Boolean (Either True or False)" +msgstr "Booleano (Verdadero o Falso)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "Cadena (máximo %(max_length)s)" + +msgid "String (unlimited)" +msgstr "" + +msgid "Comma-separated integers" +msgstr "Enteros separados por comas" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" +"El valor de “%(value)s” tiene un formato de fecha inválido. Debe usar el " +"formato AAAA-MM-DD." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" +"El valor de “%(value)s” tiene un formato de fecha correcto (AAAA-MM-DD) pero " +"representa una fecha inválida." + +msgid "Date (without time)" +msgstr "Fecha (sin hora)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"El valor de “%(value)s” tiene un formato inválido. Debe usar el formato AAAA-" +"MM-DD HH:MM[:ss[.uuuuuu]][TZ]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"El valor de “%(value)s” tiene un formato correcto (AAAA-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ]) pero representa una fecha/hora inválida." + +msgid "Date (with time)" +msgstr "Fecha (con hora)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "El valor de “%(value)s” debe ser un número decimal." + +msgid "Decimal number" +msgstr "Número decimal" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" +"El valor de “%(value)s” tiene un formato inválido. Debe usar el formato [DD] " +"[[HH:]MM:]ss[.uuuuuu]." + +msgid "Duration" +msgstr "Duración" + +msgid "Email address" +msgstr "Dirección de correo electrónico" + +msgid "File path" +msgstr "Ruta de archivo" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "El valor de “%(value)s” debe ser un número de coma flotante." + +msgid "Floating point number" +msgstr "Número de coma flotante" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "El valor de “%(value)s” debe ser un número entero." + +msgid "Integer" +msgstr "Entero" + +msgid "Big (8 byte) integer" +msgstr "Entero grande (8 bytes)" + +msgid "Small integer" +msgstr "Entero pequeño" + +msgid "IPv4 address" +msgstr "Dirección IPv4" + +msgid "IP address" +msgstr "Dirección IP" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "El valor de “%(value)s” debe ser None, Verdadero o Falso." + +msgid "Boolean (Either True, False or None)" +msgstr "Booleano (Verdadero, Falso o Nulo)" + +msgid "Positive big integer" +msgstr "Entero grande positivo" + +msgid "Positive integer" +msgstr "Entero positivo" + +msgid "Positive small integer" +msgstr "Entero pequeño positivo" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "Slug (de hasta %(max_length)s caracteres)" + +msgid "Text" +msgstr "Texto" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" +"El valor de “%(value)s” tiene un formato inválido. Debe usar el formato HH:" +"MM[:ss[.uuuuuu]]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" +"El valor de “%(value)s” tiene un formato correcto (HH:MM[:ss[.uuuuuu]]) pero " +"representa una hora inválida." + +msgid "Time" +msgstr "Hora" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "Datos binarios crudos" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "“%(value)s” no es un UUID válido." + +msgid "Universally unique identifier" +msgstr "Identificador universalmente único" + +msgid "File" +msgstr "Archivo" + +msgid "Image" +msgstr "Imagen" + +msgid "A JSON object" +msgstr "Un objeto JSON" + +msgid "Value must be valid JSON." +msgstr "El valor debe ser JSON válido." + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "No existe una instancia de %(model)s con %(field)s %(value)r." + +msgid "Foreign Key (type determined by related field)" +msgstr "Clave foránea (el tipo está determinado por el campo relacionado)" + +msgid "One-to-one relationship" +msgstr "Relación uno-a-uno" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "relación %(from)s-%(to)s" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "relaciones %(from)s-%(to)s" + +msgid "Many-to-many relationship" +msgstr "Relación muchos-a-muchos" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "Este campo es obligatorio." + +msgid "Enter a whole number." +msgstr "Introduzca un número entero." + +msgid "Enter a valid date." +msgstr "Introduzca una fecha válida." + +msgid "Enter a valid time." +msgstr "Introduzca un valor de hora válido." + +msgid "Enter a valid date/time." +msgstr "Introduzca un valor de fecha/hora válido." + +msgid "Enter a valid duration." +msgstr "Introduzca una duración válida." + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "La cantidad de días debe tener valores entre {min_days} y {max_days}." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" +"No se envió un archivo. Verifique el tipo de codificación en el formulario." + +msgid "No file was submitted." +msgstr "No se envió ningún archivo." + +msgid "The submitted file is empty." +msgstr "El archivo enviado está vacío." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"Asegúrese de que este nombre de archivo tenga como máximo %(max)d caracter " +"(tiene %(length)d)." +msgstr[1] "" +"Asegúrese de que este nombre de archivo tenga como máximo %(max)d caracteres " +"(tiene %(length)d)." +msgstr[2] "" +"Asegúrese de que este nombre de archivo tenga como máximo %(max)d caracteres " +"(tiene %(length)d)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "Por favor envíe un archivo o active el checkbox, pero no ambas cosas." + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Seleccione una imagen válida. El archivo que ha seleccionado no es una " +"imagen o es un archivo de imagen corrupto." + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" +"Seleccione una opción válida. %(value)s no es una de las opciones " +"disponibles." + +msgid "Enter a list of values." +msgstr "Introduzca una lista de valores." + +msgid "Enter a complete value." +msgstr "Introduzca un valor completo." + +msgid "Enter a valid UUID." +msgstr "Introduzca un UUID válido." + +msgid "Enter a valid JSON." +msgstr "Introduzca JSON válido." + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(Campo oculto %(name)s) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" +"Los datos de ManagementForm faltan o han sido alterados. Campos faltantes: " +"%(field_names)s. Si el problema persiste es posible que deba reportarlo como " +"un error." + +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "Por favor envíe un máximo de %(num)d formulario." +msgstr[1] "Por favor envíe un máximo de %(num)d formularios." +msgstr[2] "Por favor envíe un máximo de %(num)d formularios." + +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "Por favor envíe %(num)d o mas formularios." +msgstr[1] "Por favor envíe %(num)d o mas formularios." +msgstr[2] "Por favor envíe %(num)d o mas formularios." + +msgid "Order" +msgstr "Ordenar" + +msgid "Delete" +msgstr "Eliminar" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "Por favor, corrija la información duplicada en %(field)s." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" +"Por favor corrija la información duplicada en %(field)s, que debe ser única." + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"Por favor corrija la información duplicada en %(field_name)s que debe ser " +"única para el %(lookup)s en %(date_field)s." + +msgid "Please correct the duplicate values below." +msgstr "Por favor, corrija los valores duplicados detallados mas abajo." + +msgid "The inline value did not match the parent instance." +msgstr "El valor inline no coincide con el de la instancia padre." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" +"Seleccione una opción válida. La opción seleccionada no es una de las " +"disponibles." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "“%(pk)s” no es un valor válido." + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"%(datetime)s no puede ser interpretado en la zona horaria " +"%(current_timezone)s; ya que podría ser ambiguo o podría no existir." + +msgid "Clear" +msgstr "Eliminar" + +msgid "Currently" +msgstr "Actualmente" + +msgid "Change" +msgstr "Modificar" + +msgid "Unknown" +msgstr "Desconocido" + +msgid "Yes" +msgstr "Sí" + +msgid "No" +msgstr "No" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "si,no,talvez" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d byte" +msgstr[1] "%(size)d bytes" +msgstr[2] "%(size)d bytes" + +#, python-format +msgid "%s KB" +msgstr "%s KB" + +#, python-format +msgid "%s MB" +msgstr "%s MB" + +#, python-format +msgid "%s GB" +msgstr "%s GB" + +#, python-format +msgid "%s TB" +msgstr "%s TB" + +#, python-format +msgid "%s PB" +msgstr "%s PB" + +msgid "p.m." +msgstr "p.m." + +msgid "a.m." +msgstr "a.m." + +msgid "PM" +msgstr "PM" + +msgid "AM" +msgstr "AM" + +msgid "midnight" +msgstr "medianoche" + +msgid "noon" +msgstr "mediodía" + +msgid "Monday" +msgstr "lunes" + +msgid "Tuesday" +msgstr "martes" + +msgid "Wednesday" +msgstr "miércoles" + +msgid "Thursday" +msgstr "jueves" + +msgid "Friday" +msgstr "viernes" + +msgid "Saturday" +msgstr "sábado" + +msgid "Sunday" +msgstr "Domingo" + +msgid "Mon" +msgstr "Lun" + +msgid "Tue" +msgstr "Mar" + +msgid "Wed" +msgstr "Mie" + +msgid "Thu" +msgstr "Jue" + +msgid "Fri" +msgstr "Vie" + +msgid "Sat" +msgstr "Sab" + +msgid "Sun" +msgstr "Dom" + +msgid "January" +msgstr "enero" + +msgid "February" +msgstr "febrero" + +msgid "March" +msgstr "marzo" + +msgid "April" +msgstr "abril" + +msgid "May" +msgstr "mayo" + +msgid "June" +msgstr "junio" + +msgid "July" +msgstr "julio" + +msgid "August" +msgstr "agosto" + +msgid "September" +msgstr "setiembre" + +msgid "October" +msgstr "octubre" + +msgid "November" +msgstr "noviembre" + +msgid "December" +msgstr "diciembre" + +msgid "jan" +msgstr "ene" + +msgid "feb" +msgstr "feb" + +msgid "mar" +msgstr "mar" + +msgid "apr" +msgstr "abr" + +msgid "may" +msgstr "may" + +msgid "jun" +msgstr "jun" + +msgid "jul" +msgstr "jul" + +msgid "aug" +msgstr "ago" + +msgid "sep" +msgstr "set" + +msgid "oct" +msgstr "oct" + +msgid "nov" +msgstr "nov" + +msgid "dec" +msgstr "dic" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "Enero" + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "Feb." + +msgctxt "abbrev. month" +msgid "March" +msgstr "Marzo" + +msgctxt "abbrev. month" +msgid "April" +msgstr "Abril" + +msgctxt "abbrev. month" +msgid "May" +msgstr "Mayo" + +msgctxt "abbrev. month" +msgid "June" +msgstr "Junio" + +msgctxt "abbrev. month" +msgid "July" +msgstr "Julio" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "Ago." + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "Set." + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "Oct." + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "Nov." + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "Dic." + +msgctxt "alt. month" +msgid "January" +msgstr "enero" + +msgctxt "alt. month" +msgid "February" +msgstr "febrero" + +msgctxt "alt. month" +msgid "March" +msgstr "marzo" + +msgctxt "alt. month" +msgid "April" +msgstr "abril" + +msgctxt "alt. month" +msgid "May" +msgstr "mayo" + +msgctxt "alt. month" +msgid "June" +msgstr "junio" + +msgctxt "alt. month" +msgid "July" +msgstr "julio" + +msgctxt "alt. month" +msgid "August" +msgstr "agosto" + +msgctxt "alt. month" +msgid "September" +msgstr "setiembre" + +msgctxt "alt. month" +msgid "October" +msgstr "octubre" + +msgctxt "alt. month" +msgid "November" +msgstr "noviembre" + +msgctxt "alt. month" +msgid "December" +msgstr "diciembre" + +msgid "This is not a valid IPv6 address." +msgstr "Esta no es una dirección IPv6 válida." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "o" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "%(num)d año" +msgstr[1] "%(num)d años" +msgstr[2] "%(num)d años" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "%(num)d mes" +msgstr[1] "%(num)d meses" +msgstr[2] "%(num)d meses" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "%(num)d semana" +msgstr[1] "%(num)d semanas" +msgstr[2] "%(num)d semanas" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "%(num)d día" +msgstr[1] "%(num)d días" +msgstr[2] "%(num)d días" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "%(num)d hora" +msgstr[1] "%(num)d horas" +msgstr[2] "%(num)d horas" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "%(num)d minuto" +msgstr[1] "%(num)d minutos" +msgstr[2] "%(num)d minutos" + +msgid "Forbidden" +msgstr "Prohibido" + +msgid "CSRF verification failed. Request aborted." +msgstr "Verificación CSRF fallida. Petición abortada." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" +"Ud. está viendo este mensaje porque este sitio HTTPS tiene como " +"requerimiento que su navegador web envíe un encabezado “Referer” pero el " +"mismo no ha enviado uno. El hecho de que este encabezado sea obligatorio es " +"una medida de seguridad para comprobar que su navegador no está siendo " +"controlado por terceros." + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"Si ha configurado su browser para deshabilitar las cabeceras “Referer”, por " +"favor activelas al menos para este sitio, o para conexiones HTTPS o para " +"peticiones generadas desde el mismo origen." + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"Si está usando la etiqueta " +"o está incluyendo el encabezado “Referrer-Policy: no-referrer” por favor " +"quitelos. La protección CSRF necesita el encabezado “Referer” para realizar " +"una comprobación estricta de los referers. Si le preocupa la privacidad " +"tiene alternativas tales como usar en los enlaces a " +"sitios de terceros." + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"Ud. está viendo este mensaje porque este sitio tiene como requerimiento el " +"uso de una 'cookie' CSRF cuando se envíen formularios. El hecho de que esta " +"'cookie' sea obligatoria es una medida de seguridad para comprobar que su " +"browser no está siendo controlado por terceros." + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" +"Si ha configurado su browser para deshabilitar “cookies”, por favor " +"activelas al menos para este sitio o para peticiones generadas desde el " +"mismo origen." + +msgid "More information is available with DEBUG=True." +msgstr "Hay mas información disponible. Para ver la misma use DEBUG=True." + +msgid "No year specified" +msgstr "No se ha especificado el valor año" + +msgid "Date out of range" +msgstr "Fecha fuera de rango" + +msgid "No month specified" +msgstr "No se ha especificado el valor mes" + +msgid "No day specified" +msgstr "No se ha especificado el valor día" + +msgid "No week specified" +msgstr "No se ha especificado el valor semana" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "No hay %(verbose_name_plural)s disponibles" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"No hay %(verbose_name_plural)s futuros disponibles porque %(class_name)s." +"allow_future tiene el valor False." + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "Cadena de fecha inválida “%(datestr)s”, formato “%(format)s”" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "No se han encontrado %(verbose_name)s que coincidan con la consulta " + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "Página debe tener el valor “last” o un valor número entero." + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Página inválida (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "Lista vacía y “%(class_name)s.allow_empty” tiene el valor False." + +msgid "Directory indexes are not allowed here." +msgstr "" +"No está habilitada la generación de listados de directorios en esta " +"ubicación." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "“%(path)s” no existe" + +#, python-format +msgid "Index of %(directory)s" +msgstr "Listado de %(directory)s" + +msgid "The install worked successfully! Congratulations!" +msgstr "La instalación ha sido exitosa. ¡Felicitaciones!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"Ver las release notes de Django %(version)s" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not " +"configured any URLs." +msgstr "" +"Está viendo esta página porque el archivo de configuración contiene DEBUG=True y no ha configurado " +"ninguna URL." + +msgid "Django Documentation" +msgstr "Documentación de Django" + +msgid "Topics, references, & how-to’s" +msgstr "Tópicos, referencia & how-to’s" + +msgid "Tutorial: A Polling App" +msgstr "Tutorial: Una app de encuesta" + +msgid "Get started with Django" +msgstr "Comience a aprender Django" + +msgid "Django Community" +msgstr "Comunidad Django" + +msgid "Connect, get help, or contribute" +msgstr "Conéctese, consiga ayuda o contribuya" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/fa/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/fa/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..e535746b --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/fa/LC_MESSAGES/django 3.po @@ -0,0 +1,1327 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Ahmad Hosseini , 2020 +# alirezamastery , 2021 +# Ali Vakilzade , 2015 +# Arash Fazeli , 2012 +# Eric Hamiter , 2019 +# Eshagh , 2022 +# Farshad Asadpour, 2021 +# Jannis Leidel , 2011 +# Mariusz Felisiak , 2021 +# Mazdak Badakhshan , 2014 +# Milad Hazrati , 2019 +# MJafar Mashhadi , 2018 +# Mohammad Hossein Mojtahedi , 2013,2019 +# Pouya Abbassi, 2016 +# Pouya Abbassi, 2016 +# rahim agh , 2020-2021 +# Reza Mohammadi , 2013-2016 +# Saeed , 2011 +# Sina Cheraghi , 2011 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:23-0500\n" +"PO-Revision-Date: 2022-07-25 06:49+0000\n" +"Last-Translator: Eshagh \n" +"Language-Team: Persian (http://www.transifex.com/django/django/language/" +"fa/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: fa\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +msgid "Afrikaans" +msgstr "آفریکانس" + +msgid "Arabic" +msgstr "عربی" + +msgid "Algerian Arabic" +msgstr "عربی الجزایری" + +msgid "Asturian" +msgstr "آستوری" + +msgid "Azerbaijani" +msgstr "آذربایجانی" + +msgid "Bulgarian" +msgstr "بلغاری" + +msgid "Belarusian" +msgstr "بلاروس" + +msgid "Bengali" +msgstr "بنگالی" + +msgid "Breton" +msgstr "برتون" + +msgid "Bosnian" +msgstr "بوسنیایی" + +msgid "Catalan" +msgstr "کاتالونیایی" + +msgid "Czech" +msgstr "چکی" + +msgid "Welsh" +msgstr "ویلزی" + +msgid "Danish" +msgstr "دانمارکی" + +msgid "German" +msgstr "آلمانی" + +msgid "Lower Sorbian" +msgstr "صربستانی پایین" + +msgid "Greek" +msgstr "یونانی" + +msgid "English" +msgstr "انگلیسی" + +msgid "Australian English" +msgstr "انگلیسی استرالیایی" + +msgid "British English" +msgstr "انگلیسی بریتیش" + +msgid "Esperanto" +msgstr "اسپرانتو" + +msgid "Spanish" +msgstr "اسپانیایی" + +msgid "Argentinian Spanish" +msgstr "اسپانیایی آرژانتینی" + +msgid "Colombian Spanish" +msgstr "اسپانیایی کلمبیایی" + +msgid "Mexican Spanish" +msgstr "اسپانیولی مکزیکی" + +msgid "Nicaraguan Spanish" +msgstr "نیکاراگوئه اسپانیایی" + +msgid "Venezuelan Spanish" +msgstr "ونزوئلا اسپانیایی" + +msgid "Estonian" +msgstr "استونی" + +msgid "Basque" +msgstr "باسکی" + +msgid "Persian" +msgstr "فارسی" + +msgid "Finnish" +msgstr "فنلاندی" + +msgid "French" +msgstr "فرانسوی" + +msgid "Frisian" +msgstr "فریزی" + +msgid "Irish" +msgstr "ایرلندی" + +msgid "Scottish Gaelic" +msgstr "گیلیک اسکاتلندی" + +msgid "Galician" +msgstr "گالیسیایی" + +msgid "Hebrew" +msgstr "عبری" + +msgid "Hindi" +msgstr "هندی" + +msgid "Croatian" +msgstr "کرواتی" + +msgid "Upper Sorbian" +msgstr "صربستانی بالا" + +msgid "Hungarian" +msgstr "مجاری" + +msgid "Armenian" +msgstr "ارمنی" + +msgid "Interlingua" +msgstr "اینترلینگوا" + +msgid "Indonesian" +msgstr "اندونزیایی" + +msgid "Igbo" +msgstr "ایگبو" + +msgid "Ido" +msgstr "ایدو" + +msgid "Icelandic" +msgstr "ایسلندی" + +msgid "Italian" +msgstr "ایتالیایی" + +msgid "Japanese" +msgstr "ژاپنی" + +msgid "Georgian" +msgstr "گرجی" + +msgid "Kabyle" +msgstr "قبایلی" + +msgid "Kazakh" +msgstr "قزاقستان" + +msgid "Khmer" +msgstr "خمری" + +msgid "Kannada" +msgstr "کناده‌ای" + +msgid "Korean" +msgstr "کره‌ای" + +msgid "Kyrgyz" +msgstr "قرقیزی" + +msgid "Luxembourgish" +msgstr "لوگزامبورگی" + +msgid "Lithuanian" +msgstr "لیتوانی" + +msgid "Latvian" +msgstr "لتونیایی" + +msgid "Macedonian" +msgstr "مقدونی" + +msgid "Malayalam" +msgstr "مالایایی" + +msgid "Mongolian" +msgstr "مغولی" + +msgid "Marathi" +msgstr "مِراتی" + +msgid "Malay" +msgstr "Malay" + +msgid "Burmese" +msgstr "برمه‌ای" + +msgid "Norwegian Bokmål" +msgstr "نروژی" + +msgid "Nepali" +msgstr "نپالی" + +msgid "Dutch" +msgstr "هلندی" + +msgid "Norwegian Nynorsk" +msgstr "نروژی Nynorsk" + +msgid "Ossetic" +msgstr "آسی" + +msgid "Punjabi" +msgstr "پنجابی" + +msgid "Polish" +msgstr "لهستانی" + +msgid "Portuguese" +msgstr "پرتغالی" + +msgid "Brazilian Portuguese" +msgstr "پرتغالیِ برزیل" + +msgid "Romanian" +msgstr "رومانی" + +msgid "Russian" +msgstr "روسی" + +msgid "Slovak" +msgstr "اسلواکی" + +msgid "Slovenian" +msgstr "اسلووِنی" + +msgid "Albanian" +msgstr "آلبانیایی" + +msgid "Serbian" +msgstr "صربی" + +msgid "Serbian Latin" +msgstr "صربی لاتین" + +msgid "Swedish" +msgstr "سوئدی" + +msgid "Swahili" +msgstr "سواحیلی" + +msgid "Tamil" +msgstr "تامیلی" + +msgid "Telugu" +msgstr "تلوگویی" + +msgid "Tajik" +msgstr "تاجیک" + +msgid "Thai" +msgstr "تایلندی" + +msgid "Turkmen" +msgstr "ترکمن" + +msgid "Turkish" +msgstr "ترکی" + +msgid "Tatar" +msgstr "تاتار" + +msgid "Udmurt" +msgstr "ادمورت" + +msgid "Ukrainian" +msgstr "اکراینی" + +msgid "Urdu" +msgstr "اردو" + +msgid "Uzbek" +msgstr "ازبکی" + +msgid "Vietnamese" +msgstr "ویتنامی" + +msgid "Simplified Chinese" +msgstr "چینی ساده‌شده" + +msgid "Traditional Chinese" +msgstr "چینی سنتی" + +msgid "Messages" +msgstr "پیغام‌ها" + +msgid "Site Maps" +msgstr "نقشه‌های وب‌گاه" + +msgid "Static Files" +msgstr "پرونده‌های استاتیک" + +msgid "Syndication" +msgstr "پیوند" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "…" + +msgid "That page number is not an integer" +msgstr "شمارهٔ صفحه یک عدد طبیعی نیست" + +msgid "That page number is less than 1" +msgstr "شمارهٔ صفحه کوچکتر از ۱ است" + +msgid "That page contains no results" +msgstr "این صفحه خالی از اطلاعات است" + +msgid "Enter a valid value." +msgstr "یک مقدار معتبر وارد کنید." + +msgid "Enter a valid URL." +msgstr "یک نشانی اینترنتی معتبر وارد کنید." + +msgid "Enter a valid integer." +msgstr "یک عدد معتبر وارد کنید." + +msgid "Enter a valid email address." +msgstr "یک ایمیل آدرس معتبر وارد کنید." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" +"یک \"اسلاگ\" معتبر متشکل از حروف، اعداد، خط زیر یا خط فاصله، وارد کنید. " + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"یک \"اسلاگ\" معتبر وارد کنید که شامل حروف یونیکد، اعداد، خط زیر یا خط فاصله " +"باشد." + +msgid "Enter a valid IPv4 address." +msgstr "یک نشانی IPv4 معتبر وارد کنید." + +msgid "Enter a valid IPv6 address." +msgstr "یک آدرس معتبر IPv6 وارد کنید." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "IPv4 یا IPv6 آدرس معتبر وارد کنید." + +msgid "Enter only digits separated by commas." +msgstr "فقط ارقام جدا شده با کاما وارد کنید." + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "مطمئن شوید مقدار %(limit_value)s است. (اکنون %(show_value)s می باشد)." + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "مطمئن شوید این مقدار کوچکتر و یا مساوی %(limit_value)s است." + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "مطمئن شوید این مقدار بزرگتر و یا مساوی %(limit_value)s است." + +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "" + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"طول این مقدار باید حداقل %(limit_value)d کاراکتر باشد (طولش %(show_value)d " +"است)." +msgstr[1] "" +"طول این مقدار باید حداقل %(limit_value)d کاراکتر باشد (طولش %(show_value)d " +"است)." + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"طول این مقدار باید حداکثر %(limit_value)d کاراکتر باشد (طولش %(show_value)d " +"است)." +msgstr[1] "" +"طول این مقدار باید حداکثر %(limit_value)d کاراکتر باشد (طولش %(show_value)d " +"است)." + +msgid "Enter a number." +msgstr "یک عدد وارد کنید." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "نباید در مجموع بیش از %(max)s رقم داشته باشد." +msgstr[1] "نباید در مجموع بیش از %(max)s رقم داشته باشد." + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "نباید بیش از %(max)s رقم اعشار داشته باشد." +msgstr[1] "نباید بیش از %(max)s رقم اعشار داشته باشد." + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "نباید بیش از %(max)s رقم قبل ممیز داشته باشد." +msgstr[1] "نباید بیش از %(max)s رقم قبل ممیز داشته باشد." + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"استفاده از پرونده با پسوند '%(extension)s' مجاز نیست. پسوند‌های مجاز عبارتند " +"از: '%(allowed_extensions)s'" + +msgid "Null characters are not allowed." +msgstr "کاراکترهای تهی مجاز نیستند." + +msgid "and" +msgstr "و" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "‏%(model_name)s با این %(field_labels)s وجود دارد." + +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "" + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "مقدار %(value)r انتخاب معتبری نیست. " + +msgid "This field cannot be null." +msgstr "این فیلد نمی تواند پوچ باشد." + +msgid "This field cannot be blank." +msgstr "این فیلد نمی تواند خالی باشد." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "%(model_name)s با این %(field_label)s از قبل موجود است." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"‏%(field_label)s باید برای %(lookup_type)s %(date_field_label)s یکتا باشد." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "فیلد با نوع: %(field_type)s" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "مقدار «%(value)s» باید True یا False باشد." + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "مقدار «%(value)s» باید True یا False یا None باشد." + +msgid "Boolean (Either True or False)" +msgstr "بولی (درست یا غلط)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "رشته (تا %(max_length)s)" + +msgid "Comma-separated integers" +msgstr "اعداد صحیح جدا-شده با ویلگول" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" +"مقدار «%(value)s» در قالب نادرستی وارد شده است. تاریخ باید در قالب YYYY-MM-" +"DD باشد." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" +"مقدار تاریخ «%(value)s» با اینکه در قالب درستی (YYYY-MM-DD) است ولی تاریخ " +"ناممکنی را نشان می‌دهد." + +msgid "Date (without time)" +msgstr "تاریخ (بدون زمان)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"مقدار \"%(value)s\" یک قالب نامعتبر دارد. باید در قالب YYYY-MM-DD HH:MM[:" +"ss[.uuuuuu]][TZ] باشد." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"مقدار \"%(value)s\" یک قالب معتبر دارد (YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) " +"اما یک تاریخ/زمان نامعتبر است." + +msgid "Date (with time)" +msgstr "تاریخ (با زمان)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "مقدار '%(value)s' باید عدد دسیمال باشد." + +msgid "Decimal number" +msgstr "عدد دهدهی" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" +"مقدار «%(value)s» در قالب نادرستی وارد شده است. باید در قالب ‎[DD] [HH:" +"[MM:]]ss[.uuuuuu]‎ باشد." + +msgid "Duration" +msgstr "بازهٔ زمانی" + +msgid "Email address" +msgstr "نشانی پست الکترونیکی" + +msgid "File path" +msgstr "مسیر پرونده" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "مقدار «%(value)s» باید عدد اعشاری فلوت باشد." + +msgid "Floating point number" +msgstr "عدد اعشاری" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "مقدار «%(value)s» باید عدد حقیقی باشد." + +msgid "Integer" +msgstr "عدد صحیح" + +msgid "Big (8 byte) integer" +msgstr "بزرگ (8 بایت) عدد صحیح" + +msgid "Small integer" +msgstr "عدد صحیح کوچک" + +msgid "IPv4 address" +msgstr "IPv4 آدرس" + +msgid "IP address" +msgstr "نشانی IP" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "مقدار «%(value)s» باید True یا False یا None باشد." + +msgid "Boolean (Either True, False or None)" +msgstr "‌بولی (درست، نادرست یا پوچ)" + +msgid "Positive big integer" +msgstr "عدد صحیح مثبت" + +msgid "Positive integer" +msgstr "عدد صحیح مثبت" + +msgid "Positive small integer" +msgstr "مثبت عدد صحیح کوچک" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "تیتر (حداکثر %(max_length)s)" + +msgid "Text" +msgstr "متن" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" +"مقدار «%(value)s» در قالب نادرستی وارد شده است. باید در قالب HH:MM[:ss[." +"uuuuuu]]‎ باشد." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" +"مقدار «%(value)s» با اینکه در قالب درستی (HH:MM[:ss[.uuuuuu]]‎) است ولی زمان " +"ناممکنی را نشان می‌دهد." + +msgid "Time" +msgstr "زمان" + +msgid "URL" +msgstr "نشانی اینترنتی" + +msgid "Raw binary data" +msgstr "دادهٔ دودویی خام" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "\"%(value)s\" یک UUID معتبر نیست." + +msgid "Universally unique identifier" +msgstr "شناسه منحصر به فرد سراسری" + +msgid "File" +msgstr "پرونده" + +msgid "Image" +msgstr "تصویر" + +msgid "A JSON object" +msgstr "یک شیء JSON" + +msgid "Value must be valid JSON." +msgstr "مقدار، باید یک JSON معتبر باشد." + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "%(model)s با %(field)s %(value)r وجود ندارد." + +msgid "Foreign Key (type determined by related field)" +msgstr "کلید خارجی ( نوع بر اساس فیلد رابط مشخص میشود )" + +msgid "One-to-one relationship" +msgstr "رابطه یک به یک " + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "رابطه %(from)s به %(to)s" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "روابط %(from)s به %(to)s" + +msgid "Many-to-many relationship" +msgstr "رابطه چند به چند" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":؟.!" + +msgid "This field is required." +msgstr "این فیلد لازم است." + +msgid "Enter a whole number." +msgstr "به طور کامل یک عدد وارد کنید." + +msgid "Enter a valid date." +msgstr "یک تاریخ معتبر وارد کنید." + +msgid "Enter a valid time." +msgstr "یک زمان معتبر وارد کنید." + +msgid "Enter a valid date/time." +msgstr "یک تاریخ/زمان معتبر وارد کنید." + +msgid "Enter a valid duration." +msgstr "یک بازهٔ زمانی معتبر وارد کنید." + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "عدد روز باید بین {min_days} و {max_days} باشد." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "پرونده‌ای ارسال نشده است. نوع کدگذاری فرم را بررسی کنید." + +msgid "No file was submitted." +msgstr "پرونده‌ای ارسال نشده است." + +msgid "The submitted file is empty." +msgstr "پروندهٔ ارسال‌شده خالیست." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"طول عنوان پرونده باید حداقل %(max)d کاراکتر باشد (طولش %(length)d است)." +msgstr[1] "" +"طول عنوان پرونده باید حداقل %(max)d کاراکتر باشد (طولش %(length)d است)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "لطفا یا فایل ارسال کنید یا دکمه پاک کردن را علامت بزنید، نه هردو." + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"یک تصویر معتبر بارگذاری کنید. پرونده‌ای که بارگذاری کردید یا تصویر نبوده و یا " +"تصویری مخدوش بوده است." + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "یک گزینهٔ معتبر انتخاب کنید. %(value)s از گزینه‌های موجود نیست." + +msgid "Enter a list of values." +msgstr "فهرستی از مقادیر وارد کنید." + +msgid "Enter a complete value." +msgstr "یک مقدار کامل وارد کنید." + +msgid "Enter a valid UUID." +msgstr "یک UUID معتبر وارد کنید." + +msgid "Enter a valid JSON." +msgstr "یک JSON معتبر وارد کنید" + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(فیلد پنهان %(name)s) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" +"اطلاعات ManagementForm مفقود یا دستکاری شده است. ردیف های مفقود شده: " +"%(field_names)s. اگر این مشکل ادامه داشت، آن را گزارش کنید." + +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "" +msgstr[1] "" + +msgid "Order" +msgstr "ترتیب:" + +msgid "Delete" +msgstr "حذف" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "لطفا محتوی تکراری برای %(field)s را اصلاح کنید." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "لطفا محتوی تکراری برای %(field)s را که باید یکتا باشد اصلاح کنید." + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"لطفا اطلاعات تکراری %(field_name)s را اصلاح کنید که باید در %(lookup)s " +"یکتا باشد %(date_field)s." + +msgid "Please correct the duplicate values below." +msgstr "لطفا مقدار تکراری را اصلاح کنید." + +msgid "The inline value did not match the parent instance." +msgstr "مقدار درون خطی موجود با نمونه والد آن مطابقت ندارد." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "یک گزینهٔ معتبر انتخاب کنید. آن گزینه از گزینه‌های موجود نیست." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "\"%(pk)s\" یک مقدار معتبر نیست." + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"%(datetime)sدر محدوده زمانی %(current_timezone)s، قابل تفسیر نیست؛ ممکن است " +"نامشخص باشد یا اصلاً وجود نداشته باشد." + +msgid "Clear" +msgstr "پاک کردن" + +msgid "Currently" +msgstr "در حال حاضر" + +msgid "Change" +msgstr "تغییر" + +msgid "Unknown" +msgstr "ناشناخته" + +msgid "Yes" +msgstr "بله" + +msgid "No" +msgstr "خیر" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "بله،خیر،شاید" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d بایت" +msgstr[1] "%(size)d بایت" + +#, python-format +msgid "%s KB" +msgstr "%s KB" + +#, python-format +msgid "%s MB" +msgstr "%s MB" + +#, python-format +msgid "%s GB" +msgstr "%s GB" + +#, python-format +msgid "%s TB" +msgstr "%s TB" + +#, python-format +msgid "%s PB" +msgstr "%s PB" + +msgid "p.m." +msgstr "ب.ظ." + +msgid "a.m." +msgstr "صبح" + +msgid "PM" +msgstr "بعد از ظهر" + +msgid "AM" +msgstr "صبح" + +msgid "midnight" +msgstr "نیمه شب" + +msgid "noon" +msgstr "ظهر" + +msgid "Monday" +msgstr "دوشنبه" + +msgid "Tuesday" +msgstr "سه شنبه" + +msgid "Wednesday" +msgstr "چهارشنبه" + +msgid "Thursday" +msgstr "پنجشنبه" + +msgid "Friday" +msgstr "جمعه" + +msgid "Saturday" +msgstr "شنبه" + +msgid "Sunday" +msgstr "یکشنبه" + +msgid "Mon" +msgstr "دوشنبه" + +msgid "Tue" +msgstr "سه‌شنبه" + +msgid "Wed" +msgstr "چهارشنبه" + +msgid "Thu" +msgstr "پنجشنبه" + +msgid "Fri" +msgstr "جمعه" + +msgid "Sat" +msgstr "شنبه" + +msgid "Sun" +msgstr "یکشنبه" + +msgid "January" +msgstr "ژانویه" + +msgid "February" +msgstr "فوریه" + +msgid "March" +msgstr "مارس" + +msgid "April" +msgstr "آوریل" + +msgid "May" +msgstr "مه" + +msgid "June" +msgstr "ژوئن" + +msgid "July" +msgstr "ژوئیه" + +msgid "August" +msgstr "اوت" + +msgid "September" +msgstr "سپتامبر" + +msgid "October" +msgstr "اکتبر" + +msgid "November" +msgstr "نوامبر" + +msgid "December" +msgstr "دسامبر" + +msgid "jan" +msgstr "ژانویه" + +msgid "feb" +msgstr "فوریه" + +msgid "mar" +msgstr "مارس" + +msgid "apr" +msgstr "آوریل" + +msgid "may" +msgstr "مه" + +msgid "jun" +msgstr "ژوئن" + +msgid "jul" +msgstr "ژوئیه" + +msgid "aug" +msgstr "اوت" + +msgid "sep" +msgstr "سپتامبر" + +msgid "oct" +msgstr "اکتبر" + +msgid "nov" +msgstr "نوامبر" + +msgid "dec" +msgstr "دسامبر" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "ژانویه" + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "فوریه" + +msgctxt "abbrev. month" +msgid "March" +msgstr "مارس" + +msgctxt "abbrev. month" +msgid "April" +msgstr "آوریل" + +msgctxt "abbrev. month" +msgid "May" +msgstr "مه" + +msgctxt "abbrev. month" +msgid "June" +msgstr "ژوئن" + +msgctxt "abbrev. month" +msgid "July" +msgstr "جولای" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "اوت" + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "سپتامبر" + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "اکتبر" + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "نوامبر" + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "دسامبر" + +msgctxt "alt. month" +msgid "January" +msgstr "ژانویه" + +msgctxt "alt. month" +msgid "February" +msgstr "فوریه" + +msgctxt "alt. month" +msgid "March" +msgstr "مارس" + +msgctxt "alt. month" +msgid "April" +msgstr "آوریل" + +msgctxt "alt. month" +msgid "May" +msgstr "مه" + +msgctxt "alt. month" +msgid "June" +msgstr "ژوئن" + +msgctxt "alt. month" +msgid "July" +msgstr "جولای" + +msgctxt "alt. month" +msgid "August" +msgstr "اوت" + +msgctxt "alt. month" +msgid "September" +msgstr "سپتامبر" + +msgctxt "alt. month" +msgid "October" +msgstr "اکتبر" + +msgctxt "alt. month" +msgid "November" +msgstr "نوامبر" + +msgctxt "alt. month" +msgid "December" +msgstr "دسامبر" + +msgid "This is not a valid IPv6 address." +msgstr "این مقدار آدرس IPv6 معتبری نیست." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "یا" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr "،" + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "%(num)d سال" +msgstr[1] "%(num)d سال" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "%(num)d ماه" +msgstr[1] "%(num)d ماه" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "%(num)d هفته" +msgstr[1] "%(num)d هفته" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "%(num)d روز" +msgstr[1] "%(num)d روز" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "%(num)d ساعت" +msgstr[1] "%(num)d ساعت" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "%(num)d دقیقه" +msgstr[1] "%(num)d دقیقه" + +msgid "Forbidden" +msgstr "ممنوع" + +msgid "CSRF verification failed. Request aborted." +msgstr "‏CSRF تأیید نشد. درخواست لغو شد." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" +"شما این پیغام را مشاهده میکنید برای اینکه این HTTPS site نیازمند یک " +"\"Referer header\" برای ارسال توسط مرورگر شما دارد،‌اما مقداری ارسال " +"نمیشود . این هدر الزامی میباشد برای امنیت ، در واقع برای اینکه مرورگر شما " +"مطمین شود hijack به عنوان نفر سوم (third parties) در میان نیست" + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"اگر در مرورگر خود سر تیتر \"Referer\" را غیرفعال کرده‌اید، لطفاً آن را فعال " +"کنید، یا حداقل برای این وب‌گاه یا برای ارتباطات HTTPS و یا برای درخواست‌های " +"\"Same-origin\" فعال کنید." + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"اگر شما از تگ استفاده " +"می‌کنید یا سر تیتر \"Referrer-Policy: no-referrer\" را اضافه کرده‌اید، لطفاً " +"آن را حذف کنید. محافظ CSRF به سرتیتر \"Referer\" نیاز دارد تا بتواند بررسی " +"سخت‌گیرانه ارجاع دهنده را انجام دهد. اگر ملاحظاتی در مورد حریم خصوصی دارید از " +"روش‎‌های جایگزین مانند برای ارجاع دادن به وب‌گاه‌های " +"شخص ثالث استفاده کنید." + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"شما این پیام را میبینید چون این سایت نیازمند کوکی «جعل درخواست میان وبگاهی " +"(CSRF)» است. این کوکی برای امنیت شما ضروری است. با این کوکی می‌توانیم از " +"اینکه شخص ثالثی کنترل مرورگرتان را به دست نگرفته است اطمینان پیدا کنیم." + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" +"اگر مرورگر خود را تنظیم کرده‌اید که کوکی غیرفعال باشد، لطفاً مجدداً آن را فعال " +"کنید؛ حداقل برای این وب‌گاه یا برای درخواست‌های \"same-origin\"." + +msgid "More information is available with DEBUG=True." +msgstr "اطلاعات بیشتر با DEBUG=True ارائه خواهد شد." + +msgid "No year specified" +msgstr "هیچ سالی مشخص نشده است" + +msgid "Date out of range" +msgstr "تاریخ غیرمجاز است" + +msgid "No month specified" +msgstr "هیچ ماهی مشخص نشده است" + +msgid "No day specified" +msgstr "هیچ روزی مشخص نشده است" + +msgid "No week specified" +msgstr "هیچ هفته‌ای مشخص نشده است" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "هیچ %(verbose_name_plural)s موجود نیست" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"آینده %(verbose_name_plural)s امکان پذیر نیست زیرا مقدار %(class_name)s." +"allow_future برابر False تنظیم شده است." + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "نوشته تاریخ \"%(datestr)s\" در قالب \"%(format)s\" نامعتبر است" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "هیچ %(verbose_name)s ای مطابق جستجو پیدا نشد." + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "صفحه \"آخرین\" نیست یا شماره صفحه قابل ترجمه به یک عدد صحیح نیست." + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "صفحه‌ی اشتباه (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "لیست خالی و \"%(class_name)s.allow_empty\" برابر False است." + +msgid "Directory indexes are not allowed here." +msgstr "شاخص دایرکتوری اینجا قابل قبول نیست." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "\"%(path)s\" وجود ندارد " + +#, python-format +msgid "Index of %(directory)s" +msgstr "فهرست %(directory)s" + +msgid "The install worked successfully! Congratulations!" +msgstr "نصب درست کار کرد. تبریک می گویم!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"نمایش release notes برای نسخه %(version)s " +"جنگو" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" +"شما این صفحه را به این دلیل مشاهده می کنید که DEBUG=True در فایل تنظیمات شما وجود دارد و شما هیچ URL " +"تنظیم نکرده اید." + +msgid "Django Documentation" +msgstr "مستندات جنگو" + +msgid "Topics, references, & how-to’s" +msgstr "سرفصل‌ها، منابع و دستورالعمل‌ها" + +msgid "Tutorial: A Polling App" +msgstr "آموزش گام به گام: برنامکی برای رأی‌گیری" + +msgid "Get started with Django" +msgstr "شروع به کار با جنگو" + +msgid "Django Community" +msgstr "جامعهٔ جنگو" + +msgid "Connect, get help, or contribute" +msgstr "متصل شوید، کمک بگیرید یا مشارکت کنید" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/fi/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/fi/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..e732a740 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/fi/LC_MESSAGES/django 3.po @@ -0,0 +1,1316 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Aarni Koskela, 2015,2017-2018,2020-2022 +# Antti Kaihola , 2011 +# Jannis Leidel , 2011 +# Jiri Grönroos , 2021 +# Lasse Liehu , 2015 +# Mika Mäkelä , 2018 +# Klaus Dahlén, 2011 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:23-0500\n" +"PO-Revision-Date: 2022-07-25 06:49+0000\n" +"Last-Translator: Aarni Koskela\n" +"Language-Team: Finnish (http://www.transifex.com/django/django/language/" +"fi/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: fi\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Afrikaans" +msgstr "afrikaans" + +msgid "Arabic" +msgstr "arabia" + +msgid "Algerian Arabic" +msgstr "Algerian arabia" + +msgid "Asturian" +msgstr "asturian kieli" + +msgid "Azerbaijani" +msgstr "azeri" + +msgid "Bulgarian" +msgstr "bulgaria" + +msgid "Belarusian" +msgstr "valkovenäjän kieli" + +msgid "Bengali" +msgstr "bengali" + +msgid "Breton" +msgstr "bretoni" + +msgid "Bosnian" +msgstr "bosnia" + +msgid "Catalan" +msgstr "katalaani" + +msgid "Czech" +msgstr "tšekki" + +msgid "Welsh" +msgstr "wales" + +msgid "Danish" +msgstr "tanska" + +msgid "German" +msgstr "saksa" + +msgid "Lower Sorbian" +msgstr "Alasorbi" + +msgid "Greek" +msgstr "kreikka" + +msgid "English" +msgstr "englanti" + +msgid "Australian English" +msgstr "australianenglanti" + +msgid "British English" +msgstr "brittienglanti" + +msgid "Esperanto" +msgstr "esperanto" + +msgid "Spanish" +msgstr "espanja" + +msgid "Argentinian Spanish" +msgstr "Argentiinan espanja" + +msgid "Colombian Spanish" +msgstr "Kolumbian espanja" + +msgid "Mexican Spanish" +msgstr "Meksikon espanja" + +msgid "Nicaraguan Spanish" +msgstr "Nicaraguan espanja" + +msgid "Venezuelan Spanish" +msgstr "Venezuelan espanja" + +msgid "Estonian" +msgstr "viro" + +msgid "Basque" +msgstr "baski" + +msgid "Persian" +msgstr "persia" + +msgid "Finnish" +msgstr "suomi" + +msgid "French" +msgstr "ranska" + +msgid "Frisian" +msgstr "friisi" + +msgid "Irish" +msgstr "irlanti" + +msgid "Scottish Gaelic" +msgstr "skottilainen gaeli" + +msgid "Galician" +msgstr "galicia" + +msgid "Hebrew" +msgstr "heprea" + +msgid "Hindi" +msgstr "hindi" + +msgid "Croatian" +msgstr "kroatia" + +msgid "Upper Sorbian" +msgstr "Yläsorbi" + +msgid "Hungarian" +msgstr "unkari" + +msgid "Armenian" +msgstr "armenian kieli" + +msgid "Interlingua" +msgstr "interlingua" + +msgid "Indonesian" +msgstr "indonesia" + +msgid "Igbo" +msgstr "igbo" + +msgid "Ido" +msgstr "ido" + +msgid "Icelandic" +msgstr "islanti" + +msgid "Italian" +msgstr "italia" + +msgid "Japanese" +msgstr "japani" + +msgid "Georgian" +msgstr "georgia" + +msgid "Kabyle" +msgstr "Kabyle" + +msgid "Kazakh" +msgstr "kazakin kieli" + +msgid "Khmer" +msgstr "khmerin kieli" + +msgid "Kannada" +msgstr "kannada" + +msgid "Korean" +msgstr "korea" + +msgid "Kyrgyz" +msgstr "kirgiisi" + +msgid "Luxembourgish" +msgstr "luxemburgin kieli" + +msgid "Lithuanian" +msgstr "liettua" + +msgid "Latvian" +msgstr "latvia" + +msgid "Macedonian" +msgstr "makedonia" + +msgid "Malayalam" +msgstr "malajalam" + +msgid "Mongolian" +msgstr "mongolia" + +msgid "Marathi" +msgstr "marathi" + +msgid "Malay" +msgstr "malaiji" + +msgid "Burmese" +msgstr "burman kieli" + +msgid "Norwegian Bokmål" +msgstr "norja (bokmål)" + +msgid "Nepali" +msgstr "nepalin kieli" + +msgid "Dutch" +msgstr "hollanti" + +msgid "Norwegian Nynorsk" +msgstr "norja (uusnorja)" + +msgid "Ossetic" +msgstr "osseetin kieli" + +msgid "Punjabi" +msgstr "punjabin kieli" + +msgid "Polish" +msgstr "puola" + +msgid "Portuguese" +msgstr "portugali" + +msgid "Brazilian Portuguese" +msgstr "brasilian portugali" + +msgid "Romanian" +msgstr "romania" + +msgid "Russian" +msgstr "venäjä" + +msgid "Slovak" +msgstr "slovakia" + +msgid "Slovenian" +msgstr "slovenia" + +msgid "Albanian" +msgstr "albaani" + +msgid "Serbian" +msgstr "serbia" + +msgid "Serbian Latin" +msgstr "serbian latina" + +msgid "Swedish" +msgstr "ruotsi" + +msgid "Swahili" +msgstr "swahili" + +msgid "Tamil" +msgstr "tamili" + +msgid "Telugu" +msgstr "telugu" + +msgid "Tajik" +msgstr "tadžikki" + +msgid "Thai" +msgstr "thain kieli" + +msgid "Turkmen" +msgstr "turkmeeni" + +msgid "Turkish" +msgstr "turkki" + +msgid "Tatar" +msgstr "tataarin kieli" + +msgid "Udmurt" +msgstr "udmurtti" + +msgid "Ukrainian" +msgstr "ukraina" + +msgid "Urdu" +msgstr "urdu" + +msgid "Uzbek" +msgstr "uzbekki" + +msgid "Vietnamese" +msgstr "vietnam" + +msgid "Simplified Chinese" +msgstr "kiina (yksinkertaistettu)" + +msgid "Traditional Chinese" +msgstr "kiina (perinteinen)" + +msgid "Messages" +msgstr "Viestit" + +msgid "Site Maps" +msgstr "Sivukartat" + +msgid "Static Files" +msgstr "Staattiset tiedostot" + +msgid "Syndication" +msgstr "Syndikointi" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "..." + +msgid "That page number is not an integer" +msgstr "Annettu sivunumero ei ole kokonaisluku" + +msgid "That page number is less than 1" +msgstr "Annettu sivunumero on alle 1" + +msgid "That page contains no results" +msgstr "Annetulla sivulla ei ole tuloksia" + +msgid "Enter a valid value." +msgstr "Syötä oikea arvo." + +msgid "Enter a valid URL." +msgstr "Syötä oikea URL-osoite." + +msgid "Enter a valid integer." +msgstr "Syötä kelvollinen kokonaisluku." + +msgid "Enter a valid email address." +msgstr "Syötä kelvollinen sähköpostiosoite." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" +"Anna lyhytnimi joka koostuu vain kirjaimista, numeroista sekä ala- ja " +"tavuviivoista." + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"Anna lyhytnimi joka koostuu vain Unicode-kirjaimista, numeroista sekä ala- " +"ja tavuviivoista." + +msgid "Enter a valid IPv4 address." +msgstr "Syötä kelvollinen IPv4-osoite." + +msgid "Enter a valid IPv6 address." +msgstr "Syötä kelvollinen IPv6-osoite." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "Syötä kelvollinen IPv4- tai IPv6-osoite." + +msgid "Enter only digits separated by commas." +msgstr "Vain pilkulla erotetut numeromerkit kelpaavat tässä." + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "Tämän arvon on oltava %(limit_value)s (nyt %(show_value)s)." + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "Tämän arvon on oltava enintään %(limit_value)s." + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "Tämän luvun on oltava vähintään %(limit_value)s." + +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "Varmista, että arvo on askelkoon %(limit_value)smonikerta. " + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Varmista, että tämä arvo on vähintään %(limit_value)d merkin pituinen (tällä " +"hetkellä %(show_value)d)." +msgstr[1] "" +"Varmista, että tämä arvo on vähintään %(limit_value)d merkkiä pitkä (tällä " +"hetkellä %(show_value)d)." + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Varmista, että tämä arvo on enintään %(limit_value)d merkin pituinen (tällä " +"hetkellä %(show_value)d)." +msgstr[1] "" +"Varmista, että tämä arvo on enintään %(limit_value)d merkkiä pitkä (tällä " +"hetkellä %(show_value)d)." + +msgid "Enter a number." +msgstr "Syötä luku." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "Tässä luvussa voi olla yhteensä enintään %(max)s numero." +msgstr[1] "Tässä luvussa voi olla yhteensä enintään %(max)s numeroa." + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "Tässä luvussa saa olla enintään %(max)s desimaali." +msgstr[1] "Tässä luvussa saa olla enintään %(max)s desimaalia." + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +"Tässä luvussa saa olla enintään %(max)s numero ennen desimaalipilkkua." +msgstr[1] "" +"Tässä luvussa saa olla enintään %(max)s numeroa ennen desimaalipilkkua." + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"Pääte \"%(extension)s\" ei ole sallittu. Sallittuja päätteitä ovat " +"\"%(allowed_extensions)s\"." + +msgid "Null characters are not allowed." +msgstr "Tyhjiä merkkejä (null) ei sallita." + +msgid "and" +msgstr "ja" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "%(model_name)s jolla on nämä %(field_labels)s on jo olemassa." + +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "Rajoitetta \"%(name)s\" loukataan." + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "Arvo %(value)r ei kelpaa." + +msgid "This field cannot be null." +msgstr "Tämän kentän arvo ei voi olla \"null\"." + +msgid "This field cannot be blank." +msgstr "Tämä kenttä ei voi olla tyhjä." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "%(model_name)s jolla on tämä %(field_label)s, on jo olemassa." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"\"%(field_label)s\"-kentän on oltava uniikki suhteessa: %(date_field_label)s " +"%(lookup_type)s." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "Kenttä tyyppiä: %(field_type)s" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "%(value)s-arvo pitää olla joko tosi tai epätosi." + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "%(value)s-arvo pitää olla joko tosi, epätosi tai ei mitään." + +msgid "Boolean (Either True or False)" +msgstr "Totuusarvo: joko tosi (True) tai epätosi (False)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "Merkkijono (enintään %(max_length)s merkkiä)" + +msgid "Comma-separated integers" +msgstr "Pilkulla erotetut kokonaisluvut" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" +"%(value)s-arvo on väärässä päivämäärämuodossa. Sen tulee olla VVVV-KK-PP -" +"muodossa." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" +"%(value)s-arvo on oikeassa päivämäärämuodossa (VVVV-KK-PP), muttei ole " +"kelvollinen päivämäärä." + +msgid "Date (without time)" +msgstr "Päivämäärä (ilman kellonaikaa)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"%(value)s-arvon muoto ei kelpaa. Se tulee olla VVVV-KK-PP TT:MM[:ss[.uuuuuu]]" +"[TZ] -muodossa." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"%(value)s-arvon muoto on oikea (VVVV-KK-PP TT:MM[:ss[.uuuuuu]][TZ]), mutta " +"päivämäärä/aika ei ole kelvollinen." + +msgid "Date (with time)" +msgstr "Päivämäärä ja kellonaika" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "%(value)s-arvo tulee olla desimaaliluku." + +msgid "Decimal number" +msgstr "Desimaaliluku" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "%(value)s-arvo pitää olla muodossa [PP] TT:MM[:ss[.uuuuuu]]." + +msgid "Duration" +msgstr "Kesto" + +msgid "Email address" +msgstr "Sähköpostiosoite" + +msgid "File path" +msgstr "Tiedostopolku" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "%(value)s-arvo tulee olla liukuluku." + +msgid "Floating point number" +msgstr "Liukuluku" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "%(value)s-arvo tulee olla kokonaisluku." + +msgid "Integer" +msgstr "Kokonaisluku" + +msgid "Big (8 byte) integer" +msgstr "Suuri (8-tavuinen) kokonaisluku" + +msgid "Small integer" +msgstr "Pieni kokonaisluku" + +msgid "IPv4 address" +msgstr "IPv4-osoite" + +msgid "IP address" +msgstr "IP-osoite" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "%(value)s-arvo tulee olla joko ei mitään, tosi tai epätosi." + +msgid "Boolean (Either True, False or None)" +msgstr "Totuusarvo: joko tosi (True), epätosi (False) tai ei mikään (None)" + +msgid "Positive big integer" +msgstr "Suuri positiivinen kokonaisluku" + +msgid "Positive integer" +msgstr "Positiivinen kokonaisluku" + +msgid "Positive small integer" +msgstr "Pieni positiivinen kokonaisluku" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "Lyhytnimi (enintään %(max_length)s merkkiä)" + +msgid "Text" +msgstr "Tekstiä" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "%(value)s-arvo pitää olla muodossa TT:MM[:ss[.uuuuuu]]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" +"%(value)s-arvo on oikeassa muodossa (TT:MM[:ss[.uuuuuu]]), mutta kellonaika " +"ei kelpaa." + +msgid "Time" +msgstr "Kellonaika" + +msgid "URL" +msgstr "URL-osoite" + +msgid "Raw binary data" +msgstr "Raaka binaaridata" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "%(value)s ei ole kelvollinen UUID." + +msgid "Universally unique identifier" +msgstr "UUID-tunnus" + +msgid "File" +msgstr "Tiedosto" + +msgid "Image" +msgstr "Kuva" + +msgid "A JSON object" +msgstr "JSON-tietue" + +msgid "Value must be valid JSON." +msgstr "Arvon pitää olla kelvollista JSONia." + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "%(model)s-tietuetta %(field)s-kentällä %(value)r ei ole olemassa." + +msgid "Foreign Key (type determined by related field)" +msgstr "Vierasavain (tyyppi määräytyy liittyvän kentän mukaan)" + +msgid "One-to-one relationship" +msgstr "Yksi-yhteen -relaatio" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "%(from)s-%(to)s -suhde" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "%(from)s-%(to)s -suhteet" + +msgid "Many-to-many relationship" +msgstr "Moni-moneen -relaatio" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "Tämä kenttä vaaditaan." + +msgid "Enter a whole number." +msgstr "Syötä kokonaisluku." + +msgid "Enter a valid date." +msgstr "Syötä oikea päivämäärä." + +msgid "Enter a valid time." +msgstr "Syötä oikea kellonaika." + +msgid "Enter a valid date/time." +msgstr "Syötä oikea pvm/kellonaika." + +msgid "Enter a valid duration." +msgstr "Syötä oikea kesto." + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "Päivien määrä täytyy olla välillä {min_days} ja {max_days}." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "Tiedostoa ei lähetetty. Tarkista lomakkeen koodaus (encoding)." + +msgid "No file was submitted." +msgstr "Yhtään tiedostoa ei ole lähetetty." + +msgid "The submitted file is empty." +msgstr "Lähetetty tiedosto on tyhjä." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"Varmista, että tämä tiedostonimi on enintään %(max)d merkin pituinen (tällä " +"hetkellä %(length)d)." +msgstr[1] "" +"Varmista, että tämä tiedostonimi on enintään %(max)d merkkiä pitkä (tällä " +"hetkellä %(length)d)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "Voit joko lähettää tai poistaa tiedoston, muttei kumpaakin samalla." + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Kuva ei kelpaa. Lähettämäsi tiedosto ei ole kuva, tai tiedosto on vioittunut." + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "Valitse oikea vaihtoehto. %(value)s ei ole vaihtoehtojen joukossa." + +msgid "Enter a list of values." +msgstr "Syötä lista." + +msgid "Enter a complete value." +msgstr "Syötä kokonainen arvo." + +msgid "Enter a valid UUID." +msgstr "Syötä oikea UUID." + +msgid "Enter a valid JSON." +msgstr "Syötä oikea JSON-arvo." + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(Piilokenttä %(name)s) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" +"ManagementForm-tiedot puuttuvat tai niitä on muutettu. Puuttuvat kentät ovat " +"%(field_names)s. Jos ongelma toistuu, voi olla että joudut raportoimaan " +"tämän bugina." + +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "Lähetä enintään%(num)d lomake." +msgstr[1] "Lähetä enintään %(num)d lomaketta." + +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "Lähetä vähintään %(num)d lomake." +msgstr[1] "Lähetä vähintään %(num)d lomaketta. " + +msgid "Order" +msgstr "Järjestys" + +msgid "Delete" +msgstr "Poista" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "Korjaa kaksoisarvo kentälle %(field)s." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "Ole hyvä ja korjaa uniikin kentän %(field)s kaksoisarvo." + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." + +msgid "Please correct the duplicate values below." +msgstr "Korjaa alla olevat kaksoisarvot." + +msgid "The inline value did not match the parent instance." +msgstr "Liittyvä arvo ei vastannut vanhempaa instanssia." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "Valitse oikea vaihtoehto. Valintasi ei löydy vaihtoehtojen joukosta." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "\"%(pk)s\" ei ole kelvollinen arvo." + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"%(datetime)s -arvoa ei pystytty lukemaan aikavyöhykkeellä " +"%(current_timezone)s; se saattaa olla moniarvoinen tai määrittämätön." + +msgid "Clear" +msgstr "Poista" + +msgid "Currently" +msgstr "Tällä hetkellä" + +msgid "Change" +msgstr "Muokkaa" + +msgid "Unknown" +msgstr "Tuntematon" + +msgid "Yes" +msgstr "Kyllä" + +msgid "No" +msgstr "Ei" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "kyllä,ei,ehkä" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d tavu" +msgstr[1] "%(size)d tavua" + +#, python-format +msgid "%s KB" +msgstr "%s KB" + +#, python-format +msgid "%s MB" +msgstr "%s MB" + +#, python-format +msgid "%s GB" +msgstr "%s GB" + +#, python-format +msgid "%s TB" +msgstr "%s TB" + +#, python-format +msgid "%s PB" +msgstr "%s PB" + +msgid "p.m." +msgstr "ip" + +msgid "a.m." +msgstr "ap" + +msgid "PM" +msgstr "IP" + +msgid "AM" +msgstr "AP" + +msgid "midnight" +msgstr "keskiyö" + +msgid "noon" +msgstr "keskipäivä" + +msgid "Monday" +msgstr "maanantai" + +msgid "Tuesday" +msgstr "tiistai" + +msgid "Wednesday" +msgstr "keskiviikko" + +msgid "Thursday" +msgstr "torstai" + +msgid "Friday" +msgstr "perjantai" + +msgid "Saturday" +msgstr "lauantai" + +msgid "Sunday" +msgstr "sunnuntai" + +msgid "Mon" +msgstr "ma" + +msgid "Tue" +msgstr "ti" + +msgid "Wed" +msgstr "ke" + +msgid "Thu" +msgstr "to" + +msgid "Fri" +msgstr "pe" + +msgid "Sat" +msgstr "la" + +msgid "Sun" +msgstr "su" + +msgid "January" +msgstr "tammikuu" + +msgid "February" +msgstr "helmikuu" + +msgid "March" +msgstr "maaliskuu" + +msgid "April" +msgstr "huhtikuu" + +msgid "May" +msgstr "toukokuu" + +msgid "June" +msgstr "kesäkuu" + +msgid "July" +msgstr "heinäkuu" + +msgid "August" +msgstr "elokuu" + +msgid "September" +msgstr "syyskuu" + +msgid "October" +msgstr "lokakuu" + +msgid "November" +msgstr "marraskuu" + +msgid "December" +msgstr "joulukuu" + +msgid "jan" +msgstr "tam" + +msgid "feb" +msgstr "hel" + +msgid "mar" +msgstr "maa" + +msgid "apr" +msgstr "huh" + +msgid "may" +msgstr "tou" + +msgid "jun" +msgstr "kes" + +msgid "jul" +msgstr "hei" + +msgid "aug" +msgstr "elo" + +msgid "sep" +msgstr "syy" + +msgid "oct" +msgstr "lok" + +msgid "nov" +msgstr "mar" + +msgid "dec" +msgstr "jou" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "tammi" + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "helmi" + +msgctxt "abbrev. month" +msgid "March" +msgstr "maalis" + +msgctxt "abbrev. month" +msgid "April" +msgstr "huhti" + +msgctxt "abbrev. month" +msgid "May" +msgstr "touko" + +msgctxt "abbrev. month" +msgid "June" +msgstr "kesä" + +msgctxt "abbrev. month" +msgid "July" +msgstr "heinä" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "elo" + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "syys" + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "loka" + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "marras" + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "joulu" + +msgctxt "alt. month" +msgid "January" +msgstr "tammikuuta" + +msgctxt "alt. month" +msgid "February" +msgstr "helmikuuta" + +msgctxt "alt. month" +msgid "March" +msgstr "maaliskuuta" + +msgctxt "alt. month" +msgid "April" +msgstr "huhtikuuta" + +msgctxt "alt. month" +msgid "May" +msgstr "toukokuuta" + +msgctxt "alt. month" +msgid "June" +msgstr "kesäkuuta" + +msgctxt "alt. month" +msgid "July" +msgstr "heinäkuuta" + +msgctxt "alt. month" +msgid "August" +msgstr "elokuuta" + +msgctxt "alt. month" +msgid "September" +msgstr "syyskuuta" + +msgctxt "alt. month" +msgid "October" +msgstr "lokakuuta" + +msgctxt "alt. month" +msgid "November" +msgstr "marraskuuta" + +msgctxt "alt. month" +msgid "December" +msgstr "joulukuuta" + +msgid "This is not a valid IPv6 address." +msgstr "Tämä ei ole kelvollinen IPv6-osoite." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "tai" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "%(num)d vuosi" +msgstr[1] "%(num)d vuotta" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "%(num)d kuukausi" +msgstr[1] "%(num)d kuukautta " + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "%(num)d viikko" +msgstr[1] "%(num)d viikkoa" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "%(num)d päivä" +msgstr[1] "%(num)d päivää" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "%(num)d tunti" +msgstr[1] "%(num)d tuntia" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "%(num)d minuutti" +msgstr[1] "%(num)d minuuttia" + +msgid "Forbidden" +msgstr "Kielletty" + +msgid "CSRF verification failed. Request aborted." +msgstr "CSRF-vahvistus epäonnistui. Pyyntö hylätty." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" +"Näet tämän viestin, koska tämä HTTPS-sivusto vaatii selaintasi lähettämään " +"Referer-otsakkeen, mutta sitä ei vastaanotettu. Otsake vaaditaan " +"turvallisuussyistä, varmistamaan etteivät kolmannet osapuolet ole ottaneet " +"selaintasi haltuun." + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"Jos olet konfiguroinut selaimesi olemaan lähettämättä Referer-otsaketta, ole " +"hyvä ja kytke otsake takaisin päälle ainakin tälle sivulle, HTTPS-" +"yhteyksille tai saman lähteen (\"same-origin\") pyynnöille." + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"Jos käytät -tagia tai " +"\"Referrer-Policy: no-referrer\" -otsaketta, ole hyvä ja poista ne. CSRF-" +"suojaus vaatii Referer-otsakkeen tehdäkseen tarkan referer-tarkistuksen. Jos " +"vaadit yksityisyyttä, käytä vaihtoehtoja kuten linkittääksesi kolmannen osapuolen sivuille." + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"Näet tämän viestin, koska tämä sivusto vaatii CSRF-evästeen " +"vastaanottaessaan lomaketietoja. Eväste vaaditaan turvallisuussyistä, " +"varmistamaan etteivät kolmannet osapuolet ole ottaneet selaintasi haltuun." + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" +"Jos olet konfiguroinut selaimesi olemaan vastaanottamatta tai lähettämättä " +"evästeitä, ole hyvä ja kytke evästeet takaisin päälle ainakin tälle sivulle " +"tai saman lähteen (\"same-origin\") pyynnöille." + +msgid "More information is available with DEBUG=True." +msgstr "Lisätietoja `DEBUG=True`-konfiguraatioasetuksella." + +msgid "No year specified" +msgstr "Vuosi puuttuu" + +msgid "Date out of range" +msgstr "Päivämäärä ei alueella" + +msgid "No month specified" +msgstr "Kuukausi puuttuu" + +msgid "No day specified" +msgstr "Päivä puuttuu" + +msgid "No week specified" +msgstr "Viikko puuttuu" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "%(verbose_name_plural)s: yhtään kohdetta ei löydy" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"%(verbose_name_plural)s: tulevia kohteita ei löydy, koska %(class_name)s." +"allow_future:n arvo on False." + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "Päivämäärä '%(datestr)s' ei ole muotoa '%(format)s'" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "Hakua vastaavaa %(verbose_name)s -kohdetta ei löytynyt" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "Sivunumero ei ole 'last' (viimeinen) eikä näytä luvulta." + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Epäkelpo sivu (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "Lista on tyhjä, ja '%(class_name)s.allow_empty':n arvo on False." + +msgid "Directory indexes are not allowed here." +msgstr "Hakemistolistauksia ei sallita täällä." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "\"%(path)s\" ei ole olemassa" + +#, python-format +msgid "Index of %(directory)s" +msgstr "Hakemistolistaus: %(directory)s" + +msgid "The install worked successfully! Congratulations!" +msgstr "Asennus toimi! Onneksi olkoon!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"Katso Djangon version %(version)s julkaisutiedot" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" +"Näet tämän viestin, koska asetuksissasi on DEBUG = True etkä ole konfiguroinut yhtään URL-osoitetta." + +msgid "Django Documentation" +msgstr "Django-dokumentaatio" + +msgid "Topics, references, & how-to’s" +msgstr "Aiheet, viittaukset & how-tot" + +msgid "Tutorial: A Polling App" +msgstr "Tutoriaali: kyselyapplikaatio" + +msgid "Get started with Django" +msgstr "Miten päästä alkuun Djangolla" + +msgid "Django Community" +msgstr "Django-yhteisö" + +msgid "Connect, get help, or contribute" +msgstr "Verkostoidu, saa apua tai jatkokehitä" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/gd/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/gd/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..8706bf16 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/gd/LC_MESSAGES/django 3.po @@ -0,0 +1,1386 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Michael Bauer, 2014 +# GunChleoc, 2015-2017,2021 +# GunChleoc, 2015 +# GunChleoc, 2014-2015 +# Michael Bauer, 2014 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-09-21 10:22+0200\n" +"PO-Revision-Date: 2021-11-20 14:00+0000\n" +"Last-Translator: GunChleoc\n" +"Language-Team: Gaelic, Scottish (http://www.transifex.com/django/django/" +"language/gd/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: gd\n" +"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : " +"(n > 2 && n < 20) ? 2 : 3;\n" + +msgid "Afrikaans" +msgstr "Afraganais" + +msgid "Arabic" +msgstr "Arabais" + +msgid "Algerian Arabic" +msgstr "Arabais Aildireach" + +msgid "Asturian" +msgstr "Astùrais" + +msgid "Azerbaijani" +msgstr "Asarbaideànais" + +msgid "Bulgarian" +msgstr "Bulgarais" + +msgid "Belarusian" +msgstr "Bealaruisis" + +msgid "Bengali" +msgstr "Beangailis" + +msgid "Breton" +msgstr "Breatnais" + +msgid "Bosnian" +msgstr "Bosnais" + +msgid "Catalan" +msgstr "Catalanais" + +msgid "Czech" +msgstr "Seacais" + +msgid "Welsh" +msgstr "Cuimris" + +msgid "Danish" +msgstr "Danmhairgis" + +msgid "German" +msgstr "Gearmailtis" + +msgid "Lower Sorbian" +msgstr "Sòrbais Ìochdarach" + +msgid "Greek" +msgstr "Greugais" + +msgid "English" +msgstr "Beurla" + +msgid "Australian English" +msgstr "Beurla Astràilia" + +msgid "British English" +msgstr "Beurla Bhreatainn" + +msgid "Esperanto" +msgstr "Esperanto" + +msgid "Spanish" +msgstr "Spàinntis" + +msgid "Argentinian Spanish" +msgstr "Spàinntis na h-Argantaine" + +msgid "Colombian Spanish" +msgstr "Spàinntis Choloimbia" + +msgid "Mexican Spanish" +msgstr "Spàinntis Mheagsagach" + +msgid "Nicaraguan Spanish" +msgstr "Spàinntis Niocaragua" + +msgid "Venezuelan Spanish" +msgstr "Spàinntis na Bheiniseala" + +msgid "Estonian" +msgstr "Eastoinis" + +msgid "Basque" +msgstr "Basgais" + +msgid "Persian" +msgstr "Farsaidh" + +msgid "Finnish" +msgstr "Fionnlannais" + +msgid "French" +msgstr "Fraingis" + +msgid "Frisian" +msgstr "Frìsis" + +msgid "Irish" +msgstr "Gaeilge" + +msgid "Scottish Gaelic" +msgstr "Gàidhlig" + +msgid "Galician" +msgstr "Gailìsis" + +msgid "Hebrew" +msgstr "Eabhra" + +msgid "Hindi" +msgstr "Hindis" + +msgid "Croatian" +msgstr "Cròthaisis" + +msgid "Upper Sorbian" +msgstr "Sòrbais Uachdarach" + +msgid "Hungarian" +msgstr "Ungairis" + +msgid "Armenian" +msgstr "Airmeinis" + +msgid "Interlingua" +msgstr "Interlingua" + +msgid "Indonesian" +msgstr "Innd-Innsis" + +msgid "Igbo" +msgstr "Igbo" + +msgid "Ido" +msgstr "Ido" + +msgid "Icelandic" +msgstr "Innis Tìlis" + +msgid "Italian" +msgstr "Eadailtis" + +msgid "Japanese" +msgstr "Seapanais" + +msgid "Georgian" +msgstr "Cairtbheilis" + +msgid "Kabyle" +msgstr "Kabyle" + +msgid "Kazakh" +msgstr "Casachais" + +msgid "Khmer" +msgstr "Cmèar" + +msgid "Kannada" +msgstr "Kannada" + +msgid "Korean" +msgstr "Coirèanais" + +msgid "Kyrgyz" +msgstr "Cìorgasais" + +msgid "Luxembourgish" +msgstr "Lugsamburgais" + +msgid "Lithuanian" +msgstr "Liotuainis" + +msgid "Latvian" +msgstr "Laitbheis" + +msgid "Macedonian" +msgstr "Masadonais" + +msgid "Malayalam" +msgstr "Malayalam" + +msgid "Mongolian" +msgstr "Mongolais" + +msgid "Marathi" +msgstr "Marathi" + +msgid "Malay" +msgstr "Malaidhis" + +msgid "Burmese" +msgstr "Burmais" + +msgid "Norwegian Bokmål" +msgstr "Nirribhis (Bokmål)" + +msgid "Nepali" +msgstr "Neapàlais" + +msgid "Dutch" +msgstr "Duitsis" + +msgid "Norwegian Nynorsk" +msgstr "Nirribhis (Nynorsk)" + +msgid "Ossetic" +msgstr "Ossetic" + +msgid "Punjabi" +msgstr "Panjabi" + +msgid "Polish" +msgstr "Pòlainnis" + +msgid "Portuguese" +msgstr "Portagailis" + +msgid "Brazilian Portuguese" +msgstr "Portagailis Bhraisileach" + +msgid "Romanian" +msgstr "Romàinis" + +msgid "Russian" +msgstr "Ruisis" + +msgid "Slovak" +msgstr "Slòbhacais" + +msgid "Slovenian" +msgstr "Slòbhainis" + +msgid "Albanian" +msgstr "Albàinis" + +msgid "Serbian" +msgstr "Sèirbis" + +msgid "Serbian Latin" +msgstr "Sèirbis (Laideann)" + +msgid "Swedish" +msgstr "Suainis" + +msgid "Swahili" +msgstr "Kiswahili" + +msgid "Tamil" +msgstr "Taimilis" + +msgid "Telugu" +msgstr "Telugu" + +msgid "Tajik" +msgstr "Taidigis" + +msgid "Thai" +msgstr "Tàidh" + +msgid "Turkmen" +msgstr "Turcmanais" + +msgid "Turkish" +msgstr "Turcais" + +msgid "Tatar" +msgstr "Tatarais" + +msgid "Udmurt" +msgstr "Udmurt" + +msgid "Ukrainian" +msgstr "Ucràinis" + +msgid "Urdu" +msgstr "Ùrdu" + +msgid "Uzbek" +msgstr "Usbagais" + +msgid "Vietnamese" +msgstr "Bhiet-Namais" + +msgid "Simplified Chinese" +msgstr "Sìnis Shimplichte" + +msgid "Traditional Chinese" +msgstr "Sìnis Thradaiseanta" + +msgid "Messages" +msgstr "Teachdaireachdan" + +msgid "Site Maps" +msgstr "Mapaichean-làraich" + +msgid "Static Files" +msgstr "Faidhlichean stadastaireachd" + +msgid "Syndication" +msgstr "Siondacaideadh" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "…" + +msgid "That page number is not an integer" +msgstr "Chan eil àireamh na duilleige seo 'na àireamh slàn" + +msgid "That page number is less than 1" +msgstr "Tha àireamh na duilleige seo nas lugha na 1" + +msgid "That page contains no results" +msgstr "Chan eil toradh aig an duilleag seo" + +msgid "Enter a valid value." +msgstr "Cuir a-steach luach dligheach." + +msgid "Enter a valid URL." +msgstr "Cuir a-steach URL dligheach." + +msgid "Enter a valid integer." +msgstr "Cuir a-steach àireamh slàin dhligheach." + +msgid "Enter a valid email address." +msgstr "Cuir a-steach seòladh puist-d dligheach." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" +"Cuir a-steach “sluga” dligheach anns nach eil ach litrichean, àireamhan, fo-" +"loidhnichean is tàthanan." + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"Cuir a-steach “sluga” dligheach anns nach eil ach litrichean Unicode, " +"àireamhan, fo-loidhnichean is tàthanan." + +msgid "Enter a valid IPv4 address." +msgstr "Cuir a-steach seòladh IPv4 dligheach." + +msgid "Enter a valid IPv6 address." +msgstr "Cuir a-steach seòladh IPv6 dligheach." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "Cuir a-steach seòladh IPv4 no IPv6 dligheach." + +msgid "Enter only digits separated by commas." +msgstr "Na cuir a-steach ach àireamhan ’gan sgaradh le cromagan." + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" +"Dèan cinnteach gu bheil an luach seo %(limit_value)s (’s e %(show_value)s a " +"th’ ann)." + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "" +"Dèan cinnteach gu bheil an luach seo nas lugha na no co-ionnan ri " +"%(limit_value)s." + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "" +"Dèan cinnteach gu bheil an luach seo nas motha na no co-ionnan ri " +"%(limit_value)s." + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Dèan cinnteach gu bheil %(limit_value)d charactar aig an luach seo air a’ " +"char as lugha (tha %(show_value)d aige an-dràsta)." +msgstr[1] "" +"Dèan cinnteach gu bheil %(limit_value)d charactar aig an luach seo air a’ " +"char as lugha (tha %(show_value)d aige an-dràsta)." +msgstr[2] "" +"Dèan cinnteach gu bheil %(limit_value)d caractaran aig an luach seo air a’ " +"char as lugha (tha %(show_value)d aige an-dràsta)." +msgstr[3] "" +"Dèan cinnteach gu bheil %(limit_value)d caractar aig an luach seo air a’ " +"char as lugha (tha %(show_value)d aige an-dràsta)." + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Dèan cinnteach gu bheil %(limit_value)d charactar aig an luach seo air a’ " +"char as motha (tha %(show_value)d aige an-dràsta)." +msgstr[1] "" +"Dèan cinnteach gu bheil %(limit_value)d charactar aig an luach seo air a’ " +"char as motha (tha %(show_value)d aige an-dràsta)." +msgstr[2] "" +"Dèan cinnteach gu bheil %(limit_value)d caractaran aig an luach seo air a’ " +"char as motha (tha %(show_value)d aige an-dràsta)." +msgstr[3] "" +"Dèan cinnteach gu bheil %(limit_value)d caractar aig an luach seo air a’ " +"char as motha (tha %(show_value)d aige an-dràsta)." + +msgid "Enter a number." +msgstr "Cuir a-steach àireamh." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "" +"Dèan cinnteach nach eil barrachd air %(max)s àireamh ann gu h-iomlan." +msgstr[1] "" +"Dèan cinnteach nach eil barrachd air %(max)s àireamh ann gu h-iomlan." +msgstr[2] "" +"Dèan cinnteach nach eil barrachd air %(max)s àireamhan ann gu h-iomlan." +msgstr[3] "" +"Dèan cinnteach nach eil barrachd air %(max)s àireamh ann gu h-iomlan." + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "Dèan cinnteach nach eil barrachd air %(max)s ionad deicheach ann." +msgstr[1] "Dèan cinnteach nach eil barrachd air %(max)s ionad deicheach ann." +msgstr[2] "Dèan cinnteach nach eil barrachd air %(max)s ionadan deicheach ann." +msgstr[3] "Dèan cinnteach nach eil barrachd air %(max)s ionad deicheach ann." + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +"Dèan cinnteach nach eil barrachd air %(max)s àireamh ann ron phuing " +"dheicheach." +msgstr[1] "" +"Dèan cinnteach nach eil barrachd air %(max)s àireamh ann ron phuing " +"dheicheach." +msgstr[2] "" +"Dèan cinnteach nach eil barrachd air %(max)s àireamhan ann ron phuing " +"dheicheach." +msgstr[3] "" +"Dèan cinnteach nach eil barrachd air %(max)s àireamh ann ron phuing " +"dheicheach." + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"Chan eil an leudachan faidhle “%(extension)s” ceadaichte. Seo na leudachain " +"a tha ceadaichte: %(allowed_extensions)s." + +msgid "Null characters are not allowed." +msgstr "Chan eil caractaran null ceadaichte." + +msgid "and" +msgstr "agus" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "Tha %(model_name)s lis a’ %(field_labels)s seo ann mar-thà." + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "Chan eil an luach %(value)r ’na roghainn dhligheach." + +msgid "This field cannot be null." +msgstr "Chan fhaod an raon seo a bhith ’na neoni." + +msgid "This field cannot be blank." +msgstr "Chan fhaod an raon seo a bhith bàn." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "Tha %(model_name)s leis a’ %(field_label)s seo ann mar-thà." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. +#. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"Chan fhaod %(field_label)s a bhith ann ach aon turas airson " +"%(date_field_label)s %(lookup_type)s." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "Raon dhen t-seòrsa: %(field_type)s" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "Feumaidh “%(value)s” a bhith True no False." + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "Feumaidh “%(value)s” a bhith True, False no None." + +msgid "Boolean (Either True or False)" +msgstr "Booleach (True no False)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "Sreang (suas ri %(max_length)s)" + +msgid "Comma-separated integers" +msgstr "Àireamhan slàna sgaraichte le cromagan" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" +"Tha fòrmat cinn-là mì-dhligheach aig an luach “%(value)s”. Feumaidh e bhith " +"san fhòrmat BBBB-MM-LL." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" +"Tha fòrmat mar bu chòir (BBBB-MM-LL) aig an luach “%(value)s” ach tha an " +"ceann-là mì-dligheach." + +msgid "Date (without time)" +msgstr "Ceann-là (gun àm)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"Tha fòrmat mì-dhligheach aig an luach “%(value)s”. Feumaidh e bhith san " +"fhòrmat BBBB-MM-LL HH:MM[:dd[.uuuuuu]][TZ]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"Tha fòrmat mar bu chòir (BBBB-MM-LL HH:MM[:dd[.uuuuuu]][TZ]) aig an luach " +"“%(value)s” ach tha an ceann-là/an t-àm mì-dligheach." + +msgid "Date (with time)" +msgstr "Ceann-là (le àm)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "Feumaidh “%(value)s” a bhith ’na àireamh dheicheach." + +msgid "Decimal number" +msgstr "Àireamh dheicheach" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" +"Tha fòrmat mì-dhligheach aig an luach “%(value)s”. Feumaidh e bhith san " +"fhòrmat [DD] [[HH:]MM:]ss[.uuuuuu]." + +msgid "Duration" +msgstr "Faid" + +msgid "Email address" +msgstr "Seòladh puist-d" + +msgid "File path" +msgstr "Slighe an fhaidhle" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "Feumaidh “%(value)s” a bhith ’na àireamh floda." + +msgid "Floating point number" +msgstr "Àireamh le puing floda" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "Feumaidh “%(value)s” a bhith ’na àireamh shlàn." + +msgid "Integer" +msgstr "Àireamh shlàn" + +msgid "Big (8 byte) integer" +msgstr "Mòr-àireamh shlàn (8 baidht)" + +msgid "Small integer" +msgstr "Beag-àireamh slàn" + +msgid "IPv4 address" +msgstr "Seòladh IPv4" + +msgid "IP address" +msgstr "Seòladh IP" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "Feumaidh “%(value)s” a bhith None, True no False." + +msgid "Boolean (Either True, False or None)" +msgstr "Booleach (True, False no None)" + +msgid "Positive big integer" +msgstr "Àireamh shlàn dhearbh" + +msgid "Positive integer" +msgstr "Àireamh shlàn dhearbh" + +msgid "Positive small integer" +msgstr "Beag-àireamh shlàn dhearbh" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "Sluga (suas ri %(max_length)s)" + +msgid "Text" +msgstr "Teacsa" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" +"Tha fòrmat mì-dhligheach aig an luach “%(value)s”. Feumaidh e bhith san " +"fhòrmat HH:MM[:dd[.uuuuuu]]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" +"Tha fòrmat mar bu chòir (HH:MM[:dd[.uuuuuu]]) aig an luach “%(value)s” ach " +"tha an t-àm mì-dligheach." + +msgid "Time" +msgstr "Àm" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "Dàta bìnearaidh amh" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "Chan eil “%(value)s” ’na UUID dligheach." + +msgid "Universally unique identifier" +msgstr "Aithnichear àraidh gu h-uile-choitcheann" + +msgid "File" +msgstr "Faidhle" + +msgid "Image" +msgstr "Dealbh" + +msgid "A JSON object" +msgstr "Oibseact JSON" + +msgid "Value must be valid JSON." +msgstr "Feumaidh an luach a bhith ’na JSON dligheach." + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "Chan eil ionstans dhe %(model)s le %(field)s %(value)r ann." + +msgid "Foreign Key (type determined by related field)" +msgstr "Iuchair chèin (thèid a sheòrsa a mhìneachadh leis an raon dàimheach)" + +msgid "One-to-one relationship" +msgstr "Dàimh aonan gu aonan" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "Dàimh %(from)s-%(to)s" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "Dàimhean %(from)s-%(to)s" + +msgid "Many-to-many relationship" +msgstr "Dàimh iomadh rud gu iomadh rud" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "Tha an raon seo riatanach." + +msgid "Enter a whole number." +msgstr "Cuir a-steach àireamh shlàn." + +msgid "Enter a valid date." +msgstr "Cuir a-steach ceann-là dligheach." + +msgid "Enter a valid time." +msgstr "Cuir a-steach àm dligheach." + +msgid "Enter a valid date/time." +msgstr "Cuir a-steach ceann-là ’s àm dligheach." + +msgid "Enter a valid duration." +msgstr "Cuir a-steach faid dhligheach." + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "" +"Feumaidh an àireamh de làithean a bhith eadar {min_days} is {max_days}." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" +"Cha deach faidhle a chur a-null. Dearbhaich seòrsa a’ chòdachaidh air an " +"fhoirm." + +msgid "No file was submitted." +msgstr "Cha deach faidhle a chur a-null." + +msgid "The submitted file is empty." +msgstr "Tha am faidhle a chaidh a chur a-null falamh." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"Dèan cinnteach nach eil barrachd air %(max)d charactar ann an ainm an " +"fhaidhle (tha %(length)d aige)." +msgstr[1] "" +"Dèan cinnteach nach eil barrachd air %(max)d charactar ann an ainm an " +"fhaidhle (tha %(length)d aige)." +msgstr[2] "" +"Dèan cinnteach nach eil barrachd air %(max)d caractaran ann an ainm an " +"fhaidhle (tha %(length)d aige)." +msgstr[3] "" +"Dèan cinnteach nach eil barrachd air %(max)d caractar ann an ainm an " +"fhaidhle (tha %(length)d aige)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" +"Cuir a-null faidhle no cuir cromag sa bhogsa fhalamh, na dèan an dà chuidh " +"dhiubh." + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Luchdaich suas dealbh dligheach. Cha robh am faidhle a luchdaich thu suas " +"’na dhealbh no bha an dealbh coirbte." + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "Tagh rud dligheach. Chan eil %(value)s ’na roghainn dhut." + +msgid "Enter a list of values." +msgstr "Cuir a-steach liosta de luachan." + +msgid "Enter a complete value." +msgstr "Cuir a-steach luach slàn." + +msgid "Enter a valid UUID." +msgstr "Cuir a-steach UUID dligheach." + +msgid "Enter a valid JSON." +msgstr "Cuir a-steach JSON dligheach." + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(Raon falaichte %(name)s) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" +"Tha dàta an fhoirm stiùiridh a dhìth no chaidh beantainn ris. Seo na " +"raointean a tha a dhìth: %(field_names)s. Ma mhaireas an duilgheadas, saoil " +"an cuir thu aithris air buga thugainn?" + +#, python-format +msgid "Please submit at most %d form." +msgid_plural "Please submit at most %d forms." +msgstr[0] "Na cuir a-null barrachd air %d fhoirm." +msgstr[1] "Na cuir a-null barrachd air %d fhoirm." +msgstr[2] "Na cuir a-null barrachd air %d foirmean." +msgstr[3] "Na cuir a-null barrachd air %d foirm." + +#, python-format +msgid "Please submit at least %d form." +msgid_plural "Please submit at least %d forms." +msgstr[0] "Cuir a-null %d fhoirm air a char as lugha." +msgstr[1] "Cuir a-null %d fhoirm air a char as lugha." +msgstr[2] "Cuir a-null %d foirmichean air a char as lugha." +msgstr[3] "Cuir a-null %d foirm air a char as lugha." + +msgid "Order" +msgstr "Òrdugh" + +msgid "Delete" +msgstr "Sguab às" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "Ceartaich an dàta dùblaichte airson %(field)s." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" +"Ceartaich an dàta dùblaichte airson %(field)s, chan fhaod gach nì a bhith " +"ann ach aon turas." + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"Ceartaich an dàta dùblaichte airson %(field_name)s nach fhaod a bhith ann " +"ach aon turas airson %(lookup)s ann an %(date_field)s." + +msgid "Please correct the duplicate values below." +msgstr "Ceartaich na luachan dùblaichte gu h-ìosal." + +msgid "The inline value did not match the parent instance." +msgstr "" +"Chan eil an luach am broinn na loidhne a’ freagairt ris an ionstans-pàraint." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "Tagh rud dligheach. Chan eil an rud seo ’na roghainn dhut." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "Chan e luach dligheach a tha ann an “%(pk)s”." + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"Cha chiall dha %(datetime)s san roinn-tìde %(current_timezone)s; dh’fhaoidte " +"gu bheil e dà-sheaghach no nach eil e ann." + +msgid "Clear" +msgstr "Falamhaich" + +msgid "Currently" +msgstr "An-dràsta" + +msgid "Change" +msgstr "Atharraich" + +msgid "Unknown" +msgstr "Chan eil fhios" + +msgid "Yes" +msgstr "Tha" + +msgid "No" +msgstr "Chan eil" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "yes,no,maybe" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d baidht" +msgstr[1] "%(size)d baidht" +msgstr[2] "%(size)d baidht" +msgstr[3] "%(size)d baidht" + +#, python-format +msgid "%s KB" +msgstr "%s KB" + +#, python-format +msgid "%s MB" +msgstr "%s MB" + +#, python-format +msgid "%s GB" +msgstr "%s GB" + +#, python-format +msgid "%s TB" +msgstr "%s TB" + +#, python-format +msgid "%s PB" +msgstr "%s PB" + +msgid "p.m." +msgstr "f" + +msgid "a.m." +msgstr "m" + +msgid "PM" +msgstr "f" + +msgid "AM" +msgstr "m" + +msgid "midnight" +msgstr "meadhan-oidhche" + +msgid "noon" +msgstr "meadhan-latha" + +msgid "Monday" +msgstr "DiLuain" + +msgid "Tuesday" +msgstr "DiMàirt" + +msgid "Wednesday" +msgstr "DiCiadain" + +msgid "Thursday" +msgstr "DiarDaoin" + +msgid "Friday" +msgstr "DihAoine" + +msgid "Saturday" +msgstr "DiSathairne" + +msgid "Sunday" +msgstr "DiDòmhnaich" + +msgid "Mon" +msgstr "DiL" + +msgid "Tue" +msgstr "DiM" + +msgid "Wed" +msgstr "DiC" + +msgid "Thu" +msgstr "Dia" + +msgid "Fri" +msgstr "Dih" + +msgid "Sat" +msgstr "DiS" + +msgid "Sun" +msgstr "DiD" + +msgid "January" +msgstr "Am Faoilleach" + +msgid "February" +msgstr "An Gearran" + +msgid "March" +msgstr "Am Màrt" + +msgid "April" +msgstr "An Giblean" + +msgid "May" +msgstr "An Cèitean" + +msgid "June" +msgstr "An t-Ògmhios" + +msgid "July" +msgstr "An t-Iuchar" + +msgid "August" +msgstr "An Lùnastal" + +msgid "September" +msgstr "An t-Sultain" + +msgid "October" +msgstr "An Dàmhair" + +msgid "November" +msgstr "An t-Samhain" + +msgid "December" +msgstr "An Dùbhlachd" + +msgid "jan" +msgstr "faoi" + +msgid "feb" +msgstr "gearr" + +msgid "mar" +msgstr "màrt" + +msgid "apr" +msgstr "gibl" + +msgid "may" +msgstr "cèit" + +msgid "jun" +msgstr "ògmh" + +msgid "jul" +msgstr "iuch" + +msgid "aug" +msgstr "lùna" + +msgid "sep" +msgstr "sult" + +msgid "oct" +msgstr "dàmh" + +msgid "nov" +msgstr "samh" + +msgid "dec" +msgstr "dùbh" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "Faoi" + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "Gearr" + +msgctxt "abbrev. month" +msgid "March" +msgstr "Màrt" + +msgctxt "abbrev. month" +msgid "April" +msgstr "Gibl" + +msgctxt "abbrev. month" +msgid "May" +msgstr "Cèit" + +msgctxt "abbrev. month" +msgid "June" +msgstr "Ògmh" + +msgctxt "abbrev. month" +msgid "July" +msgstr "Iuch" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "Lùna" + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "Sult" + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "Dàmh" + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "Samh" + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "Dùbh" + +msgctxt "alt. month" +msgid "January" +msgstr "Am Faoilleach" + +msgctxt "alt. month" +msgid "February" +msgstr "An Gearran" + +msgctxt "alt. month" +msgid "March" +msgstr "Am Màrt" + +msgctxt "alt. month" +msgid "April" +msgstr "An Giblean" + +msgctxt "alt. month" +msgid "May" +msgstr "An Cèitean" + +msgctxt "alt. month" +msgid "June" +msgstr "An t-Ògmhios" + +msgctxt "alt. month" +msgid "July" +msgstr "An t-Iuchar" + +msgctxt "alt. month" +msgid "August" +msgstr "An Lùnastal" + +msgctxt "alt. month" +msgid "September" +msgstr "An t-Sultain" + +msgctxt "alt. month" +msgid "October" +msgstr "An Dàmhair" + +msgctxt "alt. month" +msgid "November" +msgstr "An t-Samhain" + +msgctxt "alt. month" +msgid "December" +msgstr "An Dùbhlachd" + +msgid "This is not a valid IPv6 address." +msgstr "Chan eil seo ’na sheòladh IPv6 dligheach." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "no" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "%(num)d bliadhna" +msgstr[1] "%(num)d bhliadhna" +msgstr[2] "%(num)d bliadhnaichean" +msgstr[3] "%(num)d bliadhna" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "%(num)d mhìos" +msgstr[1] "%(num)d mhìos" +msgstr[2] "%(num)d mìosan" +msgstr[3] "%(num)d mìos" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "%(num)d seachdain" +msgstr[1] "%(num)d sheachdain" +msgstr[2] "%(num)d seachdainean" +msgstr[3] "%(num)d seachdain" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "%(num)d latha" +msgstr[1] "%(num)d latha" +msgstr[2] "%(num)d làithean" +msgstr[3] "%(num)d latha" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "%(num)d uair a thìde" +msgstr[1] "%(num)d uair a thìde" +msgstr[2] "%(num)d uairean a thìde" +msgstr[3] "%(num)d uair a thìde" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "%(num)d mhionaid" +msgstr[1] "%(num)d mhionaid" +msgstr[2] "%(num)d mionaidean" +msgstr[3] "%(num)d mionaid" + +msgid "Forbidden" +msgstr "Toirmisgte" + +msgid "CSRF verification failed. Request aborted." +msgstr "Dh’fhàillig le dearbhadh CSRF. chaidh sgur dhen iarrtas." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" +"Chì thu an teachdaireachd seo air sgàth ’s gu bheil an làrach-lìn HTTPS seo " +"ag iarraidh air a’ bhrabhsair-lìn agad gun cuir e bann-cinn “Referer” thuice " +"ach cha deach gin a chur a-null. Tha feum air a’ bhann-chinn seo a chum " +"tèarainteachd ach nach cleachd treas-phàrtaidh am brabhsair agad gu droch-" +"rùnach." + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"Ma rèitich thu am brabhsair agad ach an cuir e bannan-cinn “Referer” à " +"comas, cuir an comas iad a-rithist, co-dhiù airson na làraich seo no airson " +"ceanglaichean HTTPS no airson iarrtasan “same-origin”." + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"Ma tha thu a’ cleachdadh taga no a’ gabhail a-staigh bann-cinn “'Referrer-Policy: no-referrer” feuch " +"an doir thu air falbh iad. Iarraidh an dìon CSRF bann-cinn “Referer” gus na " +"referers a dhearbhadh gu teann. Ma tha thu iomagaineach a thaobh do " +"prìobhaideachd, cleachd roghainnean eile mar airson " +"ceangal gu làraichean-lìn threas-phàrtaidhean." + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"Chì thu an teachdaireachd seo air sgàth ’s gu bheil an làrach-lìn seo ag " +"iarraidh briosgaid CSRF nuair a chuireas tu foirm a-null. Tha feum air a’ " +"bhriosgaid seo a chum tèarainteachd ach nach cleachd treas-phàrtaidh am " +"brabhsair agad gu droch-rùnach." + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" +"Ma rèitich thu am brabhsair agad ach an cuir e briosgaidean à comas, cuir an " +"comas iad a-rithist, co-dhiù airson na làraich seo no airson iarrtasan “same-" +"origin”." + +msgid "More information is available with DEBUG=True." +msgstr "Gheibh thu barrachd fiosrachaidh le DEBUG=True." + +msgid "No year specified" +msgstr "Cha deach bliadhna a shònrachadh" + +msgid "Date out of range" +msgstr "Tha ceann-là taobh thar na rainse" + +msgid "No month specified" +msgstr "Cha deach mìos a shònrachadh" + +msgid "No day specified" +msgstr "Cha deach latha a shònrachadh" + +msgid "No week specified" +msgstr "Cha deach seachdain a shònrachadh" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "Chan eil %(verbose_name_plural)s ri fhaighinn" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"Chan eil %(verbose_name_plural)s san àm ri teachd ri fhaighinn air sgàth ’s " +"gun deach %(class_name)s.allow_future a shuidheachadh air False." + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" +"Sreang cinn-là “%(datestr)s” mì-dhligheach airson an fhòrmait “%(format)s”" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "Cha deach %(verbose_name)s a lorg a fhreagras dhan cheist" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" +"Chan eil an duilleag ’na “last” is cha ghabh a h-iompachadh gu àireamh shlàn." + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Duilleag mhì-dhligheach (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" +"Tha liosta fhalamh ann agus chaidh “%(class_name)s.allow_empty” a " +"shuidheachadh air False." + +msgid "Directory indexes are not allowed here." +msgstr "Chan eil clàran-amais pasgain falamh ceadaichte an-seo." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "Chan eil “%(path)s” ann" + +#, python-format +msgid "Index of %(directory)s" +msgstr "Clàr-amais dhe %(directory)s" + +msgid "The install worked successfully! Congratulations!" +msgstr "Chaidh a stàladh! Meal do naidheachd!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"Seall na nòtaichean sgaoilidh airson Django " +"%(version)s" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" +"Chì thu an duilleag seo on a tha DEBUG=True ann am faidhle nan roghainnean agad agus cha do rèitich " +"thu URL sam bith fhathast." + +msgid "Django Documentation" +msgstr "Docamaideadh Django" + +msgid "Topics, references, & how-to’s" +msgstr "Cuspairean, iomraidhean ⁊ treòraichean" + +msgid "Tutorial: A Polling App" +msgstr "Oideachadh: Aplacaid cunntais-bheachd" + +msgid "Get started with Django" +msgstr "Dèan toiseach-tòiseachaidh le Django" + +msgid "Django Community" +msgstr "Coimhearsnachd Django" + +msgid "Connect, get help, or contribute" +msgstr "Dèan ceangal, faigh taic no cuidich" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/hsb/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/hsb/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..505d1e4c --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/hsb/LC_MESSAGES/django 3.po @@ -0,0 +1,1365 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Michael Wolf , 2016-2023 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 06:49+0000\n" +"Last-Translator: Michael Wolf , 2016-2023\n" +"Language-Team: Upper Sorbian (http://www.transifex.com/django/django/" +"language/hsb/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: hsb\n" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || " +"n%100==4 ? 2 : 3);\n" + +msgid "Afrikaans" +msgstr "Afrikaanšćina" + +msgid "Arabic" +msgstr "Arabšćina" + +msgid "Algerian Arabic" +msgstr "Algeriska arabšćina" + +msgid "Asturian" +msgstr "Asturišćina" + +msgid "Azerbaijani" +msgstr "Azerbajdźanšćina" + +msgid "Bulgarian" +msgstr "Bołharšćina" + +msgid "Belarusian" +msgstr "Běłorušćina" + +msgid "Bengali" +msgstr "Bengalšćina" + +msgid "Breton" +msgstr "Bretonšćina" + +msgid "Bosnian" +msgstr "Bosnišćina" + +msgid "Catalan" +msgstr "Katalanšćina" + +msgid "Central Kurdish (Sorani)" +msgstr "Centralna kurdišćina (Sorani)" + +msgid "Czech" +msgstr "Čěšćina" + +msgid "Welsh" +msgstr "Walizišćina" + +msgid "Danish" +msgstr "Danšćina" + +msgid "German" +msgstr "Němčina" + +msgid "Lower Sorbian" +msgstr "Delnjoserbšćina" + +msgid "Greek" +msgstr "Grjekšćina" + +msgid "English" +msgstr "Jendźelšćina" + +msgid "Australian English" +msgstr "Awstralska jendźelšćina" + +msgid "British English" +msgstr "Britiska jendźelšćina" + +msgid "Esperanto" +msgstr "Esperanto" + +msgid "Spanish" +msgstr "Španišćina" + +msgid "Argentinian Spanish" +msgstr "Argentinska španišćina" + +msgid "Colombian Spanish" +msgstr "Kolumbiska španišćina" + +msgid "Mexican Spanish" +msgstr "Mexiska španišćina" + +msgid "Nicaraguan Spanish" +msgstr "Nikaraguaska španišćina" + +msgid "Venezuelan Spanish" +msgstr "Venezuelska španišćina" + +msgid "Estonian" +msgstr "Estišćina" + +msgid "Basque" +msgstr "Baskišćina" + +msgid "Persian" +msgstr "Persišćina" + +msgid "Finnish" +msgstr "Finšćina" + +msgid "French" +msgstr "Francošćina" + +msgid "Frisian" +msgstr "Frizišćina" + +msgid "Irish" +msgstr "Irišćina" + +msgid "Scottish Gaelic" +msgstr "Šotiska gaelšćina" + +msgid "Galician" +msgstr "Galicišćina" + +msgid "Hebrew" +msgstr "Hebrejšćina" + +msgid "Hindi" +msgstr "Hindišćina" + +msgid "Croatian" +msgstr "Chorwatšćina" + +msgid "Upper Sorbian" +msgstr "Hornjoserbšćina" + +msgid "Hungarian" +msgstr "Madźaršćina" + +msgid "Armenian" +msgstr "Armenšćina" + +msgid "Interlingua" +msgstr "Interlingua" + +msgid "Indonesian" +msgstr "Indonezišćina" + +msgid "Igbo" +msgstr "Igbo" + +msgid "Ido" +msgstr "Ido" + +msgid "Icelandic" +msgstr "Islandšćina" + +msgid "Italian" +msgstr "Italšćina" + +msgid "Japanese" +msgstr "Japanšćina" + +msgid "Georgian" +msgstr "Georgišćina" + +msgid "Kabyle" +msgstr "Kabylšćina" + +msgid "Kazakh" +msgstr "Kazachšćina" + +msgid "Khmer" +msgstr "Khmeršćina" + +msgid "Kannada" +msgstr "Kannadšćina" + +msgid "Korean" +msgstr "Korejšćina" + +msgid "Kyrgyz" +msgstr "Kirgišćina" + +msgid "Luxembourgish" +msgstr "Luxemburgšćina" + +msgid "Lithuanian" +msgstr "Litawšćina" + +msgid "Latvian" +msgstr "Letišćina" + +msgid "Macedonian" +msgstr "Makedonšćina" + +msgid "Malayalam" +msgstr "Malajalam" + +msgid "Mongolian" +msgstr "Mongolšćina" + +msgid "Marathi" +msgstr "Marathišćina" + +msgid "Malay" +msgstr "Malajšćina" + +msgid "Burmese" +msgstr "Myanmaršćina" + +msgid "Norwegian Bokmål" +msgstr "Norwegski bokmål" + +msgid "Nepali" +msgstr "Nepalšćina" + +msgid "Dutch" +msgstr "Nižozemšćina" + +msgid "Norwegian Nynorsk" +msgstr "Norwegski nynorsk" + +msgid "Ossetic" +msgstr "Osetšćina" + +msgid "Punjabi" +msgstr "Pundźabišćina" + +msgid "Polish" +msgstr "Pólšćina" + +msgid "Portuguese" +msgstr "Portugalšćina" + +msgid "Brazilian Portuguese" +msgstr "Brazilska portugalšćina" + +msgid "Romanian" +msgstr "Rumunšćina" + +msgid "Russian" +msgstr "Rušćina" + +msgid "Slovak" +msgstr "Słowakšćina" + +msgid "Slovenian" +msgstr "Słowjenšćina" + +msgid "Albanian" +msgstr "Albanšćina" + +msgid "Serbian" +msgstr "Serbišćina" + +msgid "Serbian Latin" +msgstr "Serbšćina, łaćonska" + +msgid "Swedish" +msgstr "Šwedšćina" + +msgid "Swahili" +msgstr "Suahelšćina" + +msgid "Tamil" +msgstr "Tamilšćina" + +msgid "Telugu" +msgstr "Telugušćina" + +msgid "Tajik" +msgstr "Tadźikišćina" + +msgid "Thai" +msgstr "Thaišćina" + +msgid "Turkmen" +msgstr "Turkmenšćina" + +msgid "Turkish" +msgstr "Turkowšćina" + +msgid "Tatar" +msgstr "Tataršćina" + +msgid "Udmurt" +msgstr "Udmurtšćina" + +msgid "Ukrainian" +msgstr "Ukrainšćina" + +msgid "Urdu" +msgstr "Urdušćina" + +msgid "Uzbek" +msgstr "Uzbekšćina" + +msgid "Vietnamese" +msgstr "Vietnamšćina" + +msgid "Simplified Chinese" +msgstr "Zjednorjene chinšćina" + +msgid "Traditional Chinese" +msgstr "Tradicionalna chinšćina" + +msgid "Messages" +msgstr "Powěsće" + +msgid "Site Maps" +msgstr "Přehlady sydła" + +msgid "Static Files" +msgstr "Statiske dataje" + +msgid "Syndication" +msgstr "Syndikacija" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "…" + +msgid "That page number is not an integer" +msgstr "Tute čisko strony cyła ličba njeje." + +msgid "That page number is less than 1" +msgstr "Tute čisło strony je mjeńše hač 1." + +msgid "That page contains no results" +msgstr "Tuta strona wuslědki njewobsahuje" + +msgid "Enter a valid value." +msgstr "Zapodajće płaćiwu hódnotu." + +msgid "Enter a valid URL." +msgstr "Zapodajće płaćiwy URL." + +msgid "Enter a valid integer." +msgstr "Zapodajće płaćiwu cyłu ličbu." + +msgid "Enter a valid email address." +msgstr "Zapodajće płaćiwu e-mejlowu adresu." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" +"Zapodajće płaćiwe adresowe mjeno, kotrež jenož pismiki, ličby, podsmužki abo " +"wjazawki wobsahuje." + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"Zapodajće płaćiwe „adresowe mjeno“, kotrež jenož pismiki, ličby, podsmužki " +"abo wjazawki wobsahuje." + +msgid "Enter a valid IPv4 address." +msgstr "Zapodajće płaćiwu IPv4-adresu." + +msgid "Enter a valid IPv6 address." +msgstr "Zapodajće płaćiwu IPv6-adresu." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "Zapodajće płaćiwu IPv4- abo IPv6-adresu." + +msgid "Enter only digits separated by commas." +msgstr "Zapodajće jenož přez komy dźělene cyfry," + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "Zawěsćće, zo tuta hódnota je %(limit_value)s (je %(show_value)s)." + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "Zawěsćće, zo hódnota je mjeńša hač abo runja %(limit_value)s." + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "Zawěsćće, zo tuta hódnota je wjetša hač abo runja %(limit_value)s." + +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "" +"Zawěsćće, zo tuta hódnota je množina kročeloweje wulkosće %(limit_value)s." + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Zawěsćće, zo tuta hódnota ma znajmjeńša %(limit_value)d znamješko (ma " +"%(show_value)d)." +msgstr[1] "" +"Zawěsćće, zo tuta hódnota ma znajmjeńša %(limit_value)d znamješce (ma " +"%(show_value)d)." +msgstr[2] "" +"Zawěsćće, zo tuta hódnota ma znajmjeńša %(limit_value)d znamješka (ma " +"%(show_value)d)." +msgstr[3] "" +"Zawěsćće, zo tuta hódnota ma znajmjeńša %(limit_value)d znamješkow (ma " +"%(show_value)d)." + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Zawěsćće, zo tuta hódnota ma maksimalnje %(limit_value)d znamješko (ima " +"%(show_value)d)." +msgstr[1] "" +"Zawěsćće, zo tuta hódnota ma maksimalnje %(limit_value)d znamješce (ima " +"%(show_value)d)." +msgstr[2] "" +"Zawěsćće, zo tuta hódnota ma maksimalnje %(limit_value)d znamješka (ima " +"%(show_value)d)." +msgstr[3] "" +"Zawěsćće, zo tuta hódnota ma maksimalnje %(limit_value)d znamješkow (ima " +"%(show_value)d)." + +msgid "Enter a number." +msgstr "Zapodajće ličbu." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "Zawěsćće, zo njeje wjace hač %(max)s cyfry dohromady." +msgstr[1] "Zawěsćće, zo njeje wjace hač %(max)s cyfrow dohromady." +msgstr[2] "Zawěsćće, zo njeje wjace hač %(max)s cyfrow dohromady." +msgstr[3] "Zawěsćće, zo njeje wjace hač %(max)s cyfrow dohromady." + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "Zawěsćće, zo njeje wjace hač %(max)s decimalneho městna." +msgstr[1] "Zawěsćće, zo njeje wjace hač %(max)s decimalneju městnow." +msgstr[2] "Zawěsćće, zo njeje wjace hač %(max)s decimalnych městnow." +msgstr[3] "Zawěsćće, zo njeje wjace hač %(max)s decimalnych městnow." + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "Zawěsćće, zo njeje wjace hač %(max)s cyfry před decimalnej komu." +msgstr[1] "Zawěsćće, zo njeje wjace hač %(max)s cyfrow před decimalnej komu." +msgstr[2] "Zawěsćće, zo njeje wjace hač %(max)s cyfrow před decimalnej komu." +msgstr[3] "Zawěsćće, zo njeje wjace hač %(max)s cyfrow před decimalnej komu." + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"Datajowy sufiks ' %(extension)s' dowoleny njeje. Dowolene sufiksy su: " +"%(allowed_extensions)s." + +msgid "Null characters are not allowed." +msgstr "Prózdne znamješka dowolene njejsu." + +msgid "and" +msgstr "a" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "%(model_name)s z tutym %(field_labels)s hižo eksistuje." + +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "Wobmjezowanje \"%(name)s\" je překročene." + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "Hódnota %(value)r płaćiwa wólba njeje." + +msgid "This field cannot be null." +msgstr "Tute polo njesmě nul być." + +msgid "This field cannot be blank." +msgstr "Tute polo njesmě prózdne być." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "%(model_name)s z tutym %(field_label)s hižo eksistuje." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"%(field_label)s dyrbi za %(date_field_label)s %(lookup_type)s jónkróćne być." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "Polo typa: %(field_type)s" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "Hódnota „%(value)s“ dyrbi pak True pak False być." + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "Hódnota „%(value)s“ dyrbi pak True, False pak None być." + +msgid "Boolean (Either True or False)" +msgstr "Boolean (pak True pak False)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "Znamješkowy rjećazk (hač %(max_length)s)" + +msgid "String (unlimited)" +msgstr "Znamješkowy rjećazk (njewobmjezowany)" + +msgid "Comma-separated integers" +msgstr "Cyłe ličby dźělene přez komu" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" +"Hódnota „%(value)s“ ma njepłaćiwy datumowy format. Dyrbi we formaće DD.MM." +"YYYY być." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" +"Hódnota „%(value)s“ ma korektny format (DD.MM.YYYY), ale je njepłaćiwy datum." + +msgid "Date (without time)" +msgstr "Datum (bjez časa)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"Hódnota „%(value)s“ ma njepłaćiwy format. Dyrbi we formaće DD.MM.YYYY HH:MM[:" +"ss[.uuuuuu]][TZ] być." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"Hódnota „%(value)s“ ma korektny format (DD.MM.YYYY HH:MM[:ss[.uuuuuu]][TZ]), " +"ale je njepłaćiwy datum/čas." + +msgid "Date (with time)" +msgstr "Datum (z časom)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "Hódnota „%(value)s“ dyrbi decimalna ličba być." + +msgid "Decimal number" +msgstr "Decimalna ličba" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" +"Hódnota „%(value)s“ ma njepłaćiwy format. Dyrbi w formaće [DD] [HH:[MM:]]ss[." +"uuuuuu] być." + +msgid "Duration" +msgstr "Traće" + +msgid "Email address" +msgstr "E-mejlowa adresa" + +msgid "File path" +msgstr "Datajowa šćežka" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "Hódnota „%(value)s“ dyrbi decimalna ličba być." + +msgid "Floating point number" +msgstr "Komowa ličba typa float" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "Hódnota „%(value)s“ dyrbi integer być." + +msgid "Integer" +msgstr "Integer" + +msgid "Big (8 byte) integer" +msgstr "Big (8 byte) integer" + +msgid "Small integer" +msgstr "Mała cyła ličba" + +msgid "IPv4 address" +msgstr "IPv4-adresa" + +msgid "IP address" +msgstr "IP-adresa" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "Hódnota „%(value)s“ dyrbi pak None, True pak False być." + +msgid "Boolean (Either True, False or None)" +msgstr "Boolean (pak True, False pak None)" + +msgid "Positive big integer" +msgstr "Pozitiwna wulka cyła ličba" + +msgid "Positive integer" +msgstr "Pozitiwna cyła ličba" + +msgid "Positive small integer" +msgstr "Pozitiwna mała cyła ličba" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "Adresowe mjeno (hač %(max_length)s)" + +msgid "Text" +msgstr "Tekst" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" +"Hódnota „%(value)s“ ma njepłaćiwy format. Dyrbi we formaće HH:MM[:ss[." +"uuuuuu]] być." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" +"Hódnota „%(value)s“ ma korektny format (HH:MM[:ss[.uuuuuu]]), ale je " +"njepłaćiwy čas." + +msgid "Time" +msgstr "Čas" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "Hrube binarne daty" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "„%(value)s“ płaćiwy UUID njeje." + +msgid "Universally unique identifier" +msgstr "Uniwerselnje jónkróćny identifikator" + +msgid "File" +msgstr "Dataja" + +msgid "Image" +msgstr "Wobraz" + +msgid "A JSON object" +msgstr "JSON-objekt" + +msgid "Value must be valid JSON." +msgstr "Hódnota dyrbi płaćiwy JSON być." + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "Instanca %(model)s z %(field)s %(value)r njeeksistuje." + +msgid "Foreign Key (type determined by related field)" +msgstr "Cuzy kluč (typ so přez wotpowědne polo postaja)" + +msgid "One-to-one relationship" +msgstr "Poćah jedyn jedyn" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "Poćah %(from)s-%(to)s" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "Poćahi %(from)s-%(to)s" + +msgid "Many-to-many relationship" +msgstr "Poćah wjele wjele" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "Tute polo je trěbne." + +msgid "Enter a whole number." +msgstr "Zapodajće cyłu ličbu." + +msgid "Enter a valid date." +msgstr "Zapodajće płaćiwy datum." + +msgid "Enter a valid time." +msgstr "Zapodajće płaćiwy čas." + +msgid "Enter a valid date/time." +msgstr "Zapodajće płaćiwy datum/čas." + +msgid "Enter a valid duration." +msgstr "Zapodajće płaćiwe traće." + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "Ličba dnjow dyrbi mjez {min_days} a {max_days} być." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "Žana dataja je so pósłała. Přepruwujće kodowanski typ we formularje." + +msgid "No file was submitted." +msgstr "Žana dataja je so pósłała." + +msgid "The submitted file is empty." +msgstr "Pósłana dataja je prózdna." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"Zawěsćće, zo tute datajowe mjeno ma maksimalnje %(max)d znamješko (ma " +"%(length)d)." +msgstr[1] "" +"Zawěsćće, zo tute datajowe mjeno ma maksimalnje %(max)d znamješce (ma " +"%(length)d)." +msgstr[2] "" +"Zawěsćće, zo tute datajowe mjeno ma maksimalnje %(max)d znamješka (ma " +"%(length)d)." +msgstr[3] "" +"Zawěsćće, zo tute datajowe mjeno ma maksimalnje %(max)d znamješkow (ma " +"%(length)d)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" +"Prošu zapodajće dataju abo stajće hóčku do kontrolneho kašćika, nic wobě." + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Nahrajće płaćiwy wobraz. Dataja, kotruž sće nahrał, pak njebě wobraz pak bě " +"wobškodźeny wobraz. " + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" +"Wubjerće płaćiwu wolensku móžnosć. %(value)s žana k dispoziciji stejacych " +"wolenskich móžnosćow njeje. " + +msgid "Enter a list of values." +msgstr "Zapodajće lisćinu hódnotow." + +msgid "Enter a complete value." +msgstr "Zapodajće dospołnu hódnotu." + +msgid "Enter a valid UUID." +msgstr "Zapodajće płaćiwy UUID." + +msgid "Enter a valid JSON." +msgstr "Zapodajće płaćiwy JSON." + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(Schowane polo field %(name)s) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" +"Daty ManagementForm faluja abo su skepsane. Falowace pola: %(field_names)s. " +"Móžeće zmylkowu rozprawu spisać, jeli problem dale eksistuje." + +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "Prošu wotposćelće maksimalnje %(num)d formular." +msgstr[1] "Prošu wotposćelće maksimalnje %(num)d formularaj." +msgstr[2] "Prošu wotposćelće maksimalnje %(num)d formulary." +msgstr[3] "Prošu wotposćelće maksimalnje %(num)d formularow." + +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "Prošu zapodajće znajmjeńša %(num)d formu." +msgstr[1] "Prošu zapodajće znajmjeńša %(num)d formje." +msgstr[2] "Prošu zapodajće znajmjeńša %(num)d formy." +msgstr[3] "Prošu zapodajće znajmjeńša %(num)d formow." + +msgid "Order" +msgstr "Porjad" + +msgid "Delete" +msgstr "Zhašeć" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "Prošu porjedźće dwójne daty za %(field)s." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "Prošu porjedźće dwójne daty za %(field)s, kotrež dyrbja jónkróćne być." + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"Prošu porjedźće dwójne daty za %(field_name)s, kotrež dyrbja za %(lookup)s w " +"%(date_field)s jónkróćne być." + +msgid "Please correct the duplicate values below." +msgstr "Prošu porjedźće slědowace dwójne hódnoty." + +msgid "The inline value did not match the parent instance." +msgstr "Hódnota inline nadrjadowanej instancy njewotpowěduje." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" +"Wubjerće płaćiwu wolensku móžnosć. Tuta wolenska móžnosć jedna z k " +"dispoziciji stejacych wolenskich móžnosćow njeje." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "\"%(pk)s\" płaćiwa hódnota njeje." + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"%(datetime)s njeda so w časowym pasmje %(current_timezone)s interpretować; " +"je snano dwuzmyslny abo njeeksistuje." + +msgid "Clear" +msgstr "Zhašeć" + +msgid "Currently" +msgstr "Tuchwilu" + +msgid "Change" +msgstr "Změnić" + +msgid "Unknown" +msgstr "Njeznaty" + +msgid "Yes" +msgstr "Haj" + +msgid "No" +msgstr "Ně" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "haj,ně,snano" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d bajt" +msgstr[1] "%(size)d bajtaj" +msgstr[2] "%(size)d bajty" +msgstr[3] "%(size)d bajtow" + +#, python-format +msgid "%s KB" +msgstr "%s KB" + +#, python-format +msgid "%s MB" +msgstr "%s MB" + +#, python-format +msgid "%s GB" +msgstr "%s GB" + +#, python-format +msgid "%s TB" +msgstr "%s TB" + +#, python-format +msgid "%s PB" +msgstr "%s PB" + +msgid "p.m." +msgstr "popołdnju" + +msgid "a.m." +msgstr "dopołdnja" + +msgid "PM" +msgstr "popołdnju" + +msgid "AM" +msgstr "dopołdnja" + +msgid "midnight" +msgstr "połnoc" + +msgid "noon" +msgstr "připołdnjo" + +msgid "Monday" +msgstr "Póndźela" + +msgid "Tuesday" +msgstr "Wutora" + +msgid "Wednesday" +msgstr "Srjeda" + +msgid "Thursday" +msgstr "Štwórtk" + +msgid "Friday" +msgstr "Pjatk" + +msgid "Saturday" +msgstr "Sobota" + +msgid "Sunday" +msgstr "Njedźela" + +msgid "Mon" +msgstr "Pón" + +msgid "Tue" +msgstr "Wut" + +msgid "Wed" +msgstr "Srj" + +msgid "Thu" +msgstr "Štw" + +msgid "Fri" +msgstr "Pja" + +msgid "Sat" +msgstr "Sob" + +msgid "Sun" +msgstr "Nje" + +msgid "January" +msgstr "Januar" + +msgid "February" +msgstr "Februar" + +msgid "March" +msgstr "Měrc" + +msgid "April" +msgstr "Apryl" + +msgid "May" +msgstr "Meja" + +msgid "June" +msgstr "Junij" + +msgid "July" +msgstr "Julij" + +msgid "August" +msgstr "Awgust" + +msgid "September" +msgstr "September" + +msgid "October" +msgstr "Oktober" + +msgid "November" +msgstr "Nowember" + +msgid "December" +msgstr "December" + +msgid "jan" +msgstr "jan." + +msgid "feb" +msgstr "feb." + +msgid "mar" +msgstr "měr." + +msgid "apr" +msgstr "apr." + +msgid "may" +msgstr "mej." + +msgid "jun" +msgstr "jun." + +msgid "jul" +msgstr "jul." + +msgid "aug" +msgstr "awg." + +msgid "sep" +msgstr "sep." + +msgid "oct" +msgstr "okt." + +msgid "nov" +msgstr "now." + +msgid "dec" +msgstr "dec." + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "Jan." + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "Feb." + +msgctxt "abbrev. month" +msgid "March" +msgstr "Měrc" + +msgctxt "abbrev. month" +msgid "April" +msgstr "Apryl" + +msgctxt "abbrev. month" +msgid "May" +msgstr "Meja" + +msgctxt "abbrev. month" +msgid "June" +msgstr "Junij" + +msgctxt "abbrev. month" +msgid "July" +msgstr "Julij" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "Awg." + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "Sept." + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "Okt." + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "Now." + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "Dec." + +msgctxt "alt. month" +msgid "January" +msgstr "Januar" + +msgctxt "alt. month" +msgid "February" +msgstr "Februar" + +msgctxt "alt. month" +msgid "March" +msgstr "Měrc" + +msgctxt "alt. month" +msgid "April" +msgstr "Apryl" + +msgctxt "alt. month" +msgid "May" +msgstr "Meja" + +msgctxt "alt. month" +msgid "June" +msgstr "Junij" + +msgctxt "alt. month" +msgid "July" +msgstr "Julij" + +msgctxt "alt. month" +msgid "August" +msgstr "Awgust" + +msgctxt "alt. month" +msgid "September" +msgstr "September" + +msgctxt "alt. month" +msgid "October" +msgstr "Oktober" + +msgctxt "alt. month" +msgid "November" +msgstr "Nowember" + +msgctxt "alt. month" +msgid "December" +msgstr "December" + +msgid "This is not a valid IPv6 address." +msgstr "To płaćiwa IPv6-adresa njeje." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "abo" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "%(num)d lěto" +msgstr[1] "%(num)dlěće" +msgstr[2] "%(num)d lěta" +msgstr[3] "%(num)d lět" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "%(num)d měsac" +msgstr[1] "%(num)d měsacaj" +msgstr[2] "%(num)d měsacy" +msgstr[3] "%(num)d měsacow" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "%(num)d tydźeń" +msgstr[1] "%(num)d njedźeli" +msgstr[2] "%(num)d njedźele" +msgstr[3] "%(num)d njedźel" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "%(num)d dźeń" +msgstr[1] "%(num)d dnjej" +msgstr[2] "%(num)d dny" +msgstr[3] "%(num)d dnjow" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "%(num)d hodźina" +msgstr[1] "%(num)d hodźinje" +msgstr[2] "%(num)d hodźiny" +msgstr[3] "%(num)d hodźin" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "%(num)d mjeńšina" +msgstr[1] "%(num)d mjeńšinje" +msgstr[2] "%(num)d mjeńšiny" +msgstr[3] "%(num)d mjeńšin" + +msgid "Forbidden" +msgstr "Zakazany" + +msgid "CSRF verification failed. Request aborted." +msgstr "CSRF-přepruwowanje je so nimokuliło. Naprašowanje je so přetorhnyło." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" +"Widźiće tutu zdźělenku, dokelž tute HTTPS-sydło \"Referer header\" trjeba, " +"kotryž so ma na waš webwobhladowak pósłać, ale žadyn njeje so pósłał. Tutón " +"header je z wěstotnych přičinow trěbny, zo by so zawěsćiło, zo waš " +"wobhladowak so wot třećich njekapruje." + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"Jei sće swój wobhladowak tak konfigurował, zo su hłowy „Referer“ " +"znjemóžnjene, zmóžńće je, znajmjeńša za tute sydło abo za HTTPS-zwiski abo " +"za naprašowanja „sameorigin“." + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"Jeli značku wužiwaće abo hłowu „Referrer-Policy: no-referrer“ zapřijimaće, " +"wotstrońće je prošu. CSRF-škit trjeba hłowu „Referer“ , zo by striktnu " +"kontrolu referer přewjedźe. Jeli so wo priwatnosć staraće, wužiwajće " +"alternatiwy kaž za wotkazy k sydłam třećich." + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"Widźiće tutu zdźělenku, dokelž tute sydło CSRF-plack trjeba, hdyž so " +"formulary wotesyłaja. Tutón plack je z přičinow wěstoty trěbny, zo by so waš " +"wobhladowak wot třećich njekapruje." + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" +"Jeli sće swój wobhladowak tak konfigurował, zo su placki znjemóžnjene, " +"zmóžńće je zaso, znajmjeńša za tute sydło abo za naprašowanja „same-origin“." + +msgid "More information is available with DEBUG=True." +msgstr "Z DEBUG=True su dalše informacije k dispoziciji." + +msgid "No year specified" +msgstr "Žane lěto podate" + +msgid "Date out of range" +msgstr "Datum zwonka wobłuka" + +msgid "No month specified" +msgstr "Žadyn měsac podaty" + +msgid "No day specified" +msgstr "Žadyn dźeń podaty" + +msgid "No week specified" +msgstr "Žadyn tydźeń podaty" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "Žadyn %(verbose_name_plural)s k dispoziciji njeje" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"Přichodowe %(verbose_name_plural)s k dispoziciji njejsu, dokelž hódnota " +"%(class_name)s.allow_future je False." + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" +"Njepłaćiwy „%(format)s“ za datumowy znamješkowy rjaćazk „%(datestr)s“ podaty" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "Žane %(verbose_name)s namakane, kotrež naprašowanju wotpowěduje" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "Strona „last“ njeje, ani njeda so do int konwertować." + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Njepłaćiwa strona (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "Prózdna lisćina a „%(class_name)s.allow_empty“ je False." + +msgid "Directory indexes are not allowed here." +msgstr "Zapisowe indeksy tu dowolone njejsu." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "„%(path)s“ njeeksistuje" + +#, python-format +msgid "Index of %(directory)s" +msgstr "Indeks %(directory)s" + +msgid "The install worked successfully! Congratulations!" +msgstr "Instalacija bě wuspěšna! Zbožopřeće!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"Čitajće wersijowe informacije za Django " +"%(version)s" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not " +"configured any URLs." +msgstr "" +"Widźiće tutu stronu, dokelž DEBUG=True je we wašej dataji nastajenjow a njejsće URL " +"skonfigurował." + +msgid "Django Documentation" +msgstr "Dokumentacija Django" + +msgid "Topics, references, & how-to’s" +msgstr "Temy, referency a nawody" + +msgid "Tutorial: A Polling App" +msgstr "Nawod: Naprašowanske nałoženje" + +msgid "Get started with Django" +msgstr "Prěnje kroki z Django" + +msgid "Django Community" +msgstr "Zhromadźenstwo Django" + +msgid "Connect, get help, or contribute" +msgstr "Zwjazać, pomoc wobstarać abo přinošować" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/ja/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/ja/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..e9aabef8 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/ja/LC_MESSAGES/django 3.po @@ -0,0 +1,1313 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# xiu1 , 2016 +# tadasu , 2020 +# Goto Hayato , 2021 +# Goto Hayato , 2019 +# Hiroki Sawano, 2022 +# Jannis Leidel , 2011 +# Kamiyama Satoshi, 2021 +# Kentaro Matsuzaki , 2015 +# Masashi SHIBATA , 2017 +# Nikita K , 2019 +# Shinichi Katsumata , 2019 +# Shinya Okano , 2012-2019,2021,2023 +# Taichi Taniguchi, 2022 +# Takuro Onoue , 2020 +# Takuya N , 2020 +# Tetsuya Morimoto , 2011 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 06:49+0000\n" +"Last-Translator: Shinya Okano , 2012-2019,2021,2023\n" +"Language-Team: Japanese (http://www.transifex.com/django/django/language/" +"ja/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ja\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +msgid "Afrikaans" +msgstr "アフリカーンス語" + +msgid "Arabic" +msgstr "アラビア語" + +msgid "Algerian Arabic" +msgstr "アラビア語(アルジェリア)" + +msgid "Asturian" +msgstr "アストゥリアス語" + +msgid "Azerbaijani" +msgstr "アゼルバイジャン語" + +msgid "Bulgarian" +msgstr "ブルガリア語" + +msgid "Belarusian" +msgstr "ベラルーシ語" + +msgid "Bengali" +msgstr "ベンガル語" + +msgid "Breton" +msgstr "ブルトン語" + +msgid "Bosnian" +msgstr "ボスニア語" + +msgid "Catalan" +msgstr "カタロニア語" + +msgid "Central Kurdish (Sorani)" +msgstr "中央クルド語 (ソラニー語)" + +msgid "Czech" +msgstr "チェコ語" + +msgid "Welsh" +msgstr "ウェールズ語" + +msgid "Danish" +msgstr "デンマーク語" + +msgid "German" +msgstr "ドイツ語" + +msgid "Lower Sorbian" +msgstr "低地ソルブ語" + +msgid "Greek" +msgstr "ギリシャ語" + +msgid "English" +msgstr "英語(米国)" + +msgid "Australian English" +msgstr "英語(オーストラリア)" + +msgid "British English" +msgstr "英語(英国)" + +msgid "Esperanto" +msgstr "エスペラント語" + +msgid "Spanish" +msgstr "スペイン語" + +msgid "Argentinian Spanish" +msgstr "アルゼンチンスペイン語" + +msgid "Colombian Spanish" +msgstr "コロンビアスペイン語" + +msgid "Mexican Spanish" +msgstr "メキシコスペイン語" + +msgid "Nicaraguan Spanish" +msgstr "ニカラグアスペイン語" + +msgid "Venezuelan Spanish" +msgstr "ベネズエラスペイン語" + +msgid "Estonian" +msgstr "エストニア語" + +msgid "Basque" +msgstr "バスク語" + +msgid "Persian" +msgstr "ペルシア語" + +msgid "Finnish" +msgstr "フィンランド語" + +msgid "French" +msgstr "フランス語" + +msgid "Frisian" +msgstr "フリジア語" + +msgid "Irish" +msgstr "アイルランド語" + +msgid "Scottish Gaelic" +msgstr "ゲール語(スコットランド)" + +msgid "Galician" +msgstr "ガリシア語" + +msgid "Hebrew" +msgstr "ヘブライ語" + +msgid "Hindi" +msgstr "ヒンディー語" + +msgid "Croatian" +msgstr "クロアチア語" + +msgid "Upper Sorbian" +msgstr "高地ソルブ語" + +msgid "Hungarian" +msgstr "ハンガリー語" + +msgid "Armenian" +msgstr "アルメニア" + +msgid "Interlingua" +msgstr "インターリングア" + +msgid "Indonesian" +msgstr "インドネシア語" + +msgid "Igbo" +msgstr "イグボ語" + +msgid "Ido" +msgstr "イド語" + +msgid "Icelandic" +msgstr "アイスランド語" + +msgid "Italian" +msgstr "イタリア語" + +msgid "Japanese" +msgstr "日本語" + +msgid "Georgian" +msgstr "グルジア語" + +msgid "Kabyle" +msgstr "カビル語" + +msgid "Kazakh" +msgstr "カザフ語" + +msgid "Khmer" +msgstr "クメール語" + +msgid "Kannada" +msgstr "カンナダ語" + +msgid "Korean" +msgstr "韓国語" + +msgid "Kyrgyz" +msgstr "キルギス語" + +msgid "Luxembourgish" +msgstr "ルクセンブルグ語" + +msgid "Lithuanian" +msgstr "リトアニア語" + +msgid "Latvian" +msgstr "ラトビア語" + +msgid "Macedonian" +msgstr "マケドニア語" + +msgid "Malayalam" +msgstr "マラヤーラム語" + +msgid "Mongolian" +msgstr "モンゴル語" + +msgid "Marathi" +msgstr "マラーティー語" + +msgid "Malay" +msgstr "マレー語" + +msgid "Burmese" +msgstr "ビルマ語" + +msgid "Norwegian Bokmål" +msgstr "ノルウェーのブークモール" + +msgid "Nepali" +msgstr "ネパール語" + +msgid "Dutch" +msgstr "オランダ語" + +msgid "Norwegian Nynorsk" +msgstr "ノルウェーのニーノシュク" + +msgid "Ossetic" +msgstr "オセット語" + +msgid "Punjabi" +msgstr "パンジャブ語" + +msgid "Polish" +msgstr "ポーランド語" + +msgid "Portuguese" +msgstr "ポルトガル語" + +msgid "Brazilian Portuguese" +msgstr "ブラジルポルトガル語" + +msgid "Romanian" +msgstr "ルーマニア語" + +msgid "Russian" +msgstr "ロシア語" + +msgid "Slovak" +msgstr "スロバキア語" + +msgid "Slovenian" +msgstr "スロヴェニア語" + +msgid "Albanian" +msgstr "アルバニア語" + +msgid "Serbian" +msgstr "セルビア語" + +msgid "Serbian Latin" +msgstr "セルビア語ラテン文字" + +msgid "Swedish" +msgstr "スウェーデン語" + +msgid "Swahili" +msgstr "スワヒリ語" + +msgid "Tamil" +msgstr "タミル語" + +msgid "Telugu" +msgstr "テルグ語" + +msgid "Tajik" +msgstr "タジク語" + +msgid "Thai" +msgstr "タイ語" + +msgid "Turkmen" +msgstr "トルクメン語" + +msgid "Turkish" +msgstr "トルコ語" + +msgid "Tatar" +msgstr "タタール語" + +msgid "Udmurt" +msgstr "ウドムルト語" + +msgid "Ukrainian" +msgstr "ウクライナ語" + +msgid "Urdu" +msgstr "ウルドゥー語" + +msgid "Uzbek" +msgstr "ウズベク語" + +msgid "Vietnamese" +msgstr "ベトナム語" + +msgid "Simplified Chinese" +msgstr "簡体字中国語" + +msgid "Traditional Chinese" +msgstr "繁体字中国語" + +msgid "Messages" +msgstr "メッセージ" + +msgid "Site Maps" +msgstr "サイトマップ" + +msgid "Static Files" +msgstr "静的ファイル" + +msgid "Syndication" +msgstr "シンジケーション" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "…" + +msgid "That page number is not an integer" +msgstr "このページ番号は整数ではありません。" + +msgid "That page number is less than 1" +msgstr "ページ番号が 1 よりも小さいです。" + +msgid "That page contains no results" +msgstr "このページには結果が含まれていません。" + +msgid "Enter a valid value." +msgstr "値を正しく入力してください。" + +msgid "Enter a valid URL." +msgstr "URLを正しく入力してください。" + +msgid "Enter a valid integer." +msgstr "整数を正しく入力してください。" + +msgid "Enter a valid email address." +msgstr "有効なメールアドレスを入力してください。" + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" +"“slug” には半角の英数字、アンダースコア、ハイフン以外は使用できません。" + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"ユニコード文字、数字、アンダースコアまたはハイフンで構成された、有効なスラグ" +"を入力してください。" + +msgid "Enter a valid IPv4 address." +msgstr "有効なIPアドレス (IPv4) を入力してください。" + +msgid "Enter a valid IPv6 address." +msgstr "IPv6の正しいアドレスを入力してください。" + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "IPv4またはIPv6の正しいアドレスを入力してください。" + +msgid "Enter only digits separated by commas." +msgstr "カンマ区切りの数字だけを入力してください。" + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" +"この値は %(limit_value)s でなければなりません(実際には %(show_value)s でし" +"た) 。" + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "この値は %(limit_value)s 以下でなければなりません。" + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "この値は %(limit_value)s 以上でなければなりません。" + +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "" +"この値がステップサイズ %(limit_value)s の倍数であることを確認してください。" + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"この値が少なくとも %(limit_value)d 文字以上であることを確認してください " +"(%(show_value)d 文字になっています)。" + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"この値は %(limit_value)d 文字以下でなければなりません( %(show_value)d 文字に" +"なっています)。" + +msgid "Enter a number." +msgstr "数値を入力してください。" + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "この値は合計 %(max)s 桁以内でなければなりません。" + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "この値は小数点以下が合計 %(max)s 桁以内でなければなりません。" + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "この値は小数点より前が合計 %(max)s 桁以内でなければなりません。" + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"ファイル拡張子 “%(extension)s” は許可されていません。許可されている拡張子は " +"%(allowed_extensions)s です。" + +msgid "Null characters are not allowed." +msgstr "何か文字を入力してください。" + +msgid "and" +msgstr "と" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "この %(field_labels)s を持った %(model_name)s が既に存在します。" + +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "制約 “%(name)s” に違反しています。" + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "%(value)r は有効な選択肢ではありません。" + +msgid "This field cannot be null." +msgstr "このフィールドには NULL を指定できません。" + +msgid "This field cannot be blank." +msgstr "このフィールドは空ではいけません。" + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "この %(field_label)s を持った %(model_name)s が既に存在します。" + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"%(date_field_label)s %(lookup_type)s では %(field_label)s がユニークである必" +"要があります。" + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "タイプが %(field_type)s のフィールド" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "“%(value)s” は True または False にしなければなりません。" + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "“%(value)s” は True 、 False または None の値でなければなりません。" + +msgid "Boolean (Either True or False)" +msgstr "ブール値 (真: True または偽: False)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "文字列 ( %(max_length)s 字まで )" + +msgid "String (unlimited)" +msgstr "文字列 (無制限)" + +msgid "Comma-separated integers" +msgstr "カンマ区切りの整数" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" +"“%(value)s” は無効な日付形式です。YYYY-MM-DD 形式にしなければなりません。" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "“%(value)s” は有効な日付形式(YYYY-MM-DD)ですが、不正な日付です。" + +msgid "Date (without time)" +msgstr "日付" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"“%(value)s” は無効な形式の値です。 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] 形式で" +"なければなりません。" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"“%(value)s” は正しい形式 (YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) の値ですが、無" +"効な日時です。" + +msgid "Date (with time)" +msgstr "日時" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "“%(value)s” は10進浮動小数値にしなければなりません。" + +msgid "Decimal number" +msgstr "10 進数 (小数可)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" +"“%(value)s” は無効な形式の値です。 [DD] [HH:[MM:]]ss[.uuuuuu] 形式でなければ" +"なりません。" + +msgid "Duration" +msgstr "時間差分" + +msgid "Email address" +msgstr "メールアドレス" + +msgid "File path" +msgstr "ファイルの場所" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "“%(value)s” は小数値にしなければなりません。" + +msgid "Floating point number" +msgstr "浮動小数点" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "“%(value)s” は整数値にしなければなりません。" + +msgid "Integer" +msgstr "整数" + +msgid "Big (8 byte) integer" +msgstr "大きな(8バイト)整数" + +msgid "Small integer" +msgstr "小さな整数" + +msgid "IPv4 address" +msgstr "IPv4アドレス" + +msgid "IP address" +msgstr "IP アドレス" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "“%(value)s” は None、True または False の値でなければなりません。" + +msgid "Boolean (Either True, False or None)" +msgstr "ブール値 (真: True 、偽: False または None)" + +msgid "Positive big integer" +msgstr "正の多倍長整数" + +msgid "Positive integer" +msgstr "正の整数" + +msgid "Positive small integer" +msgstr "小さな正の整数" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "スラグ(%(max_length)s文字以内)" + +msgid "Text" +msgstr "テキスト" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" +"“%(value)s” は無効な形式の値です。 HH:MM[:ss[.uuuuuu]] 形式でなければなりませ" +"ん。" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "“%(value)s” は正しい形式(HH:MM[:ss[.uuuuuu]])ですが、無効な時刻です。" + +msgid "Time" +msgstr "時刻" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "生のバイナリデータ" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "“%(value)s” は有効なUUIDではありません。" + +msgid "Universally unique identifier" +msgstr "汎用一意識別子" + +msgid "File" +msgstr "ファイル" + +msgid "Image" +msgstr "画像" + +msgid "A JSON object" +msgstr "JSONオブジェクト" + +msgid "Value must be valid JSON." +msgstr "JSONとして正しい値にしてください。" + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "%(field)s が %(value)r である %(model)s のインスタンスは存在しません。" + +msgid "Foreign Key (type determined by related field)" +msgstr "外部キー(型は関連フィールドによって決まります)" + +msgid "One-to-one relationship" +msgstr "1対1の関連" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "%(from)s-%(to)s の関連" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "%(from)s-%(to)s の関連" + +msgid "Many-to-many relationship" +msgstr "多対多の関連" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "このフィールドは必須です。" + +msgid "Enter a whole number." +msgstr "整数を入力してください。" + +msgid "Enter a valid date." +msgstr "日付を正しく入力してください。" + +msgid "Enter a valid time." +msgstr "時間を正しく入力してください。" + +msgid "Enter a valid date/time." +msgstr "日時を正しく入力してください。" + +msgid "Enter a valid duration." +msgstr "時間差分を正しく入力してください。" + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "日数は{min_days}から{max_days}の間でなければなりません。" + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" +"ファイルが取得できませんでした。フォームのencoding typeを確認してください。" + +msgid "No file was submitted." +msgstr "ファイルが送信されていません。" + +msgid "The submitted file is empty." +msgstr "入力されたファイルは空です。" + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"このファイル名は %(max)d 文字以下でなければなりません( %(length)d 文字になっ" +"ています)。" + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" +"ファイルを投稿するか、クリアチェックボックスをチェックするかどちらかを選択し" +"てください。両方とも行ってはいけません。" + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"画像をアップロードしてください。アップロードしたファイルは画像でないか、また" +"は壊れています。" + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "正しく選択してください。 %(value)s は候補にありません。" + +msgid "Enter a list of values." +msgstr "リストを入力してください。" + +msgid "Enter a complete value." +msgstr "すべての値を入力してください。" + +msgid "Enter a valid UUID." +msgstr "UUIDを正しく入力してください。" + +msgid "Enter a valid JSON." +msgstr "JSONを正しく入力してください。" + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(隠しフィールド %(name)s) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" +"ManagementForm のデータが不足しているか改竄されています。不足するフィールドの" +"数: %(field_names)s 。問題が続くようならバグレポートを出す必要があるかもしれ" +"ません。" + +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "最大で %(num)d 個のフォームを送信してください。" + +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "少なくとも %(num)d 個のフォームを送信してください。" + +msgid "Order" +msgstr "並び変え" + +msgid "Delete" +msgstr "削除" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "%(field)s の重複したデータを修正してください。" + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" +"%(field)s の重複したデータを修正してください。このフィールドはユニークである" +"必要があります。" + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"%(field_name)s の重複したデータを修正してください。%(date_field)s %(lookup)s " +"では %(field_name)s がユニークである必要があります。" + +msgid "Please correct the duplicate values below." +msgstr "下記の重複したデータを修正してください。" + +msgid "The inline value did not match the parent instance." +msgstr "インライン値が親のインスタンスに一致しません。" + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "正しく選択してください。選択したものは候補にありません。" + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "“%(pk)s” は無効な値です。" + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"%(datetime)s は %(current_timezone)s のタイムゾーンでは解釈できませんでした。" +"それは曖昧であるか、存在しない可能性があります。" + +msgid "Clear" +msgstr "クリア" + +msgid "Currently" +msgstr "現在" + +msgid "Change" +msgstr "変更" + +msgid "Unknown" +msgstr "不明" + +msgid "Yes" +msgstr "はい" + +msgid "No" +msgstr "いいえ" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "はい,いいえ,たぶん" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d バイト" + +#, python-format +msgid "%s KB" +msgstr "%s KB" + +#, python-format +msgid "%s MB" +msgstr "%s MB" + +#, python-format +msgid "%s GB" +msgstr "%s GB" + +#, python-format +msgid "%s TB" +msgstr "%s TB" + +#, python-format +msgid "%s PB" +msgstr "%s PB" + +msgid "p.m." +msgstr "p.m." + +msgid "a.m." +msgstr "a.m." + +msgid "PM" +msgstr "PM" + +msgid "AM" +msgstr "AM" + +msgid "midnight" +msgstr "0時" + +msgid "noon" +msgstr "12時" + +msgid "Monday" +msgstr "月曜日" + +msgid "Tuesday" +msgstr "火曜日" + +msgid "Wednesday" +msgstr "水曜日" + +msgid "Thursday" +msgstr "木曜日" + +msgid "Friday" +msgstr "金曜日" + +msgid "Saturday" +msgstr "土曜日" + +msgid "Sunday" +msgstr "日曜日" + +msgid "Mon" +msgstr "月" + +msgid "Tue" +msgstr "火" + +msgid "Wed" +msgstr "水" + +msgid "Thu" +msgstr "木" + +msgid "Fri" +msgstr "金" + +msgid "Sat" +msgstr "土" + +msgid "Sun" +msgstr "日" + +msgid "January" +msgstr "1月" + +msgid "February" +msgstr "2月" + +msgid "March" +msgstr "3月" + +msgid "April" +msgstr "4月" + +msgid "May" +msgstr "5月" + +msgid "June" +msgstr "6月" + +msgid "July" +msgstr "7月" + +msgid "August" +msgstr "8月" + +msgid "September" +msgstr "9月" + +msgid "October" +msgstr "10月" + +msgid "November" +msgstr "11月" + +msgid "December" +msgstr "12月" + +msgid "jan" +msgstr "1月" + +msgid "feb" +msgstr "2月" + +msgid "mar" +msgstr "3月" + +msgid "apr" +msgstr "4月" + +msgid "may" +msgstr "5月" + +msgid "jun" +msgstr "6月" + +msgid "jul" +msgstr "7月" + +msgid "aug" +msgstr "8月" + +msgid "sep" +msgstr "9月" + +msgid "oct" +msgstr "10月" + +msgid "nov" +msgstr "11月" + +msgid "dec" +msgstr "12月" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "1月" + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "2月" + +msgctxt "abbrev. month" +msgid "March" +msgstr "3月" + +msgctxt "abbrev. month" +msgid "April" +msgstr "4月" + +msgctxt "abbrev. month" +msgid "May" +msgstr "5月" + +msgctxt "abbrev. month" +msgid "June" +msgstr "6月" + +msgctxt "abbrev. month" +msgid "July" +msgstr "7月" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "8月" + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "9月" + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "10月" + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "11月" + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "12月" + +msgctxt "alt. month" +msgid "January" +msgstr "1月" + +msgctxt "alt. month" +msgid "February" +msgstr "2月" + +msgctxt "alt. month" +msgid "March" +msgstr "3月" + +msgctxt "alt. month" +msgid "April" +msgstr "4月" + +msgctxt "alt. month" +msgid "May" +msgstr "5月" + +msgctxt "alt. month" +msgid "June" +msgstr "6月" + +msgctxt "alt. month" +msgid "July" +msgstr "7月" + +msgctxt "alt. month" +msgid "August" +msgstr "8月" + +msgctxt "alt. month" +msgid "September" +msgstr "9月" + +msgctxt "alt. month" +msgid "October" +msgstr "10月" + +msgctxt "alt. month" +msgid "November" +msgstr "11月" + +msgctxt "alt. month" +msgid "December" +msgstr "12月" + +msgid "This is not a valid IPv6 address." +msgstr "これは有効なIPv6アドレスではありません。" + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "または" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "%(num)d年" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "%(num)dヶ月" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "%(num)d週間" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "%(num)d日" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "%(num)d時間" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "%(num)d分" + +msgid "Forbidden" +msgstr "アクセス禁止" + +msgid "CSRF verification failed. Request aborted." +msgstr "CSRF検証に失敗したため、リクエストは中断されました。" + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" +"このメッセージが表示されている理由は、このHTTPSのサイトはウェブブラウザからリ" +"ファラーヘッダが送信されることを必須としていますが、送信されなかったためで" +"す。このヘッダはセキュリティ上の理由(使用中のブラウザが第三者によってハイ" +"ジャックされていないことを確認するため)で必要です。" + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"もしブラウザのリファラーヘッダを無効に設定しているならば、HTTPS接続やsame-" +"originリクエストのために、少なくともこのサイトでは再度有効にしてください。" + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"もし タグを使用しているか " +"“Referrer-Policy: no-referrer” ヘッダを含んでいる場合は削除してください。" +"CSRF プロテクションは、厳密に “Referer” ヘッダが必要です。プライバシーが気に" +"なる場合は などの代替で第三者サイトと接続してくださ" +"い。" + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"このメッセージが表示されている理由は、このサイトはフォーム送信時にCSRFクッ" +"キーを必須としているためです。このクッキーはセキュリティ上の理由(使用中のブラ" +"ウザが第三者によってハイジャックされていないことを確認するため)で必要です。" + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" +"もしブラウザのクッキーを無効に設定しているならば、same-originリクエストのため" +"に少なくともこのサイトでは再度有効にしてください。" + +msgid "More information is available with DEBUG=True." +msgstr "詳細な情報は DEBUG=True を設定すると利用できます。" + +msgid "No year specified" +msgstr "年が未指定です" + +msgid "Date out of range" +msgstr "日付が有効範囲外です" + +msgid "No month specified" +msgstr "月が未指定です" + +msgid "No day specified" +msgstr "日が未指定です" + +msgid "No week specified" +msgstr "週が未指定です" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "%(verbose_name_plural)s は利用できません" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"%(class_name)s.allow_futureがFalseであるため、未来の%(verbose_name_plural)sは" +"利用できません。" + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "指定された形式 “%(format)s” では “%(datestr)s” は無効な日付文字列です" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "クエリーに一致する %(verbose_name)s は見つかりませんでした" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "ページが 「最後」ではないか、数値に変換できる値ではありません。" + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "無効なページです (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "空の一覧かつ “%(class_name)s.allow_empty” が False です。" + +msgid "Directory indexes are not allowed here." +msgstr "ここではディレクトリインデックスが許可されていません。" + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "“%(path)s” が存在しません" + +#, python-format +msgid "Index of %(directory)s" +msgstr "%(directory)sのディレクトリインデックス" + +msgid "The install worked successfully! Congratulations!" +msgstr "インストールは成功しました!おめでとうございます!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"Django%(version)sのリリースノートを見る。" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not " +"configured any URLs." +msgstr "" +"このページは、設定ファイルでDEBUG=Trueが指定され、何もURLが設定されていない時に表示" +"されます。" + +msgid "Django Documentation" +msgstr "Django ドキュメント" + +msgid "Topics, references, & how-to’s" +msgstr "トピック、リファレンス、ハウツー" + +msgid "Tutorial: A Polling App" +msgstr "チュートリアル: 投票アプリケーション" + +msgid "Get started with Django" +msgstr "Djangoを始めよう" + +msgid "Django Community" +msgstr "Djangoのコミュニティ" + +msgid "Connect, get help, or contribute" +msgstr "つながり、助け合い、貢献しよう" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/kab/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/kab/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..47c0a3c4 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/kab/LC_MESSAGES/django 3.po @@ -0,0 +1,1211 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-09-27 22:40+0200\n" +"PO-Revision-Date: 2019-11-05 00:38+0000\n" +"Last-Translator: Ramiro Morales\n" +"Language-Team: Kabyle (http://www.transifex.com/django/django/language/" +"kab/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: kab\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Afrikaans" +msgstr "Tafrikanst" + +msgid "Arabic" +msgstr "Taɛṛabt" + +msgid "Asturian" +msgstr "Tasturyant" + +msgid "Azerbaijani" +msgstr "Tazeṛbayǧant" + +msgid "Bulgarian" +msgstr "Tabulgarit" + +msgid "Belarusian" +msgstr "Tabilurusit" + +msgid "Bengali" +msgstr "Tabelgalit" + +msgid "Breton" +msgstr "Tabrutunt" + +msgid "Bosnian" +msgstr "Tabusnit" + +msgid "Catalan" +msgstr "Takaṭalant" + +msgid "Czech" +msgstr "Tačikit" + +msgid "Welsh" +msgstr "Takusit" + +msgid "Danish" +msgstr "Tadanit" + +msgid "German" +msgstr "Talmanit" + +msgid "Lower Sorbian" +msgstr "Tasiṛbit n wadda" + +msgid "Greek" +msgstr "Tagrigit" + +msgid "English" +msgstr "Taglizit" + +msgid "Australian English" +msgstr "Taglizit n Ustralya" + +msgid "British English" +msgstr "Taglizit (UK)" + +msgid "Esperanto" +msgstr "Taspirantit" + +msgid "Spanish" +msgstr "Taspanit" + +msgid "Argentinian Spanish" +msgstr "Taspanit n Arjuntin" + +msgid "Colombian Spanish" +msgstr "Taspanit n Kulumbya" + +msgid "Mexican Spanish" +msgstr "Taspanit n Miksik" + +msgid "Nicaraguan Spanish" +msgstr "Taspanit n Nikaragwa" + +msgid "Venezuelan Spanish" +msgstr "Taspanit n Vinizwila" + +msgid "Estonian" +msgstr "Tastunit" + +msgid "Basque" +msgstr "Tabaskit" + +msgid "Persian" +msgstr "Tafarsit" + +msgid "Finnish" +msgstr "Tafinit" + +msgid "French" +msgstr "Tafṛansist" + +msgid "Frisian" +msgstr "" + +msgid "Irish" +msgstr "" + +msgid "Scottish Gaelic" +msgstr "" + +msgid "Galician" +msgstr "" + +msgid "Hebrew" +msgstr "" + +msgid "Hindi" +msgstr "Tahendit" + +msgid "Croatian" +msgstr "Takarwasit" + +msgid "Upper Sorbian" +msgstr "" + +msgid "Hungarian" +msgstr "Tahungarit" + +msgid "Armenian" +msgstr "" + +msgid "Interlingua" +msgstr "" + +msgid "Indonesian" +msgstr "Tandunizit" + +msgid "Ido" +msgstr "" + +msgid "Icelandic" +msgstr "Taslandit" + +msgid "Italian" +msgstr "Taṭelyanit" + +msgid "Japanese" +msgstr "" + +msgid "Georgian" +msgstr "Tajyuṛjit" + +msgid "Kabyle" +msgstr "" + +msgid "Kazakh" +msgstr "Takazaxt" + +msgid "Khmer" +msgstr "" + +msgid "Kannada" +msgstr "Takannadat" + +msgid "Korean" +msgstr "Takurit" + +msgid "Luxembourgish" +msgstr "" + +msgid "Lithuanian" +msgstr "Talitwanit" + +msgid "Latvian" +msgstr "Talitunit" + +msgid "Macedonian" +msgstr "Tamasidunit" + +msgid "Malayalam" +msgstr "Tamayalamt" + +msgid "Mongolian" +msgstr "" + +msgid "Marathi" +msgstr "" + +msgid "Burmese" +msgstr "Tabirmanit" + +msgid "Norwegian Bokmål" +msgstr "" + +msgid "Nepali" +msgstr "Tanipalit" + +msgid "Dutch" +msgstr "Tahulandit" + +msgid "Norwegian Nynorsk" +msgstr "" + +msgid "Ossetic" +msgstr "" + +msgid "Punjabi" +msgstr "Tabenjabit" + +msgid "Polish" +msgstr "Tapulandit" + +msgid "Portuguese" +msgstr "Tapurtugit" + +msgid "Brazilian Portuguese" +msgstr "" + +msgid "Romanian" +msgstr "Tarumanit" + +msgid "Russian" +msgstr "Tarusit" + +msgid "Slovak" +msgstr "Tasluvakt" + +msgid "Slovenian" +msgstr "" + +msgid "Albanian" +msgstr "Talbanit" + +msgid "Serbian" +msgstr "Tasiṛbit" + +msgid "Serbian Latin" +msgstr "" + +msgid "Swedish" +msgstr "Taswidit" + +msgid "Swahili" +msgstr "Taswahilit" + +msgid "Tamil" +msgstr "Taṭamult" + +msgid "Telugu" +msgstr "" + +msgid "Thai" +msgstr "" + +msgid "Turkish" +msgstr "Taṭurkit" + +msgid "Tatar" +msgstr "" + +msgid "Udmurt" +msgstr "" + +msgid "Ukrainian" +msgstr "" + +msgid "Urdu" +msgstr "" + +msgid "Uzbek" +msgstr "" + +msgid "Vietnamese" +msgstr "" + +msgid "Simplified Chinese" +msgstr "" + +msgid "Traditional Chinese" +msgstr "" + +msgid "Messages" +msgstr "Iznan" + +msgid "Site Maps" +msgstr "" + +msgid "Static Files" +msgstr "" + +msgid "Syndication" +msgstr "" + +msgid "That page number is not an integer" +msgstr "" + +msgid "That page number is less than 1" +msgstr "" + +msgid "That page contains no results" +msgstr "" + +msgid "Enter a valid value." +msgstr "Sekcem azal ameɣtu." + +msgid "Enter a valid URL." +msgstr "" + +msgid "Enter a valid integer." +msgstr "" + +msgid "Enter a valid email address." +msgstr "Sekcem tansa imayl tameɣtut." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" + +msgid "Enter a valid IPv4 address." +msgstr "Sekcem tansa IPv4 tameɣtut." + +msgid "Enter a valid IPv6 address." +msgstr "" + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "" + +msgid "Enter only digits separated by commas." +msgstr "" + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "" + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "" + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" + +msgid "Enter a number." +msgstr "Sekcem amḍan." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" + +msgid "Null characters are not allowed." +msgstr "" + +msgid "and" +msgstr "akked" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "" + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "" + +msgid "This field cannot be null." +msgstr "" + +msgid "This field cannot be blank." +msgstr "" + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "" + +#. Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. +#. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "" + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "" + +msgid "Boolean (Either True or False)" +msgstr "" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "" + +msgid "Comma-separated integers" +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" + +msgid "Date (without time)" +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" + +msgid "Date (with time)" +msgstr "Azemz (s wakud)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "" + +msgid "Decimal number" +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" + +msgid "Duration" +msgstr "Tanzagt" + +msgid "Email address" +msgstr "Tansa email" + +msgid "File path" +msgstr "Abrid n ufaylu" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "" + +msgid "Floating point number" +msgstr "" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "" + +msgid "Integer" +msgstr "Ummid" + +msgid "Big (8 byte) integer" +msgstr "" + +msgid "IPv4 address" +msgstr "" + +msgid "IP address" +msgstr "Tansa IP" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "" + +msgid "Boolean (Either True, False or None)" +msgstr "" + +msgid "Positive integer" +msgstr "" + +msgid "Positive small integer" +msgstr "" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "" + +msgid "Small integer" +msgstr "" + +msgid "Text" +msgstr "Aḍris" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" + +msgid "Time" +msgstr "Akud" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "" + +msgid "Universally unique identifier" +msgstr "" + +msgid "File" +msgstr "Afaylu" + +msgid "Image" +msgstr "Tugna" + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "" + +msgid "Foreign Key (type determined by related field)" +msgstr "" + +msgid "One-to-one relationship" +msgstr "" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "" + +msgid "Many-to-many relationship" +msgstr "" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "" + +msgid "Enter a whole number." +msgstr "Sekcem amḍan ummid." + +msgid "Enter a valid date." +msgstr "" + +msgid "Enter a valid time." +msgstr "" + +msgid "Enter a valid date/time." +msgstr "" + +msgid "Enter a valid duration." +msgstr "" + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "" + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" + +msgid "No file was submitted." +msgstr "Afaylu ur yettwazen ara." + +msgid "The submitted file is empty." +msgstr "" + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +msgstr[1] "" + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" + +msgid "Enter a list of values." +msgstr "" + +msgid "Enter a complete value." +msgstr "Sekcem azal ummid." + +msgid "Enter a valid UUID." +msgstr "" + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "" + +msgid "ManagementForm data is missing or has been tampered with" +msgstr "" + +#, python-format +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "" +msgstr[1] "" + +msgid "Order" +msgstr "Amizwer" + +msgid "Delete" +msgstr "KKES" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "" + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" + +msgid "Please correct the duplicate values below." +msgstr "" + +msgid "The inline value did not match the parent instance." +msgstr "" + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "" + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" + +msgid "Clear" +msgstr "Sfeḍ" + +msgid "Currently" +msgstr "Tura" + +msgid "Change" +msgstr "Beddel" + +msgid "Unknown" +msgstr "Arussin" + +msgid "Yes" +msgstr "Ih" + +msgid "No" +msgstr "Uhu" + +msgid "Year" +msgstr "" + +msgid "Month" +msgstr "" + +msgid "Day" +msgstr "" + +msgid "yes,no,maybe" +msgstr "" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%s KB" +msgstr "%s KAṬ" + +#, python-format +msgid "%s MB" +msgstr "%s MAṬ" + +#, python-format +msgid "%s GB" +msgstr "%s GAṬ" + +#, python-format +msgid "%s TB" +msgstr "%s TAṬ" + +#, python-format +msgid "%s PB" +msgstr "%s PAṬ" + +msgid "p.m." +msgstr "m.d." + +msgid "a.m." +msgstr "f.t." + +msgid "PM" +msgstr "MD" + +msgid "AM" +msgstr "FT" + +msgid "midnight" +msgstr "ttnaṣfa n yiḍ" + +msgid "noon" +msgstr "ttnaṣfa n uzal" + +msgid "Monday" +msgstr "Arim" + +msgid "Tuesday" +msgstr "Aram" + +msgid "Wednesday" +msgstr "Ahad" + +msgid "Thursday" +msgstr "Amhad" + +msgid "Friday" +msgstr "Sem" + +msgid "Saturday" +msgstr "Sed" + +msgid "Sunday" +msgstr "Acer" + +msgid "Mon" +msgstr "Ari" + +msgid "Tue" +msgstr "Ara" + +msgid "Wed" +msgstr "Aha" + +msgid "Thu" +msgstr "Amh" + +msgid "Fri" +msgstr "Sem" + +msgid "Sat" +msgstr "Sed" + +msgid "Sun" +msgstr "Ace" + +msgid "January" +msgstr "Yennayer" + +msgid "February" +msgstr "Fuṛaṛ" + +msgid "March" +msgstr "Meɣres" + +msgid "April" +msgstr "Yebrir" + +msgid "May" +msgstr "Mayyu" + +msgid "June" +msgstr "Yunyu" + +msgid "July" +msgstr "Yulyu" + +msgid "August" +msgstr "Ɣuct" + +msgid "September" +msgstr "Ctamber" + +msgid "October" +msgstr "Tuber" + +msgid "November" +msgstr "Wamber" + +msgid "December" +msgstr "Dujamber" + +msgid "jan" +msgstr "yen" + +msgid "feb" +msgstr "fuṛ" + +msgid "mar" +msgstr "meɣ" + +msgid "apr" +msgstr "yeb" + +msgid "may" +msgstr "may" + +msgid "jun" +msgstr "yun" + +msgid "jul" +msgstr "yul" + +msgid "aug" +msgstr "ɣuc" + +msgid "sep" +msgstr "cte" + +msgid "oct" +msgstr "tub" + +msgid "nov" +msgstr "wam" + +msgid "dec" +msgstr "duj" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "Yen." + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "Fuṛ." + +msgctxt "abbrev. month" +msgid "March" +msgstr "Meɣres" + +msgctxt "abbrev. month" +msgid "April" +msgstr "Yebrir" + +msgctxt "abbrev. month" +msgid "May" +msgstr "Mayyu" + +msgctxt "abbrev. month" +msgid "June" +msgstr "Yunyu" + +msgctxt "abbrev. month" +msgid "July" +msgstr "Yulyu" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "Ɣuc." + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "" + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "Tub." + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "Wam." + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "Duj." + +msgctxt "alt. month" +msgid "January" +msgstr "Yennayer" + +msgctxt "alt. month" +msgid "February" +msgstr "Fuṛaṛ" + +msgctxt "alt. month" +msgid "March" +msgstr "Meɣres" + +msgctxt "alt. month" +msgid "April" +msgstr "Yebrir" + +msgctxt "alt. month" +msgid "May" +msgstr "Mayyu" + +msgctxt "alt. month" +msgid "June" +msgstr "Yunyu" + +msgctxt "alt. month" +msgid "July" +msgstr "Yulyu" + +msgctxt "alt. month" +msgid "August" +msgstr "Ɣuct" + +msgctxt "alt. month" +msgid "September" +msgstr "Ctamber" + +msgctxt "alt. month" +msgid "October" +msgstr "Tuber" + +msgctxt "alt. month" +msgid "November" +msgstr "Wamber" + +msgctxt "alt. month" +msgid "December" +msgstr "Dujamber" + +msgid "This is not a valid IPv6 address." +msgstr "" + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "" + +msgid "or" +msgstr "neɣ" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "" +msgstr[1] "" + +msgid "0 minutes" +msgstr "0 n tisdatin" + +msgid "Forbidden" +msgstr "Yegdel" + +msgid "CSRF verification failed. Request aborted." +msgstr "" + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your Web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" + +msgid "More information is available with DEBUG=True." +msgstr "" + +msgid "No year specified" +msgstr "" + +msgid "Date out of range" +msgstr "" + +msgid "No month specified" +msgstr "" + +msgid "No day specified" +msgstr "" + +msgid "No week specified" +msgstr "" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" + +msgid "Directory indexes are not allowed here." +msgstr "" + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "" + +#, python-format +msgid "Index of %(directory)s" +msgstr "" + +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" + +msgid "The install worked successfully! Congratulations!" +msgstr "" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" + +msgid "Django Documentation" +msgstr "" + +msgid "Topics, references, & how-to’s" +msgstr "" + +msgid "Tutorial: A Polling App" +msgstr "" + +msgid "Get started with Django" +msgstr "Bdu s Django" + +msgid "Django Community" +msgstr "" + +msgid "Connect, get help, or contribute" +msgstr "" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/km/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/km/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..ac8d5249 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/km/LC_MESSAGES/django 3.po @@ -0,0 +1,1196 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Jannis Leidel , 2011 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-09-27 22:40+0200\n" +"PO-Revision-Date: 2019-11-05 00:38+0000\n" +"Last-Translator: Ramiro Morales\n" +"Language-Team: Khmer (http://www.transifex.com/django/django/language/km/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: km\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +msgid "Afrikaans" +msgstr "" + +msgid "Arabic" +msgstr "ភាសាអារ៉ាប់" + +msgid "Asturian" +msgstr "" + +msgid "Azerbaijani" +msgstr "" + +msgid "Bulgarian" +msgstr "" + +msgid "Belarusian" +msgstr "" + +msgid "Bengali" +msgstr "ភាសាបេឡារុស្ស" + +msgid "Breton" +msgstr "" + +msgid "Bosnian" +msgstr "" + +msgid "Catalan" +msgstr "" + +msgid "Czech" +msgstr "ភាសាឆេក" + +msgid "Welsh" +msgstr "ភាសាអ៊ុយក្រែន" + +msgid "Danish" +msgstr "ភាសាដាណឺម៉ាក" + +msgid "German" +msgstr "ភាសាអាល្លឺម៉ង់" + +msgid "Lower Sorbian" +msgstr "" + +msgid "Greek" +msgstr "ភាសាហ្កែលិគ" + +msgid "English" +msgstr "ភាសាអង់គ្លេស" + +msgid "Australian English" +msgstr "" + +msgid "British English" +msgstr "" + +msgid "Esperanto" +msgstr "" + +msgid "Spanish" +msgstr "ភាសាអេស្ប៉ាញ" + +msgid "Argentinian Spanish" +msgstr "" + +msgid "Colombian Spanish" +msgstr "" + +msgid "Mexican Spanish" +msgstr "" + +msgid "Nicaraguan Spanish" +msgstr "" + +msgid "Venezuelan Spanish" +msgstr "" + +msgid "Estonian" +msgstr "" + +msgid "Basque" +msgstr "" + +msgid "Persian" +msgstr "" + +msgid "Finnish" +msgstr "ភាសាហ្វាំងឡង់" + +msgid "French" +msgstr "ភាសាបារាំង" + +msgid "Frisian" +msgstr "" + +msgid "Irish" +msgstr "" + +msgid "Scottish Gaelic" +msgstr "" + +msgid "Galician" +msgstr "ភាសាហ្កែលិគ" + +msgid "Hebrew" +msgstr "ភាសាហេប្រិ" + +msgid "Hindi" +msgstr "" + +msgid "Croatian" +msgstr "" + +msgid "Upper Sorbian" +msgstr "" + +msgid "Hungarian" +msgstr "ភាសាហុងគ្រី" + +msgid "Armenian" +msgstr "" + +msgid "Interlingua" +msgstr "" + +msgid "Indonesian" +msgstr "" + +msgid "Ido" +msgstr "" + +msgid "Icelandic" +msgstr "ភាសាអ៉ីស្លង់" + +msgid "Italian" +msgstr "ភាសាអ៊ីតាលី" + +msgid "Japanese" +msgstr "ភាសាជប៉ុន" + +msgid "Georgian" +msgstr "" + +msgid "Kabyle" +msgstr "" + +msgid "Kazakh" +msgstr "" + +msgid "Khmer" +msgstr "" + +msgid "Kannada" +msgstr "" + +msgid "Korean" +msgstr "" + +msgid "Luxembourgish" +msgstr "" + +msgid "Lithuanian" +msgstr "" + +msgid "Latvian" +msgstr "" + +msgid "Macedonian" +msgstr "" + +msgid "Malayalam" +msgstr "" + +msgid "Mongolian" +msgstr "" + +msgid "Marathi" +msgstr "" + +msgid "Burmese" +msgstr "" + +msgid "Norwegian Bokmål" +msgstr "" + +msgid "Nepali" +msgstr "" + +msgid "Dutch" +msgstr "ភាសាហ្វាំងឡង់" + +msgid "Norwegian Nynorsk" +msgstr "" + +msgid "Ossetic" +msgstr "" + +msgid "Punjabi" +msgstr "" + +msgid "Polish" +msgstr "" + +msgid "Portuguese" +msgstr "" + +msgid "Brazilian Portuguese" +msgstr "" + +msgid "Romanian" +msgstr "ភាសារូម៉ានី" + +msgid "Russian" +msgstr "ភាសាรัរូស្ស៉ី" + +msgid "Slovak" +msgstr "ភាសាស្លូវ៉ាគី" + +msgid "Slovenian" +msgstr "ភាសាស្លូវ៉ានី" + +msgid "Albanian" +msgstr "" + +msgid "Serbian" +msgstr "" + +msgid "Serbian Latin" +msgstr "" + +msgid "Swedish" +msgstr "ភាសាស៊ុយអែដ" + +msgid "Swahili" +msgstr "" + +msgid "Tamil" +msgstr "ភាសាតាមីល" + +msgid "Telugu" +msgstr "" + +msgid "Thai" +msgstr "" + +msgid "Turkish" +msgstr "ភាសាទួរគី" + +msgid "Tatar" +msgstr "" + +msgid "Udmurt" +msgstr "" + +msgid "Ukrainian" +msgstr "ភាសាអ៊ុយក្រែន" + +msgid "Urdu" +msgstr "" + +msgid "Uzbek" +msgstr "" + +msgid "Vietnamese" +msgstr "" + +msgid "Simplified Chinese" +msgstr "ភាសាចិនសាមញ្ញ" + +msgid "Traditional Chinese" +msgstr "ភាសាចិនបុរាណ" + +msgid "Messages" +msgstr "" + +msgid "Site Maps" +msgstr "" + +msgid "Static Files" +msgstr "" + +msgid "Syndication" +msgstr "" + +msgid "That page number is not an integer" +msgstr "" + +msgid "That page number is less than 1" +msgstr "" + +msgid "That page contains no results" +msgstr "" + +msgid "Enter a valid value." +msgstr "" + +msgid "Enter a valid URL." +msgstr "" + +msgid "Enter a valid integer." +msgstr "" + +msgid "Enter a valid email address." +msgstr "" + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" + +msgid "Enter a valid IPv4 address." +msgstr "" + +msgid "Enter a valid IPv6 address." +msgstr "" + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "" + +msgid "Enter only digits separated by commas." +msgstr "បំពេញតែលេខហើយផ្តាច់ចេញពីគ្នាដោយសញ្ញាក្បៀស។" + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "" + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "" + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" + +msgid "Enter a number." +msgstr "" + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "" + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "" + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" + +msgid "Null characters are not allowed." +msgstr "" + +msgid "and" +msgstr "និង" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "" + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "" + +msgid "This field cannot be null." +msgstr "ចាំបាច់បំពេញទិន្នន័យកន្លែងនេះ។" + +msgid "This field cannot be blank." +msgstr "" + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "" + +#. Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. +#. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "" + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "" + +msgid "Boolean (Either True or False)" +msgstr "Boolean (អាច​ជា True រឺ False)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "" + +msgid "Comma-separated integers" +msgstr "ចំនួនពិត(Integer) ដែលផ្តាច់ចេញពីគ្នាដោយ​ក្បៀស" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" + +msgid "Date (without time)" +msgstr "កាល​បរិច្ឆេទ (Date) (មិនមានសរសេរម៉ោង)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" + +msgid "Date (with time)" +msgstr "កាល​បរិច្ឆេទ (Date) (មានសរសេរម៉ោង)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "" + +msgid "Decimal number" +msgstr "ចំនួនទសភាគ (Decimal)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" + +msgid "Duration" +msgstr "" + +msgid "Email address" +msgstr "" + +msgid "File path" +msgstr "ផ្លូវទៅកាន់ឯកសារ" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "" + +msgid "Floating point number" +msgstr "" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "" + +msgid "Integer" +msgstr "ចំនួនពិត(Integer)" + +msgid "Big (8 byte) integer" +msgstr "" + +msgid "IPv4 address" +msgstr "" + +msgid "IP address" +msgstr "លេខ IP" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "" + +msgid "Boolean (Either True, False or None)" +msgstr "Boolean (អាចជា True​ រឺ False រឺ None)" + +msgid "Positive integer" +msgstr "" + +msgid "Positive small integer" +msgstr "" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "" + +msgid "Small integer" +msgstr "" + +msgid "Text" +msgstr "អត្ថបទ" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" + +msgid "Time" +msgstr "ពេលវេលា" + +msgid "URL" +msgstr "អាស័យដ្ឋានគេហទំព័រ(URL)" + +msgid "Raw binary data" +msgstr "" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "" + +msgid "Universally unique identifier" +msgstr "" + +msgid "File" +msgstr "" + +msgid "Image" +msgstr "" + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "" + +msgid "Foreign Key (type determined by related field)" +msgstr "" + +msgid "One-to-one relationship" +msgstr "" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "" + +msgid "Many-to-many relationship" +msgstr "" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr "" + +msgid "This field is required." +msgstr "ចាំបាច់បំពេញទិន្នន័យកន្លែងនេះ។" + +msgid "Enter a whole number." +msgstr "បំពេញចំនួនទាំងអស់។" + +msgid "Enter a valid date." +msgstr "" + +msgid "Enter a valid time." +msgstr "" + +msgid "Enter a valid date/time." +msgstr "" + +msgid "Enter a valid duration." +msgstr "" + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "" + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "មិនមានឯកសារត្រូវបានជ្រើសរើស។ សូមពិនិត្យប្រភេទឯកសារម្តងទៀត។" + +msgid "No file was submitted." +msgstr "" + +msgid "The submitted file is empty." +msgstr "ពុំមានឯកសារ។​" + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "រូបភាពដែលទាញយកមិនត្រឹមត្រូវ ប្រហែលជាមិនមែនជារូបភាព ឬក៏ជា រូបភាពខូច។" + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" + +msgid "Enter a list of values." +msgstr "" + +msgid "Enter a complete value." +msgstr "" + +msgid "Enter a valid UUID." +msgstr "" + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr "" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "" + +msgid "ManagementForm data is missing or has been tampered with" +msgstr "" + +#, python-format +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "" + +#, python-format +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "" + +msgid "Order" +msgstr "" + +msgid "Delete" +msgstr "លប់" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "" + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" + +msgid "Please correct the duplicate values below." +msgstr "" + +msgid "The inline value did not match the parent instance." +msgstr "" + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "" + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" + +msgid "Clear" +msgstr "" + +msgid "Currently" +msgstr "" + +msgid "Change" +msgstr "ផ្លាស់ប្តូរ" + +msgid "Unknown" +msgstr "មិន​ដឹង" + +msgid "Yes" +msgstr "យល់ព្រម" + +msgid "No" +msgstr "មិនយល់ព្រម" + +msgid "Year" +msgstr "" + +msgid "Month" +msgstr "" + +msgid "Day" +msgstr "" + +msgid "yes,no,maybe" +msgstr "យល់ព្រម មិនយល់ព្រម​ ប្រហែល" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "" + +#, python-format +msgid "%s KB" +msgstr "" + +#, python-format +msgid "%s MB" +msgstr "" + +#, python-format +msgid "%s GB" +msgstr "" + +#, python-format +msgid "%s TB" +msgstr "" + +#, python-format +msgid "%s PB" +msgstr "" + +msgid "p.m." +msgstr "" + +msgid "a.m." +msgstr "" + +msgid "PM" +msgstr "" + +msgid "AM" +msgstr "" + +msgid "midnight" +msgstr "" + +msgid "noon" +msgstr "" + +msgid "Monday" +msgstr "ច័ន្ទ" + +msgid "Tuesday" +msgstr "អង្គារ" + +msgid "Wednesday" +msgstr "ពុធ" + +msgid "Thursday" +msgstr "ព្រហស្បតិ៍" + +msgid "Friday" +msgstr "សុក្រ" + +msgid "Saturday" +msgstr "សៅរ៍" + +msgid "Sunday" +msgstr "អាទិត្យ" + +msgid "Mon" +msgstr "" + +msgid "Tue" +msgstr "" + +msgid "Wed" +msgstr "" + +msgid "Thu" +msgstr "" + +msgid "Fri" +msgstr "" + +msgid "Sat" +msgstr "" + +msgid "Sun" +msgstr "" + +msgid "January" +msgstr "មករា" + +msgid "February" +msgstr "កុម្ភៈ" + +msgid "March" +msgstr "មិនា" + +msgid "April" +msgstr "មេសា" + +msgid "May" +msgstr "ឧសភា" + +msgid "June" +msgstr "មិថុនា" + +msgid "July" +msgstr "កក្កដា" + +msgid "August" +msgstr "សីហា" + +msgid "September" +msgstr "កញ្ញា" + +msgid "October" +msgstr "តុលា" + +msgid "November" +msgstr "វិច្ឆិកា" + +msgid "December" +msgstr "ធ្នូ" + +msgid "jan" +msgstr "មករា" + +msgid "feb" +msgstr "កុម្ភះ" + +msgid "mar" +msgstr "មិនា" + +msgid "apr" +msgstr "មេសា" + +msgid "may" +msgstr "ឧសភា" + +msgid "jun" +msgstr "មិថុនា" + +msgid "jul" +msgstr "កក្កដា" + +msgid "aug" +msgstr "សីហា" + +msgid "sep" +msgstr "កញ្ញា" + +msgid "oct" +msgstr "តុលា" + +msgid "nov" +msgstr "វិច្ឆិកា" + +msgid "dec" +msgstr "ធ្នូ" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "" + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "" + +msgctxt "abbrev. month" +msgid "March" +msgstr "មិនា" + +msgctxt "abbrev. month" +msgid "April" +msgstr "មេសា" + +msgctxt "abbrev. month" +msgid "May" +msgstr "ឧសភា" + +msgctxt "abbrev. month" +msgid "June" +msgstr "មិថុនា" + +msgctxt "abbrev. month" +msgid "July" +msgstr "កក្កដា" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "" + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "" + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "" + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "" + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "" + +msgctxt "alt. month" +msgid "January" +msgstr "មករា" + +msgctxt "alt. month" +msgid "February" +msgstr "កុម្ភៈ" + +msgctxt "alt. month" +msgid "March" +msgstr "មិនា" + +msgctxt "alt. month" +msgid "April" +msgstr "មេសា" + +msgctxt "alt. month" +msgid "May" +msgstr "ឧសភា" + +msgctxt "alt. month" +msgid "June" +msgstr "មិថុនា" + +msgctxt "alt. month" +msgid "July" +msgstr "កក្កដា" + +msgctxt "alt. month" +msgid "August" +msgstr "សីហា" + +msgctxt "alt. month" +msgid "September" +msgstr "កញ្ញា" + +msgctxt "alt. month" +msgid "October" +msgstr "តុលា" + +msgctxt "alt. month" +msgid "November" +msgstr "វិច្ឆិកា" + +msgctxt "alt. month" +msgid "December" +msgstr "ធ្នូ" + +msgid "This is not a valid IPv6 address." +msgstr "" + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "" + +msgid "or" +msgstr "" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr "" + +#, python-format +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "" + +#, python-format +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "" + +#, python-format +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "" + +#, python-format +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "" + +#, python-format +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "" + +#, python-format +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "" + +msgid "0 minutes" +msgstr "" + +msgid "Forbidden" +msgstr "" + +msgid "CSRF verification failed. Request aborted." +msgstr "" + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your Web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" + +msgid "More information is available with DEBUG=True." +msgstr "" + +msgid "No year specified" +msgstr "" + +msgid "Date out of range" +msgstr "" + +msgid "No month specified" +msgstr "" + +msgid "No day specified" +msgstr "" + +msgid "No week specified" +msgstr "" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" + +msgid "Directory indexes are not allowed here." +msgstr "" + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "" + +#, python-format +msgid "Index of %(directory)s" +msgstr "" + +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" + +msgid "The install worked successfully! Congratulations!" +msgstr "" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" + +msgid "Django Documentation" +msgstr "" + +msgid "Topics, references, & how-to’s" +msgstr "" + +msgid "Tutorial: A Polling App" +msgstr "" + +msgid "Get started with Django" +msgstr "" + +msgid "Django Community" +msgstr "" + +msgid "Connect, get help, or contribute" +msgstr "" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/ko/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/ko/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..8b72b53c --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/ko/LC_MESSAGES/django 3.po @@ -0,0 +1,1305 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# BJ Jang , 2014 +# JunGu Kang , 2017 +# Jiyoon, Ha , 2016 +# DONGHO JEONG , 2020 +# Park Hyunwoo , 2017 +# Geonho Kim / Leo Kim , 2019 +# hoseung2 , 2017 +# Ian Y. Choi , 2015 +# Jaehong Kim , 2011 +# Jannis Leidel , 2011 +# Jay Oh , 2020 +# Le Tartuffe , 2014,2016 +# Jonghwa Seo , 2019 +# Jubeen Lee , 2020 +# JuneHyeon Bae , 2014 +# JunGu Kang , 2015 +# JunGu Kang , 2019 +# Kagami Sascha Rosylight , 2017 +# Mariusz Felisiak , 2021 +# Seho Noh , 2018 +# Subin Choi , 2016 +# Taesik Yoon , 2015 +# 정훈 이, 2021 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:23-0500\n" +"PO-Revision-Date: 2021-12-25 06:49+0000\n" +"Last-Translator: 정훈 이\n" +"Language-Team: Korean (http://www.transifex.com/django/django/language/ko/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ko\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +msgid "Afrikaans" +msgstr "아프리칸스어" + +msgid "Arabic" +msgstr "아랍어" + +msgid "Algerian Arabic" +msgstr "알제리 아랍어" + +msgid "Asturian" +msgstr "호주어" + +msgid "Azerbaijani" +msgstr "아제르바이잔어" + +msgid "Bulgarian" +msgstr "불가리어" + +msgid "Belarusian" +msgstr "벨라루스어" + +msgid "Bengali" +msgstr "방글라데시어" + +msgid "Breton" +msgstr "브르타뉴어" + +msgid "Bosnian" +msgstr "보스니아어" + +msgid "Catalan" +msgstr "카탈로니아어" + +msgid "Czech" +msgstr "체코어" + +msgid "Welsh" +msgstr "웨일즈어" + +msgid "Danish" +msgstr "덴마크어" + +msgid "German" +msgstr "독일어" + +msgid "Lower Sorbian" +msgstr "저지 소르브어" + +msgid "Greek" +msgstr "그리스어" + +msgid "English" +msgstr "영어" + +msgid "Australian English" +msgstr "영어(호주)" + +msgid "British English" +msgstr "영어 (영국)" + +msgid "Esperanto" +msgstr "에스페란토어" + +msgid "Spanish" +msgstr "스페인어" + +msgid "Argentinian Spanish" +msgstr "아르헨티나 스페인어" + +msgid "Colombian Spanish" +msgstr "콜롬비아 스페인어" + +msgid "Mexican Spanish" +msgstr "멕시컨 스페인어" + +msgid "Nicaraguan Spanish" +msgstr "니카과라 스페인어" + +msgid "Venezuelan Spanish" +msgstr "베네수엘라 스페인어" + +msgid "Estonian" +msgstr "에스토니아어" + +msgid "Basque" +msgstr "바스크어" + +msgid "Persian" +msgstr "페르시아어" + +msgid "Finnish" +msgstr "핀란드어" + +msgid "French" +msgstr "프랑스어" + +msgid "Frisian" +msgstr "프리슬란트어" + +msgid "Irish" +msgstr "아일랜드어" + +msgid "Scottish Gaelic" +msgstr "스코틀랜드 게일어" + +msgid "Galician" +msgstr "갈리시아어" + +msgid "Hebrew" +msgstr "히브리어" + +msgid "Hindi" +msgstr "힌두어" + +msgid "Croatian" +msgstr "크로아티아어" + +msgid "Upper Sorbian" +msgstr "고지 소르브어" + +msgid "Hungarian" +msgstr "헝가리어" + +msgid "Armenian" +msgstr "아르메니아어" + +msgid "Interlingua" +msgstr "인테르링구아어" + +msgid "Indonesian" +msgstr "인도네시아어" + +msgid "Igbo" +msgstr "이그보어" + +msgid "Ido" +msgstr "이도어" + +msgid "Icelandic" +msgstr "아이슬란드어" + +msgid "Italian" +msgstr "이탈리아어" + +msgid "Japanese" +msgstr "일본어" + +msgid "Georgian" +msgstr "조지아어" + +msgid "Kabyle" +msgstr "커바일어" + +msgid "Kazakh" +msgstr "카자흐어" + +msgid "Khmer" +msgstr "크메르어" + +msgid "Kannada" +msgstr "칸나다어" + +msgid "Korean" +msgstr "한국어" + +msgid "Kyrgyz" +msgstr "키르키즈 공화국어" + +msgid "Luxembourgish" +msgstr "룩셈부르크" + +msgid "Lithuanian" +msgstr "리투아니아어" + +msgid "Latvian" +msgstr "라트비아어" + +msgid "Macedonian" +msgstr "마케도니아어" + +msgid "Malayalam" +msgstr "말라얄람어" + +msgid "Mongolian" +msgstr "몽고어" + +msgid "Marathi" +msgstr "마라티어" + +msgid "Malay" +msgstr "말레이시아어" + +msgid "Burmese" +msgstr "룩셈부르크어" + +msgid "Norwegian Bokmål" +msgstr "노르웨이어(보크몰)" + +msgid "Nepali" +msgstr "네팔어" + +msgid "Dutch" +msgstr "네덜란드어" + +msgid "Norwegian Nynorsk" +msgstr "노르웨이어 (뉘노르스크)" + +msgid "Ossetic" +msgstr "오세티아어" + +msgid "Punjabi" +msgstr "펀자브어" + +msgid "Polish" +msgstr "폴란드어" + +msgid "Portuguese" +msgstr "포르투갈어" + +msgid "Brazilian Portuguese" +msgstr "브라질 포르투갈어" + +msgid "Romanian" +msgstr "루마니아어" + +msgid "Russian" +msgstr "러시아어" + +msgid "Slovak" +msgstr "슬로바키아어" + +msgid "Slovenian" +msgstr "슬로베니아어" + +msgid "Albanian" +msgstr "알바니아어" + +msgid "Serbian" +msgstr "세르비아어" + +msgid "Serbian Latin" +msgstr "세르비아어" + +msgid "Swedish" +msgstr "스웨덴어" + +msgid "Swahili" +msgstr "스와힐리어" + +msgid "Tamil" +msgstr "타밀어" + +msgid "Telugu" +msgstr "텔루구어" + +msgid "Tajik" +msgstr "타지크어" + +msgid "Thai" +msgstr "태국어" + +msgid "Turkmen" +msgstr "튀르크멘어" + +msgid "Turkish" +msgstr "터키어" + +msgid "Tatar" +msgstr "타타르" + +msgid "Udmurt" +msgstr "이제프스크" + +msgid "Ukrainian" +msgstr "우크라이나어" + +msgid "Urdu" +msgstr "우르드어" + +msgid "Uzbek" +msgstr "우즈베크어" + +msgid "Vietnamese" +msgstr "베트남어" + +msgid "Simplified Chinese" +msgstr "중국어 간체" + +msgid "Traditional Chinese" +msgstr "중국어 번체" + +msgid "Messages" +msgstr "메시지" + +msgid "Site Maps" +msgstr "사이트 맵" + +msgid "Static Files" +msgstr "정적 파일" + +msgid "Syndication" +msgstr "신디케이션" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "..." + +msgid "That page number is not an integer" +msgstr "페이지 번호가 정수가 아닙니다." + +msgid "That page number is less than 1" +msgstr "페이지 번호가 1보다 작습니다." + +msgid "That page contains no results" +msgstr "해당 페이지에 결과가 없습니다." + +msgid "Enter a valid value." +msgstr "올바른 값을 입력하세요." + +msgid "Enter a valid URL." +msgstr "올바른 URL을 입력하세요." + +msgid "Enter a valid integer." +msgstr "올바른 정수를 입력하세요." + +msgid "Enter a valid email address." +msgstr "올바른 이메일 주소를 입력하세요." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "문자, 숫자, '_', '-'만 가능합니다." + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"유니코드 문자, 숫자, 언더스코어 또는 하이픈으로 구성된 올바른 내용을 입력하세" +"요." + +msgid "Enter a valid IPv4 address." +msgstr "올바른 IPv4 주소를 입력하세요." + +msgid "Enter a valid IPv6 address." +msgstr "올바른 IPv6 주소를 입력하세요." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "올바른 IPv4 혹은 IPv6 주소를 입력하세요." + +msgid "Enter only digits separated by commas." +msgstr "콤마로 구분된 숫자만 입력하세요." + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" +"%(limit_value)s 안의 값을 입력해 주세요. (입력하신 값은 %(show_value)s입니" +"다.)" + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "%(limit_value)s 이하의 값을 입력해 주세요." + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "%(limit_value)s 이상의 값을 입력해 주세요." + +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "" + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"이 값이 최소 %(limit_value)d 개의 글자인지 확인하세요(입력값 %(show_value)d " +"자)." + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"이 값이 최대 %(limit_value)d 개의 글자인지 확인하세요(입력값 %(show_value)d " +"자)." + +msgid "Enter a number." +msgstr "숫자를 입력하세요." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "전체 자릿수가 %(max)s 개를 넘지 않도록 해주세요." + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "전체 유효자리 개수가 %(max)s 개를 넘지 않도록 해주세요." + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "전체 유효자리 개수가 %(max)s 개를 넘지 않도록 해주세요." + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"파일 확장자 '%(extension)s'는 허용되지 않습니다. 허용된 확장자: " +"'%(allowed_extensions)s'." + +msgid "Null characters are not allowed." +msgstr "null 문자는 사용할 수 없습니다. " + +msgid "and" +msgstr "또한" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "%(model_name)s의 %(field_labels)s 은/는 이미 존재합니다." + +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "" + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "%(value)r 은/는 올바른 선택사항이 아닙니다." + +msgid "This field cannot be null." +msgstr "이 필드는 null 값을 사용할 수 없습니다. " + +msgid "This field cannot be blank." +msgstr "이 필드는 빈 칸으로 둘 수 없습니다." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "%(model_name)s의 %(field_label)s은/는 이미 존재합니다." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"%(field_label)s은/는 반드시 %(date_field_label)s %(lookup_type)s에 대해 유일" +"해야 합니다." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "%(field_type)s 형식 필드" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "'%(value)s' 값은 반드시 True 또는 False 중 하나여야만 합니다." + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "'%(value)s'값은 반드시 True, False, None 중 하나여야만 합니다." + +msgid "Boolean (Either True or False)" +msgstr "boolean(True 또는 False)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "문자열(%(max_length)s 글자까지)" + +msgid "Comma-separated integers" +msgstr "정수(콤마로 구분)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "'%(value)s' 값은 날짜 형식이 아닙니다. YYYY-MM-DD 형식이어야 합니다." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" +"'%(value)s' 값은 올바른 형식(YYYY-MM-DD)이지만 유효하지 않은 날짜입니다." + +msgid "Date (without time)" +msgstr "날짜(시간 제외)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"'%(value)s' 값은 올바르지 않은 형식입니다. YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ] 형식이어야 합니다." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"'%(value)s' 값은 올바른 형식이지만 (YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) 유효" +"하지 않은 날짜/시간입니다." + +msgid "Date (with time)" +msgstr "날짜(시간 포함)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "'%(value)s' 값은 10진수를 입력하여야 합니다." + +msgid "Decimal number" +msgstr "10진수" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" +"'%(value)s' 값은 올바르지 않은 형식입니다. [DD] [HH:[MM:]]ss[.uuuuuu] 형식이" +"어야 합니다." + +msgid "Duration" +msgstr "지속시간" + +msgid "Email address" +msgstr "이메일 주소" + +msgid "File path" +msgstr "파일 경로" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "\"%(value)s\" 값은 실수를 입력하여야 합니다." + +msgid "Floating point number" +msgstr "부동소수점 숫자" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "\"%(value)s\" 값은 정수를 입력하여야 합니다." + +msgid "Integer" +msgstr "정수" + +msgid "Big (8 byte) integer" +msgstr "큰 정수 (8 byte)" + +msgid "Small integer" +msgstr "작은 정수" + +msgid "IPv4 address" +msgstr "IPv4 주소" + +msgid "IP address" +msgstr "IP 주소" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "\"%(value)s\" 값은 반드시 None, True 또는 False이어야 합니다." + +msgid "Boolean (Either True, False or None)" +msgstr "boolean (True, False 또는 None)" + +msgid "Positive big integer" +msgstr "큰 양의 정수" + +msgid "Positive integer" +msgstr "양의 정수" + +msgid "Positive small integer" +msgstr "작은 양의 정수" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "슬러그(%(max_length)s 까지)" + +msgid "Text" +msgstr "텍스트" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" +"\"%(value)s\" 값의 형식이 올바르지 않습니다. HH:MM[:ss[.uuuuuu]] 형식이어야 " +"합니다." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" +"\"%(value)s\" 값이 올바른 형식(HH:MM[:ss[.uuuuuu]])이나, 유효하지 않은 시간 " +"값입니다." + +msgid "Time" +msgstr "시각" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "Raw binary data" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "\"%(value)s\"은 유효하지 않은 UUID입니다." + +msgid "Universally unique identifier" +msgstr "범용 고유 식별 수단(UUID)" + +msgid "File" +msgstr "파일" + +msgid "Image" +msgstr "이미지" + +msgid "A JSON object" +msgstr "JSON 객체" + +msgid "Value must be valid JSON." +msgstr "올바른 JSON 형식이여야 합니다." + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "%(field)s %(value)r 를 가지는 %(model)s 인스턴스가 존재하지 않습니다." + +msgid "Foreign Key (type determined by related field)" +msgstr "외래 키 (연관 필드에 의해 형식 결정)" + +msgid "One-to-one relationship" +msgstr "일대일 관계" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "%(from)s-%(to)s 관계" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "%(from)s-%(to)s 관계들" + +msgid "Many-to-many relationship" +msgstr "다대다 관계" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "필수 항목입니다." + +msgid "Enter a whole number." +msgstr "정수를 입력하세요." + +msgid "Enter a valid date." +msgstr "올바른 날짜를 입력하세요." + +msgid "Enter a valid time." +msgstr "올바른 시각을 입력하세요." + +msgid "Enter a valid date/time." +msgstr "올바른 날짜/시각을 입력하세요." + +msgid "Enter a valid duration." +msgstr "올바른 기간을 입력하세요." + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "날짜는 {min_days}와 {max_days} 사이여야 합니다." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "등록된 파일이 없습니다. 인코딩 형식을 확인하세요." + +msgid "No file was submitted." +msgstr "파일이 전송되지 않았습니다." + +msgid "The submitted file is empty." +msgstr "입력하신 파일은 빈 파일입니다." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "파일이름의 길이가 최대 %(max)d 자인지 확인하세요(%(length)d 자)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" +"파일 업로드 또는 삭제 체크박스를 선택하세요. 동시에 둘 다 할 수는 없습니다." + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"올바른 이미지를 업로드하세요. 업로드하신 파일은 이미지 파일이 아니거나 파일" +"이 깨져 있습니다." + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "올바르게 선택해 주세요. %(value)s 이/가 선택가능항목에 없습니다." + +msgid "Enter a list of values." +msgstr "리스트를 입력하세요." + +msgid "Enter a complete value." +msgstr "완전한 값을 입력하세요." + +msgid "Enter a valid UUID." +msgstr "올바른 UUID를 입력하세요." + +msgid "Enter a valid JSON." +msgstr "올바른 JSON을 입력하세요." + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(%(name)s hidden 필드) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" +"ManagementForm 데이터가 없거나 변경되었습니다. 현재 없는 필드: " +"%(field_names)s. 이런 이슈가 지속된다면 버그 리포트를 제출해주시기 바랍니다." + +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "" + +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "" + +msgid "Order" +msgstr "순서:" + +msgid "Delete" +msgstr "삭제" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "%(field)s의 중복된 데이터를 고쳐주세요." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "%(field)s의 중복된 데이터를 고쳐주세요. 유일한 값이어야 합니다." + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"%(field_name)s의 값은 %(date_field)s의 %(lookup)s에 대해 유일해야 합니다. 중" +"복된 데이터를 고쳐주세요." + +msgid "Please correct the duplicate values below." +msgstr "아래의 중복된 값들을 고쳐주세요." + +msgid "The inline value did not match the parent instance." +msgstr "Inline 값이 부모 인스턴스와 일치하지 않습니다." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "올바르게 선택해 주세요. 선택하신 것이 선택가능항목에 없습니다." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "\"%(pk)s\" 은/는 유효한 값이 아닙니다." + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"%(datetime)s 은/는 %(current_timezone)s 시간대에서 해석될 수 없습니다; 정보" +"가 모호하거나 존재하지 않을 수 있습니다." + +msgid "Clear" +msgstr "취소" + +msgid "Currently" +msgstr "현재" + +msgid "Change" +msgstr "변경" + +msgid "Unknown" +msgstr "알 수 없습니다." + +msgid "Yes" +msgstr "예" + +msgid "No" +msgstr "아니오" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "예,아니오,아마도" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d 바이트" + +#, python-format +msgid "%s KB" +msgstr "%s KB" + +#, python-format +msgid "%s MB" +msgstr "%s MB" + +#, python-format +msgid "%s GB" +msgstr "%s GB" + +#, python-format +msgid "%s TB" +msgstr "%s TB" + +#, python-format +msgid "%s PB" +msgstr "%s PB" + +msgid "p.m." +msgstr "오후" + +msgid "a.m." +msgstr "오전" + +msgid "PM" +msgstr "오후" + +msgid "AM" +msgstr "오전" + +msgid "midnight" +msgstr "자정" + +msgid "noon" +msgstr "정오" + +msgid "Monday" +msgstr "월요일" + +msgid "Tuesday" +msgstr "화요일" + +msgid "Wednesday" +msgstr "수요일" + +msgid "Thursday" +msgstr "목요일" + +msgid "Friday" +msgstr "금요일" + +msgid "Saturday" +msgstr "토요일" + +msgid "Sunday" +msgstr "일요일" + +msgid "Mon" +msgstr "월요일" + +msgid "Tue" +msgstr "화요일" + +msgid "Wed" +msgstr "수요일" + +msgid "Thu" +msgstr "목요일" + +msgid "Fri" +msgstr "금요일" + +msgid "Sat" +msgstr "토요일" + +msgid "Sun" +msgstr "일요일" + +msgid "January" +msgstr "1월" + +msgid "February" +msgstr "2월" + +msgid "March" +msgstr "3월" + +msgid "April" +msgstr "4월" + +msgid "May" +msgstr "5월" + +msgid "June" +msgstr "6월" + +msgid "July" +msgstr "7월" + +msgid "August" +msgstr "8월" + +msgid "September" +msgstr "9월" + +msgid "October" +msgstr "10월" + +msgid "November" +msgstr "11월" + +msgid "December" +msgstr "12월" + +msgid "jan" +msgstr "1월" + +msgid "feb" +msgstr "2월" + +msgid "mar" +msgstr "3월" + +msgid "apr" +msgstr "4월" + +msgid "may" +msgstr "5월" + +msgid "jun" +msgstr "6월" + +msgid "jul" +msgstr "7월" + +msgid "aug" +msgstr "8월" + +msgid "sep" +msgstr "9월" + +msgid "oct" +msgstr "10월" + +msgid "nov" +msgstr "11월" + +msgid "dec" +msgstr "12월" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "1월" + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "2월" + +msgctxt "abbrev. month" +msgid "March" +msgstr "3월" + +msgctxt "abbrev. month" +msgid "April" +msgstr "4월" + +msgctxt "abbrev. month" +msgid "May" +msgstr "5월" + +msgctxt "abbrev. month" +msgid "June" +msgstr "6월" + +msgctxt "abbrev. month" +msgid "July" +msgstr "7월" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "8월" + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "9월" + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "10월" + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "11월" + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "12월" + +msgctxt "alt. month" +msgid "January" +msgstr "1월" + +msgctxt "alt. month" +msgid "February" +msgstr "2월" + +msgctxt "alt. month" +msgid "March" +msgstr "3월" + +msgctxt "alt. month" +msgid "April" +msgstr "4월" + +msgctxt "alt. month" +msgid "May" +msgstr "5월" + +msgctxt "alt. month" +msgid "June" +msgstr "6월" + +msgctxt "alt. month" +msgid "July" +msgstr "7월" + +msgctxt "alt. month" +msgid "August" +msgstr "8월" + +msgctxt "alt. month" +msgid "September" +msgstr "9월" + +msgctxt "alt. month" +msgid "October" +msgstr "10월" + +msgctxt "alt. month" +msgid "November" +msgstr "11월" + +msgctxt "alt. month" +msgid "December" +msgstr "12월" + +msgid "This is not a valid IPv6 address." +msgstr "올바른 IPv6 주소가 아닙니다." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "또는" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "%(num)d년" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "%(num)d개월" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "%(num)d주" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "%(num)d일" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "%(num)d시간" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "%(num)d분" + +msgid "Forbidden" +msgstr "Forbidden" + +msgid "CSRF verification failed. Request aborted." +msgstr "CSRF 검증에 실패했습니다. 요청을 중단하였습니다." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" +"이 메세지가 보이는 이유는 이 HTTPS 사이트가 당신의 웹 브라우저로부터 \"참조 " +"헤더\"를 요구하지만, 아무것도 받기 못하였기 때문입니다. 이 헤더는 보안상의 이" +"유로 필요하며, 당신의 웹 브라우저가 제3자에 의해 해킹당하고 있지 않다는 것을 " +"보장하기 위함입니다." + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"만약 브라우저 설정에서 '참조' 헤더를 비활성화 시켰을 경우, 적어도 이 사이트" +"나 HTTPS 연결, '동일-출처' 요청에 대해서는 이를 다시 활성화 시키십시오. " + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"태그나 'Referrer-Policy: no-" +"referrer' 헤더를 포함하고 있다면, 제거해주시기 바랍니다. CSRF 방지를 위한 리" +"퍼러 검사를 위해 'Referer' 헤더가 필요합니다. 개인 정보에 대해 우려가 있는 경" +"우, 서드 파티 사이트에 대한 링크에 와 같은 대안을 사" +"용할 수 있습니다." + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"이 메세지가 보이는 이유는 사이트가 폼을 제출할 때 CSRF 쿠키를 필요로 하기 때" +"문입니다. 이 쿠키는 보안상의 이유로 필요하며, 제3자에 의해 당신의 브라우저가 " +"해킹당하고 있지 않다는 것을 보장합니다." + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" +"만약 브라우저 설정에서 쿠키를 비활성화 시켰을 경우, 적어도 이 사이트나 '동일-" +"출처' 요청에 대해서는 활성화 시키십시오." + +msgid "More information is available with DEBUG=True." +msgstr "DEBUG=True 로 더 많은 정보를 확인할 수 있습니다." + +msgid "No year specified" +msgstr "년도가 없습니다." + +msgid "Date out of range" +msgstr "유효 범위 밖의 날짜" + +msgid "No month specified" +msgstr "월이 없습니다." + +msgid "No day specified" +msgstr "날짜가 없습니다." + +msgid "No week specified" +msgstr "주가 없습니다." + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr " %(verbose_name_plural)s를 사용할 수 없습니다." + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"Future 모듈 %(verbose_name_plural)s을 사용할 수 없습니다. %(class_name)s." +"allow_future가 False 입니다." + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "날짜 문자열 '%(datestr)s'이 표준 형식 '%(format)s'과 다릅니다." + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "쿼리 결과에 %(verbose_name)s가 없습니다." + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "'마지막' 페이지가 아니거나, 정수형으로 변환할 수 없습니다." + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Invalid page (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "빈 리스트이고 '%(class_name)s.allow_empty'가 False입니다." + +msgid "Directory indexes are not allowed here." +msgstr "디렉토리 인덱스는 이곳에 사용할 수 없습니다." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "\"%(path)s\" 이/가 존재하지 않습니다." + +#, python-format +msgid "Index of %(directory)s" +msgstr "Index of %(directory)s" + +msgid "The install worked successfully! Congratulations!" +msgstr "성공적으로 설치되었습니다! 축하합니다!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"Django %(version)s릴리스 노트 보기" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" +"이 페이지는 어떤 URL도 지정되지 않았고, settings 파일에 DEBUG=True가 설정되어 있을 때 표시됩니다." + +msgid "Django Documentation" +msgstr "Django 문서" + +msgid "Topics, references, & how-to’s" +msgstr "주제, 레퍼런스, & 입문참조하다" + +msgid "Tutorial: A Polling App" +msgstr "튜토리얼: 폴링 애플리케이션" + +msgid "Get started with Django" +msgstr "Django와 함께 시작하기" + +msgid "Django Community" +msgstr "Django 커뮤니티" + +msgid "Connect, get help, or contribute" +msgstr "연결하고, 도움을 받거나 기여하기" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/lb/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/lb/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..673533d4 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/lb/LC_MESSAGES/django 3.po @@ -0,0 +1,1213 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# sim0n , 2011,2013 +# sim0n , 2013 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-09-27 22:40+0200\n" +"PO-Revision-Date: 2019-11-05 00:38+0000\n" +"Last-Translator: Ramiro Morales\n" +"Language-Team: Luxembourgish (http://www.transifex.com/django/django/" +"language/lb/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: lb\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Afrikaans" +msgstr "Afrikaans" + +msgid "Arabic" +msgstr "Arabesch" + +msgid "Asturian" +msgstr "" + +msgid "Azerbaijani" +msgstr "" + +msgid "Bulgarian" +msgstr "Bulgaresch" + +msgid "Belarusian" +msgstr "Wäissrussesch" + +msgid "Bengali" +msgstr "Bengalesch" + +msgid "Breton" +msgstr "" + +msgid "Bosnian" +msgstr "Bosnesch" + +msgid "Catalan" +msgstr "Katalanesch" + +msgid "Czech" +msgstr "Tschechesch" + +msgid "Welsh" +msgstr "Walisesch" + +msgid "Danish" +msgstr "Dänesch" + +msgid "German" +msgstr "Däitsch" + +msgid "Lower Sorbian" +msgstr "" + +msgid "Greek" +msgstr "Griichesch" + +msgid "English" +msgstr "Englesch" + +msgid "Australian English" +msgstr "" + +msgid "British English" +msgstr "Britesch Englesch" + +msgid "Esperanto" +msgstr "" + +msgid "Spanish" +msgstr "Spuenesch" + +msgid "Argentinian Spanish" +msgstr "Argentinesch Spuenesch" + +msgid "Colombian Spanish" +msgstr "" + +msgid "Mexican Spanish" +msgstr "Mexikanesch Spuenesch" + +msgid "Nicaraguan Spanish" +msgstr "" + +msgid "Venezuelan Spanish" +msgstr "" + +msgid "Estonian" +msgstr "Estonesch" + +msgid "Basque" +msgstr "Baskesch" + +msgid "Persian" +msgstr "Persesch" + +msgid "Finnish" +msgstr "Finnesch" + +msgid "French" +msgstr "Franséisch" + +msgid "Frisian" +msgstr "Frisesch" + +msgid "Irish" +msgstr "Iresch" + +msgid "Scottish Gaelic" +msgstr "" + +msgid "Galician" +msgstr "Galesch" + +msgid "Hebrew" +msgstr "Hebräesch" + +msgid "Hindi" +msgstr "Hindi" + +msgid "Croatian" +msgstr "Kroatesch" + +msgid "Upper Sorbian" +msgstr "" + +msgid "Hungarian" +msgstr "Ungaresch" + +msgid "Armenian" +msgstr "" + +msgid "Interlingua" +msgstr "" + +msgid "Indonesian" +msgstr "Indonesesch" + +msgid "Ido" +msgstr "" + +msgid "Icelandic" +msgstr "Islännesch" + +msgid "Italian" +msgstr "Italienesch" + +msgid "Japanese" +msgstr "Japanesch" + +msgid "Georgian" +msgstr "Georgesch" + +msgid "Kabyle" +msgstr "" + +msgid "Kazakh" +msgstr "" + +msgid "Khmer" +msgstr "Khmer" + +msgid "Kannada" +msgstr "Kanadesch" + +msgid "Korean" +msgstr "Koreanesch" + +msgid "Luxembourgish" +msgstr "Lëtzebuergesch" + +msgid "Lithuanian" +msgstr "Lithuanesesch" + +msgid "Latvian" +msgstr "Lättesch" + +msgid "Macedonian" +msgstr "Macedonesch" + +msgid "Malayalam" +msgstr "Malayalam" + +msgid "Mongolian" +msgstr "Mongolesch" + +msgid "Marathi" +msgstr "" + +msgid "Burmese" +msgstr "" + +msgid "Norwegian Bokmål" +msgstr "" + +msgid "Nepali" +msgstr "" + +msgid "Dutch" +msgstr "Hollännesch" + +msgid "Norwegian Nynorsk" +msgstr "Norwegesch Nynorsk" + +msgid "Ossetic" +msgstr "" + +msgid "Punjabi" +msgstr "Punjabi" + +msgid "Polish" +msgstr "Polnesch" + +msgid "Portuguese" +msgstr "Portugisesch" + +msgid "Brazilian Portuguese" +msgstr "Brasilianesch Portugisesch" + +msgid "Romanian" +msgstr "Rumänesch" + +msgid "Russian" +msgstr "Russesch" + +msgid "Slovak" +msgstr "Slowakesch" + +msgid "Slovenian" +msgstr "Slowenesch" + +msgid "Albanian" +msgstr "Albanesch" + +msgid "Serbian" +msgstr "Serbesch" + +msgid "Serbian Latin" +msgstr "Serbesch Latäinesch" + +msgid "Swedish" +msgstr "Schwedesch" + +msgid "Swahili" +msgstr "" + +msgid "Tamil" +msgstr "Tamil" + +msgid "Telugu" +msgstr "Telugu" + +msgid "Thai" +msgstr "Thai" + +msgid "Turkish" +msgstr "Tierkesch" + +msgid "Tatar" +msgstr "" + +msgid "Udmurt" +msgstr "" + +msgid "Ukrainian" +msgstr "Ukrainesch" + +msgid "Urdu" +msgstr "" + +msgid "Uzbek" +msgstr "" + +msgid "Vietnamese" +msgstr "Vietnamesesch" + +msgid "Simplified Chinese" +msgstr "Einfach d'Chinesesch" + +msgid "Traditional Chinese" +msgstr "Traditionell d'Chinesesch" + +msgid "Messages" +msgstr "" + +msgid "Site Maps" +msgstr "" + +msgid "Static Files" +msgstr "" + +msgid "Syndication" +msgstr "" + +msgid "That page number is not an integer" +msgstr "" + +msgid "That page number is less than 1" +msgstr "" + +msgid "That page contains no results" +msgstr "" + +msgid "Enter a valid value." +msgstr "Gëff en validen Wärt an." + +msgid "Enter a valid URL." +msgstr "Gëff eng valid URL an." + +msgid "Enter a valid integer." +msgstr "" + +msgid "Enter a valid email address." +msgstr "Gëff eng valid e-mail Adress an." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" + +msgid "Enter a valid IPv4 address." +msgstr "Gëff eng valid IPv4 Adress an." + +msgid "Enter a valid IPv6 address." +msgstr "Gëff eng valid IPv6 Adress an." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "Gëff eng valid IPv4 oder IPv6 Adress an." + +msgid "Enter only digits separated by commas." +msgstr "" + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "" + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "" + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" + +msgid "Enter a number." +msgstr "" + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" + +msgid "Null characters are not allowed." +msgstr "" + +msgid "and" +msgstr "an" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "" + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "" + +msgid "This field cannot be null." +msgstr "" + +msgid "This field cannot be blank." +msgstr "" + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "" + +#. Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. +#. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "" + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "" + +msgid "Boolean (Either True or False)" +msgstr "" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "" + +msgid "Comma-separated integers" +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" + +msgid "Date (without time)" +msgstr "Datum (ouni Zäit)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" + +msgid "Date (with time)" +msgstr "Datum (mat Zäit)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "" + +msgid "Decimal number" +msgstr "Dezimalzuel" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" + +msgid "Duration" +msgstr "" + +msgid "Email address" +msgstr "E-mail Adress" + +msgid "File path" +msgstr "" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "" + +msgid "Floating point number" +msgstr "Kommazuel" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "" + +msgid "Integer" +msgstr "Zuel" + +msgid "Big (8 byte) integer" +msgstr "Grouss (8 byte) Zuel" + +msgid "IPv4 address" +msgstr "IPv4 Adress" + +msgid "IP address" +msgstr "IP Adress" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "" + +msgid "Boolean (Either True, False or None)" +msgstr "" + +msgid "Positive integer" +msgstr "Positiv Zuel" + +msgid "Positive small integer" +msgstr "Kleng positiv Zuel" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "" + +msgid "Small integer" +msgstr "Kleng Zuel" + +msgid "Text" +msgstr "Text" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" + +msgid "Time" +msgstr "Zäit" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "Rei Binär Daten" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "" + +msgid "Universally unique identifier" +msgstr "" + +msgid "File" +msgstr "Fichier" + +msgid "Image" +msgstr "Bild" + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "" + +msgid "Foreign Key (type determined by related field)" +msgstr "" + +msgid "One-to-one relationship" +msgstr "" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "" + +msgid "Many-to-many relationship" +msgstr "" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "" + +msgid "Enter a whole number." +msgstr "" + +msgid "Enter a valid date." +msgstr "" + +msgid "Enter a valid time." +msgstr "" + +msgid "Enter a valid date/time." +msgstr "" + +msgid "Enter a valid duration." +msgstr "" + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "" + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" + +msgid "No file was submitted." +msgstr "Et ass keng Datei geschéckt ginn." + +msgid "The submitted file is empty." +msgstr "" + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +msgstr[1] "" + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" + +msgid "Enter a list of values." +msgstr "Gëff eng Lescht vun Wäerter an." + +msgid "Enter a complete value." +msgstr "" + +msgid "Enter a valid UUID." +msgstr "" + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "" + +msgid "ManagementForm data is missing or has been tampered with" +msgstr "" + +#, python-format +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "" +msgstr[1] "" + +msgid "Order" +msgstr "Sortéier" + +msgid "Delete" +msgstr "Läsch" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "" + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" + +msgid "Please correct the duplicate values below." +msgstr "" + +msgid "The inline value did not match the parent instance." +msgstr "" + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "" + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" + +msgid "Clear" +msgstr "Maach eidel" + +msgid "Currently" +msgstr "Momentan" + +msgid "Change" +msgstr "Änner" + +msgid "Unknown" +msgstr "Onbekannt" + +msgid "Yes" +msgstr "Jo" + +msgid "No" +msgstr "Nee" + +msgid "Year" +msgstr "" + +msgid "Month" +msgstr "" + +msgid "Day" +msgstr "" + +msgid "yes,no,maybe" +msgstr "jo,nee,vläit" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%s KB" +msgstr "%s KB" + +#, python-format +msgid "%s MB" +msgstr "%s MB" + +#, python-format +msgid "%s GB" +msgstr "%s GB" + +#, python-format +msgid "%s TB" +msgstr "%s TB" + +#, python-format +msgid "%s PB" +msgstr "%s PB" + +msgid "p.m." +msgstr "p.m." + +msgid "a.m." +msgstr "a.m." + +msgid "PM" +msgstr "PM" + +msgid "AM" +msgstr "AM" + +msgid "midnight" +msgstr "" + +msgid "noon" +msgstr "" + +msgid "Monday" +msgstr "Méindeg" + +msgid "Tuesday" +msgstr "Dënschdeg" + +msgid "Wednesday" +msgstr "Mëttwoch" + +msgid "Thursday" +msgstr "Donneschdes" + +msgid "Friday" +msgstr "Freides" + +msgid "Saturday" +msgstr "Samschdes" + +msgid "Sunday" +msgstr "Sonndes" + +msgid "Mon" +msgstr "Mei" + +msgid "Tue" +msgstr "Dën" + +msgid "Wed" +msgstr "Mett" + +msgid "Thu" +msgstr "Don" + +msgid "Fri" +msgstr "Fre" + +msgid "Sat" +msgstr "Sam" + +msgid "Sun" +msgstr "Son" + +msgid "January" +msgstr "Januar" + +msgid "February" +msgstr "Februar" + +msgid "March" +msgstr "März" + +msgid "April" +msgstr "Abrell" + +msgid "May" +msgstr "" + +msgid "June" +msgstr "Juni" + +msgid "July" +msgstr "Juli" + +msgid "August" +msgstr "August" + +msgid "September" +msgstr "September" + +msgid "October" +msgstr "Oktober" + +msgid "November" +msgstr "November" + +msgid "December" +msgstr "Dezember" + +msgid "jan" +msgstr "jan" + +msgid "feb" +msgstr "feb" + +msgid "mar" +msgstr "mär" + +msgid "apr" +msgstr "abr" + +msgid "may" +msgstr "" + +msgid "jun" +msgstr "jun" + +msgid "jul" +msgstr "jul" + +msgid "aug" +msgstr "aug" + +msgid "sep" +msgstr "sep" + +msgid "oct" +msgstr "okt" + +msgid "nov" +msgstr "nov" + +msgid "dec" +msgstr "dec" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "Jan." + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "Feb." + +msgctxt "abbrev. month" +msgid "March" +msgstr "März" + +msgctxt "abbrev. month" +msgid "April" +msgstr "Abrell" + +msgctxt "abbrev. month" +msgid "May" +msgstr "" + +msgctxt "abbrev. month" +msgid "June" +msgstr "Juni" + +msgctxt "abbrev. month" +msgid "July" +msgstr "Juli" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "Aug." + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "Sept." + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "Okt." + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "Nov." + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "Dec." + +msgctxt "alt. month" +msgid "January" +msgstr "Januar" + +msgctxt "alt. month" +msgid "February" +msgstr "Februar" + +msgctxt "alt. month" +msgid "March" +msgstr "März" + +msgctxt "alt. month" +msgid "April" +msgstr "Abrell" + +msgctxt "alt. month" +msgid "May" +msgstr "" + +msgctxt "alt. month" +msgid "June" +msgstr "Juni" + +msgctxt "alt. month" +msgid "July" +msgstr "Juli" + +msgctxt "alt. month" +msgid "August" +msgstr "August" + +msgctxt "alt. month" +msgid "September" +msgstr "September" + +msgctxt "alt. month" +msgid "October" +msgstr "Oktober" + +msgctxt "alt. month" +msgid "November" +msgstr "November" + +msgctxt "alt. month" +msgid "December" +msgstr "December" + +msgid "This is not a valid IPv6 address." +msgstr "" + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "" + +msgid "or" +msgstr "oder" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d Joer" +msgstr[1] "%d Joren" + +#, python-format +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d Mount" +msgstr[1] "%d Meint" + +#, python-format +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d Woch" +msgstr[1] "%d Wochen" + +#, python-format +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d Dag" +msgstr[1] "%d Deeg" + +#, python-format +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d Stonn" +msgstr[1] "%d Stonnen" + +#, python-format +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d Minutt" +msgstr[1] "%d Minutten" + +msgid "0 minutes" +msgstr "0 Minutten" + +msgid "Forbidden" +msgstr "" + +msgid "CSRF verification failed. Request aborted." +msgstr "" + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your Web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" + +msgid "More information is available with DEBUG=True." +msgstr "" + +msgid "No year specified" +msgstr "" + +msgid "Date out of range" +msgstr "" + +msgid "No month specified" +msgstr "" + +msgid "No day specified" +msgstr "" + +msgid "No week specified" +msgstr "" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" + +msgid "Directory indexes are not allowed here." +msgstr "" + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "" + +#, python-format +msgid "Index of %(directory)s" +msgstr "" + +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" + +msgid "The install worked successfully! Congratulations!" +msgstr "" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" + +msgid "Django Documentation" +msgstr "" + +msgid "Topics, references, & how-to’s" +msgstr "" + +msgid "Tutorial: A Polling App" +msgstr "" + +msgid "Get started with Django" +msgstr "" + +msgid "Django Community" +msgstr "" + +msgid "Connect, get help, or contribute" +msgstr "" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/ml/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/ml/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..3694eac3 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/ml/LC_MESSAGES/django 3.po @@ -0,0 +1,1274 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# c1007a0b890405f1fbddfacebc4c6ef7, 2013 +# Claude Paroz , 2020 +# Hrishikesh , 2019-2020 +# Jannis Leidel , 2011 +# Jaseem KM , 2019 +# Jeffy , 2012 +# Jibin Mathew , 2019 +# Mariusz Felisiak , 2021 +# Rag sagar , 2016 +# Rajeesh Nair , 2011-2012 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-09-21 10:22+0200\n" +"PO-Revision-Date: 2021-11-24 16:29+0000\n" +"Last-Translator: Mariusz Felisiak \n" +"Language-Team: Malayalam (http://www.transifex.com/django/django/language/" +"ml/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ml\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Afrikaans" +msgstr "ആഫ്രിക്കാന്‍സ്" + +msgid "Arabic" +msgstr "അറബിൿ" + +msgid "Algerian Arabic" +msgstr "അൾഗേരിയൻ അറബിൿ" + +msgid "Asturian" +msgstr "ആസ്ടൂറിയൻ" + +msgid "Azerbaijani" +msgstr "അസര്‍ബൈജാനി" + +msgid "Bulgarian" +msgstr "ബള്‍ഗേറിയന്‍" + +msgid "Belarusian" +msgstr "ബെലറൂഷ്യന്‍" + +msgid "Bengali" +msgstr "ബംഗാളി" + +msgid "Breton" +msgstr "ബ്രെട്ടണ്‍" + +msgid "Bosnian" +msgstr "ബോസ്നിയന്‍" + +msgid "Catalan" +msgstr "കാറ്റലന്‍" + +msgid "Czech" +msgstr "ചെൿ" + +msgid "Welsh" +msgstr "വെല്‍ഷ്" + +msgid "Danish" +msgstr "ഡാനിഷ്" + +msgid "German" +msgstr "ജര്‍മന്‍" + +msgid "Lower Sorbian" +msgstr "ലോവർ സോർബിയൻ " + +msgid "Greek" +msgstr "ഗ്രീക്ക്" + +msgid "English" +msgstr "ഇംഗ്ലീഷ്" + +msgid "Australian English" +msgstr "ആസ്ട്രേലിയൻ ഇംഗ്ലീഷ്" + +msgid "British English" +msgstr "ബ്രിട്ടീഷ് ഇംഗ്ലീഷ്" + +msgid "Esperanto" +msgstr "എസ്പെരാന്റോ" + +msgid "Spanish" +msgstr "സ്പാനിഷ്" + +msgid "Argentinian Spanish" +msgstr "അര്‍ജന്റീനിയന്‍ സ്പാനിഷ്" + +msgid "Colombian Spanish" +msgstr "കൊളംബിയൻ സ്പാനിഷ്" + +msgid "Mexican Spanish" +msgstr "മെക്സിക്കന്‍ സ്പാനിഷ്" + +msgid "Nicaraguan Spanish" +msgstr "നിക്കരാഗ്വന്‍ സ്പാനിഷ്" + +msgid "Venezuelan Spanish" +msgstr "വെനിസ്വലന്‍ സ്പാനിഷ്" + +msgid "Estonian" +msgstr "എസ്ടോണിയന്‍ സ്പാനിഷ്" + +msgid "Basque" +msgstr "ബാസ്ക്യു" + +msgid "Persian" +msgstr "പേര്‍ഷ്യന്‍" + +msgid "Finnish" +msgstr "ഫിന്നിഷ്" + +msgid "French" +msgstr "ഫ്രെഞ്ച്" + +msgid "Frisian" +msgstr "ഫ്രിസിയന്‍" + +msgid "Irish" +msgstr "ഐറിഷ്" + +msgid "Scottish Gaelic" +msgstr "സ്കോട്ടിഷ് ഗൈലിൿ" + +msgid "Galician" +msgstr "ഗലിഷ്യന്‍" + +msgid "Hebrew" +msgstr "ഹീബ്രു" + +msgid "Hindi" +msgstr "ഹിന്ദി" + +msgid "Croatian" +msgstr "ക്രൊയേഷ്യന്‍" + +msgid "Upper Sorbian" +msgstr "അപ്പർ സോർബിയൻ " + +msgid "Hungarian" +msgstr "ഹംഗേറിയന്‍" + +msgid "Armenian" +msgstr "അർമേനിയൻ" + +msgid "Interlingua" +msgstr "ഇന്റര്‍ലിംഗ്വാ" + +msgid "Indonesian" +msgstr "ഇന്തൊനേഷ്യന്‍" + +msgid "Igbo" +msgstr "" + +msgid "Ido" +msgstr "ഈടോ" + +msgid "Icelandic" +msgstr "ഐസ്ലാന്‍ഡിൿ" + +msgid "Italian" +msgstr "ഇറ്റാലിയന്‍" + +msgid "Japanese" +msgstr "ജാപ്പനീസ്" + +msgid "Georgian" +msgstr "ജോര്‍ജിയന്‍" + +msgid "Kabyle" +msgstr "കാബയെൽ " + +msgid "Kazakh" +msgstr "കസാഖ്" + +msgid "Khmer" +msgstr "ഖ്മേര്‍" + +msgid "Kannada" +msgstr "കന്നഡ" + +msgid "Korean" +msgstr "കൊറിയന്‍" + +msgid "Kyrgyz" +msgstr "" + +msgid "Luxembourgish" +msgstr "ലക്സംബര്‍ഗിഷ് " + +msgid "Lithuanian" +msgstr "ലിത്വാനിയന്‍" + +msgid "Latvian" +msgstr "ലാറ്റ്വിയന്‍" + +msgid "Macedonian" +msgstr "മാസിഡോണിയന്‍" + +msgid "Malayalam" +msgstr "മലയാളം" + +msgid "Mongolian" +msgstr "മംഗോളിയന്‍" + +msgid "Marathi" +msgstr "മറാത്തി" + +msgid "Malay" +msgstr "" + +msgid "Burmese" +msgstr "ബര്‍മീസ്" + +msgid "Norwegian Bokmål" +msgstr "നോർവേജിയൻ ബുക്ക്മൊൾ" + +msgid "Nepali" +msgstr "നേപ്പാളി" + +msgid "Dutch" +msgstr "ഡച്ച്" + +msgid "Norwegian Nynorsk" +msgstr "നോര്‍വീജിയന്‍ നിനോഷ്ക്" + +msgid "Ossetic" +msgstr "ഒസ്സെറ്റിക്" + +msgid "Punjabi" +msgstr "പഞ്ചാബി" + +msgid "Polish" +msgstr "പോളിഷ്" + +msgid "Portuguese" +msgstr "പോര്‍ചുഗീസ്" + +msgid "Brazilian Portuguese" +msgstr "ബ്രസീലിയന്‍ പോര്‍ച്ചുഗീസ്" + +msgid "Romanian" +msgstr "റൊമാനിയന്‍" + +msgid "Russian" +msgstr "റഷ്യന്‍" + +msgid "Slovak" +msgstr "സ്ലൊവാൿ" + +msgid "Slovenian" +msgstr "സ്ളൊവേനിയന്‍" + +msgid "Albanian" +msgstr "അല്‍ബേനിയന്‍" + +msgid "Serbian" +msgstr "സെര്‍ബിയന്‍" + +msgid "Serbian Latin" +msgstr "സെര്‍ബിയന്‍ ലാറ്റിന്‍" + +msgid "Swedish" +msgstr "സ്വീഡിഷ്" + +msgid "Swahili" +msgstr "സ്വാഹിലി" + +msgid "Tamil" +msgstr "തമിഴ്" + +msgid "Telugu" +msgstr "തെലുങ്ക്" + +msgid "Tajik" +msgstr "" + +msgid "Thai" +msgstr "തായ്" + +msgid "Turkmen" +msgstr "" + +msgid "Turkish" +msgstr "ടര്‍ക്കിഷ്" + +msgid "Tatar" +msgstr "തൊതാര്‍" + +msgid "Udmurt" +msgstr "ഉദ്മര്‍ത്" + +msgid "Ukrainian" +msgstr "യുക്രേനിയന്‍" + +msgid "Urdu" +msgstr "ഉര്‍ദു" + +msgid "Uzbek" +msgstr "ഉസ്ബെൿ" + +msgid "Vietnamese" +msgstr "വിയറ്റ്നാമീസ്" + +msgid "Simplified Chinese" +msgstr "സിമ്പ്ലിഫൈഡ് ചൈനീസ്" + +msgid "Traditional Chinese" +msgstr "പരമ്പരാഗത ചൈനീസ്" + +msgid "Messages" +msgstr "സന്ദേശങ്ങൾ" + +msgid "Site Maps" +msgstr "സൈറ്റ് മാപ്പുകൾ" + +msgid "Static Files" +msgstr " സ്റ്റാറ്റിൿ ഫയലുകൾ" + +msgid "Syndication" +msgstr "വിതരണം " + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "" + +msgid "That page number is not an integer" +msgstr "ആ പേജ് നമ്പർ ഒരു ഇന്റിജറല്ല" + +msgid "That page number is less than 1" +msgstr "ആ പേജ് നമ്പർ 1 നെ കാൾ ചെറുതാണ് " + +msgid "That page contains no results" +msgstr "ആ പേജിൽ റിസൾട്ടുകൾ ഒന്നും ഇല്ല " + +msgid "Enter a valid value." +msgstr "ശരിയായ വാല്യു നൽകുക." + +msgid "Enter a valid URL." +msgstr "ശരിയായ URL നല്‍കുക" + +msgid "Enter a valid integer." +msgstr "ശരിയായ ഇന്റിജർ നൽകുക." + +msgid "Enter a valid email address." +msgstr "ശരിയായ ഇമെയില്‍ വിലാസം നല്‍കുക." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" +"അക്ഷരങ്ങള്‍, അക്കങ്ങള്‍, അണ്ടര്‍സ്കോര്‍, ഹൈഫന്‍ എന്നിവ മാത്രം അടങ്ങിയ ശരിയായ ഒരു 'സ്ലഗ്ഗ്' നൽകുക. " + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"യൂണികോഡ് അക്ഷരങ്ങൾ, അക്കങ്ങൾ, ഹൈഫണുകൾ, അണ്ടർസ്കോറുക‌‌ൾ എന്നിവമാത്രം അടങ്ങിയ ശെരിയായ ‌ഒരു " +"“സ്ലഗ്” എഴുതുക ." + +msgid "Enter a valid IPv4 address." +msgstr "ശരിയായ IPv4 വിലാസം നൽകുക." + +msgid "Enter a valid IPv6 address." +msgstr "ശരിയായ ഒരു IPv6 വിലാസം നൽകുക." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "ശരിയായ ഒരു IPv4 വിലാസമോ IPv6 വിലാസമോ നൽകുക." + +msgid "Enter only digits separated by commas." +msgstr "കോമകൾ ഉപയോഗിച്ച് വേർതിരിച്ച രീതിയിലുള്ള അക്കങ്ങൾ മാത്രം നൽകുക." + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "ഇത് %(limit_value)s ആവണം. (ഇപ്പോള്‍ %(show_value)s)." + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "ഇത് %(limit_value)s-ഓ അതില്‍ കുറവോ ആവണം" + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "ഇത് %(limit_value)s-ഓ അതില്‍ കൂടുതലോ ആവണം" + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"ഈ വാല്യൂയിൽ %(limit_value)d ക്യാരക്ടർ എങ്കിലും ഉണ്ടെന്നു ഉറപ്പു വരുത്തുക(ഇതിൽ " +"%(show_value)d ഉണ്ട് )" +msgstr[1] "" +"ഈ വാല്യൂയിൽ %(limit_value)dക്യാരക്ടേർസ് എങ്കിലും ഉണ്ടെന്നു ഉറപ്പു വരുത്തുക(ഇതിൽ " +"%(show_value)d ഉണ്ട് )" + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"ഈ വാല്യൂയിൽ %(limit_value)d ക്യാരക്ടർ 1 ഇൽ കൂടുതൽ ഇല്ലെന്നു ഉറപ്പു വരുത്തുക(ഇതിൽ 2 " +"%(show_value)d ഉണ്ട് )" +msgstr[1] "" +"ഈ വാല്യൂയിൽ %(limit_value)d ക്യാരക്ടർസ് 1 ഇൽ കൂടുതൽ ഇല്ലെന്നു ഉറപ്പു വരുത്തുക(ഇതിൽ 2 " +"%(show_value)d ഉണ്ട് )" + +msgid "Enter a number." +msgstr "ഒരു സംഖ്യ നല്കുക." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "%(max)s ഡിജിറ്റിൽ കൂടുതൽ ഇല്ല എന്ന് ഉറപ്പു വരുത്തുക ." +msgstr[1] "%(max)sഡിജിറ്റ്സിൽ കൂടുതൽ ഇല്ല എന്ന് ഉറപ്പു വരുത്തുക. " + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "%(max)sകൂടുതൽ ഡെസിമൽ പോയന്റില്ല എന്ന് ഉറപ്പു വരുത്തുക. " +msgstr[1] "%(max)sകൂടുതൽ ഡെസിമൽ പോയിന്റുകളില്ല എന്ന് ഉറപ്പു വരുത്തുക. " + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "%(max)sഡിജിറ്റ് ഡെസിമൽ പോയിന്റിനു മുൻപ് ഇല്ല എന്ന് ഉറപ്പു വരുത്തുക." +msgstr[1] "%(max)sഡിജിറ്റ്സ് ഡെസിമൽ പോയിന്റിനു മുൻപ് ഇല്ല എന്ന് ഉറപ്പു വരുത്തുക. " + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"“%(extension)s” എന്ന ഫയൽ എക്സ്റ്റൻഷൻ അനുവദനീയമല്ല. അനുവദനീയമായ എക്സ്റ്റൻഷനുകൾ ഇവയാണ്: " +"%(allowed_extensions)s" + +msgid "Null characters are not allowed." +msgstr "Null ക്യാരക്ടറുകൾ അനുവദനീയമല്ല." + +msgid "and" +msgstr "പിന്നെ" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "%(field_labels)sഉള്ള %(model_name)sനിലവിലുണ്ട്." + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "%(value)r എന്ന വാല്യൂ ശെരിയായ ചോയ്സ് അല്ല. " + +msgid "This field cannot be null." +msgstr "ഈ കളം (ഫീല്‍ഡ്) ഒഴിച്ചിടരുത്." + +msgid "This field cannot be blank." +msgstr "ഈ കളം (ഫീല്‍ഡ്) ഒഴിച്ചിടരുത്." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "%(field_label)s-ഓടു കൂടിയ %(model_name)s നിലവിലുണ്ട്." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. +#. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"%(date_field_label)s %(lookup_type)s-നു %(field_label)s ആവര്‍ത്തിക്കാന്‍ പാടില്ല." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "%(field_type)s എന്ന തരത്തിലുള്ള കളം (ഫീല്‍ഡ്)" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "“%(value)s” മൂല്യം ഒന്നുകിൽ True, False എന്നിവയിലേതെങ്കിലുമേ ആവാൻ പാടുള്ളൂ." + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "" +"“%(value)s” മൂല്യം ഒന്നുകിൽ True, False അല്ലെങ്കിൽ None എന്നിവയിലേതെങ്കിലുമേ ആവാൻ " +"പാടുള്ളൂ." + +msgid "Boolean (Either True or False)" +msgstr "ശരിയോ തെറ്റോ (True അഥവാ False)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "സ്ട്രിങ്ങ് (%(max_length)s വരെ നീളമുള്ളത്)" + +msgid "Comma-separated integers" +msgstr "കോമയിട്ട് വേര്‍തിരിച്ച സംഖ്യകള്‍" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" + +msgid "Date (without time)" +msgstr "തീയതി (സമയം വേണ്ട)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" + +msgid "Date (with time)" +msgstr "തീയതി (സമയത്തോടൊപ്പം)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "" + +msgid "Decimal number" +msgstr "ദശാംശസംഖ്യ" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" + +msgid "Duration" +msgstr "കാലയളവ്" + +msgid "Email address" +msgstr "ഇ-മെയില്‍ വിലാസം" + +msgid "File path" +msgstr "ഫയല്‍ സ്ഥാനം" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "" + +msgid "Floating point number" +msgstr "ദശാംശസംഖ്യ" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "" + +msgid "Integer" +msgstr "പൂര്‍ണ്ണസംഖ്യ" + +msgid "Big (8 byte) integer" +msgstr "8 ബൈറ്റ് പൂര്‍ണസംഖ്യ." + +msgid "Small integer" +msgstr "ഹ്രസ്വ പൂര്‍ണസംഖ്യ" + +msgid "IPv4 address" +msgstr "IPv4 വിലാസം" + +msgid "IP address" +msgstr "IP വിലാസം" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "" + +msgid "Boolean (Either True, False or None)" +msgstr "ശരിയോ തെറ്റോ എന്നു മാത്രം (True, False, None എന്നിവയില്‍ ഏതെങ്കിലും ഒന്ന്)" + +msgid "Positive big integer" +msgstr "" + +msgid "Positive integer" +msgstr "ധന പൂര്‍ണസംഖ്യ" + +msgid "Positive small integer" +msgstr "ധന ഹ്രസ്വ പൂര്‍ണസംഖ്യ" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "സ്ലഗ് (%(max_length)s വരെ)" + +msgid "Text" +msgstr "ടെക്സ്റ്റ്" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" + +msgid "Time" +msgstr "സമയം" + +msgid "URL" +msgstr "URL(വെബ്-വിലാസം)" + +msgid "Raw binary data" +msgstr "റോ ബൈനറി ഡാറ്റ" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "" + +msgid "Universally unique identifier" +msgstr "എല്ലായിടത്തും യുണീക്കായ ഐഡന്റിഫൈയർ." + +msgid "File" +msgstr "ഫയല്‍" + +msgid "Image" +msgstr "ചിത്രം" + +msgid "A JSON object" +msgstr "" + +msgid "Value must be valid JSON." +msgstr "" + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "%(field)s%(value)r ഉള്ള%(model)s ഇൻസ്റ്റൻസ് നിലവിൽ ഇല്ല." + +msgid "Foreign Key (type determined by related field)" +msgstr "ഫോറിന്‍ കീ (ടൈപ്പ് ബന്ധപ്പെട്ട ഫീല്‍ഡില്‍ നിന്നും നിര്‍ണ്ണയിക്കുന്നതാണ്)" + +msgid "One-to-one relationship" +msgstr "വണ്‍-ടു-വണ്‍ ബന്ധം" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "%(from)s-%(to)s റിലേഷൻഷിപ്‌." + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "%(from)s-%(to)sറിലേഷൻഷിപ്‌സ്. " + +msgid "Many-to-many relationship" +msgstr "മെനി-ടു-മെനി ബന്ധം" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "ഈ കള്ളി(ഫീല്‍ഡ്) നിര്‍ബന്ധമാണ്." + +msgid "Enter a whole number." +msgstr "ഒരു പൂര്‍ണസംഖ്യ നല്കുക." + +msgid "Enter a valid date." +msgstr "ശരിയായ തീയതി നല്കുക." + +msgid "Enter a valid time." +msgstr "ശരിയായ സമയം നല്കുക." + +msgid "Enter a valid date/time." +msgstr "ശരിയായ തീയതിയും സമയവും നല്കുക." + +msgid "Enter a valid duration." +msgstr "സാധുതയുള്ള കാലയളവ് നല്കുക." + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "ദിവസങ്ങളുടെ എണ്ണം {min_days}, {max_days} എന്നിവയുടെ ഇടയിലായിരിക്കണം." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "ഫയലൊന്നും ലഭിച്ചിട്ടില്ല. ഫോമിലെ എന്‍-കോഡിംഗ് പരിശോധിക്കുക." + +msgid "No file was submitted." +msgstr "ഫയലൊന്നും ലഭിച്ചിട്ടില്ല." + +msgid "The submitted file is empty." +msgstr "ലഭിച്ച ഫയല്‍ ശൂന്യമാണ്." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"ഈ ഫയൽ നെയ്മിൽ%(max)dക്യാരക്ടറിൽ കൂടുതലില്ല എന്ന് ഉറപ്പു വരുത്തുക (അതിൽ %(length)dഉണ്ട്) . " +msgstr[1] "" +"ഈ ഫയൽ നെയ്മിൽ%(max)dക്യാരക്ടേഴ്‌സിൽ കൂടുതലില്ല എന്ന് ഉറപ്പു വരുത്തുക (അതിൽ %(length)dഉണ്ട്)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" +"ഒന്നുകില്‍ ഫയല്‍ സമര്‍പ്പിക്കണം, അല്ലെങ്കില്‍ ക്ളിയര്‍ എന്ന ചെക്ബോക്സ് ടിക് ചെയ്യണം. ദയവായി രണ്ടും " +"കൂടി ചെയ്യരുത്." + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"ശരിയായ ചിത്രം അപ് ലോഡ് ചെയ്യുക. നിങ്ങള്‍ നല്കിയ ഫയല്‍ ഒന്നുകില്‍ ഒരു ചിത്രമല്ല, അല്ലെങ്കില്‍ " +"വികലമാണ്." + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "യോഗ്യമായത് തെരഞ്ഞെടുക്കുക. %(value)s ലഭ്യമായവയില്‍ ഉള്‍പ്പെടുന്നില്ല." + +msgid "Enter a list of values." +msgstr "മൂല്യങ്ങളുടെ പട്ടിക(ലിസ്റ്റ്) നല്കുക." + +msgid "Enter a complete value." +msgstr "പൂർണ്ണമായ വാല്യൂ നല്കുക." + +msgid "Enter a valid UUID." +msgstr "സാധുവായ യു യു ഐ ഡി നല്കുക." + +msgid "Enter a valid JSON." +msgstr "" + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(ഹിഡൻ ഫീൽഡ് %(name)s)%(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" + +#, python-format +msgid "Please submit at most %d form." +msgid_plural "Please submit at most %d forms." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "Please submit at least %d form." +msgid_plural "Please submit at least %d forms." +msgstr[0] "" +msgstr[1] "" + +msgid "Order" +msgstr "ക്രമം" + +msgid "Delete" +msgstr "ഡിലീറ്റ്" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "%(field)s-നായി നല്കുന്ന വിവരം ആവര്‍ത്തിച്ചത് ദയവായി തിരുത്തുക." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "%(field)s-നായി നല്കുന്ന വിവരം ആവര്‍ത്തിക്കാന്‍ പാടില്ല. ദയവായി തിരുത്തുക." + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"%(date_field)s ലെ %(lookup)s നു വേണ്ടി %(field_name)s നു നല്കുന്ന വിവരം ആവര്‍ത്തിക്കാന്‍ " +"പാടില്ല. ദയവായി തിരുത്തുക." + +msgid "Please correct the duplicate values below." +msgstr "താഴെ കൊടുത്തവയില്‍ ആവര്‍ത്തനം ഒഴിവാക്കുക." + +msgid "The inline value did not match the parent instance." +msgstr "ഇൻലൈൻ വാല്യൂ, പാരെന്റ് ഇൻസ്റ്റൻസുമായി ചേരുന്നില്ല." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "യോഗ്യമായത് തെരഞ്ഞെടുക്കുക. നിങ്ങള്‍ നല്കിയത് ലഭ്യമായവയില്‍ ഉള്‍പ്പെടുന്നില്ല." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "" + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" + +msgid "Clear" +msgstr "കാലിയാക്കുക" + +msgid "Currently" +msgstr "നിലവിലുള്ളത്" + +msgid "Change" +msgstr "മാറ്റുക" + +msgid "Unknown" +msgstr "അജ്ഞാതം" + +msgid "Yes" +msgstr "അതെ" + +msgid "No" +msgstr "അല്ല" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "ഉണ്ട്,ഇല്ല,ഉണ്ടായേക്കാം" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d ബൈറ്റ്" +msgstr[1] "%(size)d ബൈറ്റുകള്‍" + +#, python-format +msgid "%s KB" +msgstr "%s കെ.ബി" + +#, python-format +msgid "%s MB" +msgstr "%s എം.ബി" + +#, python-format +msgid "%s GB" +msgstr "%s ജി.ബി" + +#, python-format +msgid "%s TB" +msgstr "%s ടി.ബി" + +#, python-format +msgid "%s PB" +msgstr "%s പി.ബി" + +msgid "p.m." +msgstr "പി. എം (ഉച്ചയ്ക്കു ശേഷം) " + +msgid "a.m." +msgstr "എ. എം (ഉച്ചയ്ക്കു മുമ്പ്)" + +msgid "PM" +msgstr "പി. എം (ഉച്ചയ്ക്കു ശേഷം) " + +msgid "AM" +msgstr "എ. എം (ഉച്ചയ്ക്കു മുമ്പ്)" + +msgid "midnight" +msgstr "അര്‍ധരാത്രി" + +msgid "noon" +msgstr "ഉച്ച" + +msgid "Monday" +msgstr "തിങ്കളാഴ്ച" + +msgid "Tuesday" +msgstr "ചൊവ്വാഴ്ച" + +msgid "Wednesday" +msgstr "ബുധനാഴ്ച" + +msgid "Thursday" +msgstr "വ്യാഴാഴ്ച" + +msgid "Friday" +msgstr "വെള്ളിയാഴ്ച" + +msgid "Saturday" +msgstr "ശനിയാഴ്ച" + +msgid "Sunday" +msgstr "ഞായറാഴ്ച" + +msgid "Mon" +msgstr "തിങ്കള്‍" + +msgid "Tue" +msgstr "ചൊവ്വ" + +msgid "Wed" +msgstr "ബുധന്‍" + +msgid "Thu" +msgstr "വ്യാഴം" + +msgid "Fri" +msgstr "വെള്ളി" + +msgid "Sat" +msgstr "ശനി" + +msgid "Sun" +msgstr "ഞായര്‍" + +msgid "January" +msgstr "ജനുവരി" + +msgid "February" +msgstr "ഫെബ്രുവരി" + +msgid "March" +msgstr "മാര്‍ച്ച്" + +msgid "April" +msgstr "ഏപ്രില്‍" + +msgid "May" +msgstr "മേയ്" + +msgid "June" +msgstr "ജൂണ്‍" + +msgid "July" +msgstr "ജൂലൈ" + +msgid "August" +msgstr "ആഗസ്ത്" + +msgid "September" +msgstr "സെപ്തംബര്‍" + +msgid "October" +msgstr "ഒക്ടോബര്‍" + +msgid "November" +msgstr "നവംബര്‍" + +msgid "December" +msgstr "ഡിസംബര്‍" + +msgid "jan" +msgstr "ജനു." + +msgid "feb" +msgstr "ഫെബ്രു." + +msgid "mar" +msgstr "മാര്‍ച്ച്" + +msgid "apr" +msgstr "ഏപ്രില്‍" + +msgid "may" +msgstr "മേയ്" + +msgid "jun" +msgstr "ജൂണ്‍" + +msgid "jul" +msgstr "ജൂലൈ" + +msgid "aug" +msgstr "ആഗസ്ത്" + +msgid "sep" +msgstr "സെപ്ടം." + +msgid "oct" +msgstr "ഒക്ടോ." + +msgid "nov" +msgstr "നവം." + +msgid "dec" +msgstr "ഡിസം." + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "ജനു." + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "ഫെബ്രു." + +msgctxt "abbrev. month" +msgid "March" +msgstr "മാര്‍ച്ച്" + +msgctxt "abbrev. month" +msgid "April" +msgstr "ഏപ്രില്‍" + +msgctxt "abbrev. month" +msgid "May" +msgstr "മേയ്" + +msgctxt "abbrev. month" +msgid "June" +msgstr "ജൂണ്‍" + +msgctxt "abbrev. month" +msgid "July" +msgstr "ജൂലൈ" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "ആഗ." + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "സെപ്തം." + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "ഒക്ടോ." + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "നവം." + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "ഡിസം." + +msgctxt "alt. month" +msgid "January" +msgstr "ജനുവരി" + +msgctxt "alt. month" +msgid "February" +msgstr "ഫെബ്രുവരി" + +msgctxt "alt. month" +msgid "March" +msgstr "മാര്‍ച്ച്" + +msgctxt "alt. month" +msgid "April" +msgstr "ഏപ്രില്‍" + +msgctxt "alt. month" +msgid "May" +msgstr "മേയ്" + +msgctxt "alt. month" +msgid "June" +msgstr "ജൂണ്‍" + +msgctxt "alt. month" +msgid "July" +msgstr "ജൂലൈ" + +msgctxt "alt. month" +msgid "August" +msgstr "ആഗസ്ത്" + +msgctxt "alt. month" +msgid "September" +msgstr "സെപ്തംബര്‍" + +msgctxt "alt. month" +msgid "October" +msgstr "ഒക്ടോബര്‍" + +msgctxt "alt. month" +msgid "November" +msgstr "നവംബര്‍" + +msgctxt "alt. month" +msgid "December" +msgstr "ഡിസംബര്‍" + +msgid "This is not a valid IPv6 address." +msgstr "ഇതു സാധുവായ IPv6 വിലാസമല്ല." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "അഥവാ" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "" +msgstr[1] "" + +msgid "Forbidden" +msgstr "വിലക്കപ്പെട്ടത്" + +msgid "CSRF verification failed. Request aborted." +msgstr "സി എസ് ആർ എഫ് പരിശോധന പരാജയപ്പെട്ടു. റിക്വെസ്റ്റ് റദ്ദാക്കി." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"ഫോം സമർപ്പിക്കുമ്പോൾ ഒരു CSRF കുക്കി ഈ സൈറ്റിൽ ആവശ്യമാണ് എന്നതിനാലാണ് നിങ്ങൾ ഈ സന്ദേശം " +"കാണുന്നത്. മറ്റുള്ളവരാരെങ്കിലും നിങ്ങളുടെ ബ്രൗസറിനെ നിയന്ത്രിക്കുന്നില്ല എന്ന് ഉറപ്പുവരുത്താനായി ഈ " +"കുക്കി ആവശ്യമാണ്. " + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" + +msgid "More information is available with DEBUG=True." +msgstr "Debug=True എന്നു കൊടുത്താൽ കൂടുതൽ കാര്യങ്ങൾ അറിയാൻ കഴിയും." + +msgid "No year specified" +msgstr "വര്‍ഷം പരാമര്‍ശിച്ചിട്ടില്ല" + +msgid "Date out of range" +msgstr "ഡാറ്റ പരിധിയുടെ പുറത്താണ്" + +msgid "No month specified" +msgstr "മാസം പരാമര്‍ശിച്ചിട്ടില്ല" + +msgid "No day specified" +msgstr "ദിവസം പരാമര്‍ശിച്ചിട്ടില്ല" + +msgid "No week specified" +msgstr "ആഴ്ച പരാമര്‍ശിച്ചിട്ടില്ല" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "%(verbose_name_plural)s ഒന്നും ലഭ്യമല്ല" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"%(class_name)s.allow_future ന് False എന്നു നല്കിയിട്ടുള്ളതിനാല്‍ Future " +"%(verbose_name_plural)s ഒന്നും ലഭ്യമല്ല." + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "ചോദ്യത്തിനു ചേരുന്ന് %(verbose_name)s ഇല്ല" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" + +msgid "Directory indexes are not allowed here." +msgstr "ഡയറക്ടറി സൂചികകള്‍ ഇവിടെ അനുവദനീയമല്ല." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "" + +#, python-format +msgid "Index of %(directory)s" +msgstr "%(directory)s യുടെ സൂചിക" + +msgid "The install worked successfully! Congratulations!" +msgstr "ഇൻസ്ടാൾ ഭംഗിയായി നടന്നു! അഭിനന്ദനങ്ങൾ !" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" + +msgid "Django Documentation" +msgstr "ജാംഗോ ഡോക്യുമെന്റേഷൻ" + +msgid "Topics, references, & how-to’s" +msgstr "" + +msgid "Tutorial: A Polling App" +msgstr "പരിശീലനം: ഒരു പോളിങ്ങ് ആപ്പ്" + +msgid "Get started with Django" +msgstr "ജാംഗോയുമായി പരിചയത്തിലാവുക" + +msgid "Django Community" +msgstr "ജാംഗോ കമ്യൂണിറ്റി" + +msgid "Connect, get help, or contribute" +msgstr "കൂട്ടുകൂടൂ, സഹായം തേടൂ, അല്ലെങ്കിൽ സഹകരിക്കൂ" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/my/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/my/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..27a3f934 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/my/LC_MESSAGES/django 3.po @@ -0,0 +1,1197 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Yhal Htet Aung , 2013,2015 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-09-27 22:40+0200\n" +"PO-Revision-Date: 2019-11-05 00:38+0000\n" +"Last-Translator: Ramiro Morales\n" +"Language-Team: Burmese (http://www.transifex.com/django/django/language/" +"my/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: my\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +msgid "Afrikaans" +msgstr "အာဖရိကန်" + +msgid "Arabic" +msgstr "အာရပ်" + +msgid "Asturian" +msgstr "" + +msgid "Azerbaijani" +msgstr "" + +msgid "Bulgarian" +msgstr "ဘူဂေးရီယန်" + +msgid "Belarusian" +msgstr "" + +msgid "Bengali" +msgstr "ဘင်းဂလီ" + +msgid "Breton" +msgstr "" + +msgid "Bosnian" +msgstr "ဘော့်စ်နီယန်" + +msgid "Catalan" +msgstr "ကက်တလန်" + +msgid "Czech" +msgstr "ချက်" + +msgid "Welsh" +msgstr "ဝေးလ်" + +msgid "Danish" +msgstr "ဒိန်းမတ်" + +msgid "German" +msgstr "ဂျာမန်" + +msgid "Lower Sorbian" +msgstr "" + +msgid "Greek" +msgstr "ဂရိ" + +msgid "English" +msgstr "အင်္ဂလိပ်" + +msgid "Australian English" +msgstr "" + +msgid "British English" +msgstr "ဗြိတိသျှအင်္ဂလိပ်" + +msgid "Esperanto" +msgstr "" + +msgid "Spanish" +msgstr "စပိန်" + +msgid "Argentinian Spanish" +msgstr "" + +msgid "Colombian Spanish" +msgstr "" + +msgid "Mexican Spanish" +msgstr "" + +msgid "Nicaraguan Spanish" +msgstr "" + +msgid "Venezuelan Spanish" +msgstr "" + +msgid "Estonian" +msgstr "" + +msgid "Basque" +msgstr "" + +msgid "Persian" +msgstr "" + +msgid "Finnish" +msgstr "" + +msgid "French" +msgstr "" + +msgid "Frisian" +msgstr "" + +msgid "Irish" +msgstr "" + +msgid "Scottish Gaelic" +msgstr "" + +msgid "Galician" +msgstr "" + +msgid "Hebrew" +msgstr "" + +msgid "Hindi" +msgstr "" + +msgid "Croatian" +msgstr "" + +msgid "Upper Sorbian" +msgstr "" + +msgid "Hungarian" +msgstr "" + +msgid "Armenian" +msgstr "" + +msgid "Interlingua" +msgstr "" + +msgid "Indonesian" +msgstr "" + +msgid "Ido" +msgstr "" + +msgid "Icelandic" +msgstr "" + +msgid "Italian" +msgstr "" + +msgid "Japanese" +msgstr "" + +msgid "Georgian" +msgstr "" + +msgid "Kabyle" +msgstr "" + +msgid "Kazakh" +msgstr "" + +msgid "Khmer" +msgstr "" + +msgid "Kannada" +msgstr "" + +msgid "Korean" +msgstr "" + +msgid "Luxembourgish" +msgstr "" + +msgid "Lithuanian" +msgstr "" + +msgid "Latvian" +msgstr "" + +msgid "Macedonian" +msgstr "" + +msgid "Malayalam" +msgstr "" + +msgid "Mongolian" +msgstr "" + +msgid "Marathi" +msgstr "" + +msgid "Burmese" +msgstr "" + +msgid "Norwegian Bokmål" +msgstr "" + +msgid "Nepali" +msgstr "" + +msgid "Dutch" +msgstr "" + +msgid "Norwegian Nynorsk" +msgstr "" + +msgid "Ossetic" +msgstr "" + +msgid "Punjabi" +msgstr "" + +msgid "Polish" +msgstr "" + +msgid "Portuguese" +msgstr "" + +msgid "Brazilian Portuguese" +msgstr "" + +msgid "Romanian" +msgstr "" + +msgid "Russian" +msgstr "" + +msgid "Slovak" +msgstr "" + +msgid "Slovenian" +msgstr "" + +msgid "Albanian" +msgstr "" + +msgid "Serbian" +msgstr "" + +msgid "Serbian Latin" +msgstr "" + +msgid "Swedish" +msgstr "" + +msgid "Swahili" +msgstr "" + +msgid "Tamil" +msgstr "" + +msgid "Telugu" +msgstr "" + +msgid "Thai" +msgstr "" + +msgid "Turkish" +msgstr "" + +msgid "Tatar" +msgstr "" + +msgid "Udmurt" +msgstr "" + +msgid "Ukrainian" +msgstr "" + +msgid "Urdu" +msgstr "" + +msgid "Uzbek" +msgstr "" + +msgid "Vietnamese" +msgstr "" + +msgid "Simplified Chinese" +msgstr "" + +msgid "Traditional Chinese" +msgstr "" + +msgid "Messages" +msgstr "" + +msgid "Site Maps" +msgstr "" + +msgid "Static Files" +msgstr "" + +msgid "Syndication" +msgstr "" + +msgid "That page number is not an integer" +msgstr "" + +msgid "That page number is less than 1" +msgstr "" + +msgid "That page contains no results" +msgstr "" + +msgid "Enter a valid value." +msgstr "" + +msgid "Enter a valid URL." +msgstr "" + +msgid "Enter a valid integer." +msgstr "" + +msgid "Enter a valid email address." +msgstr "" + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" + +msgid "Enter a valid IPv4 address." +msgstr "" + +msgid "Enter a valid IPv6 address." +msgstr "" + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "" + +msgid "Enter only digits separated by commas." +msgstr "" + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "" + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "" + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" + +msgid "Enter a number." +msgstr "" + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "" + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "" + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" + +msgid "Null characters are not allowed." +msgstr "" + +msgid "and" +msgstr "နှင့်" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "" + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "" + +msgid "This field cannot be null." +msgstr "" + +msgid "This field cannot be blank." +msgstr "" + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "" + +#. Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. +#. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "" + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "" + +msgid "Boolean (Either True or False)" +msgstr "" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "" + +msgid "Comma-separated integers" +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" + +msgid "Date (without time)" +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" + +msgid "Date (with time)" +msgstr "" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "" + +msgid "Decimal number" +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" + +msgid "Duration" +msgstr "" + +msgid "Email address" +msgstr "အီးမေးလ်လိပ်စာ" + +msgid "File path" +msgstr "" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "" + +msgid "Floating point number" +msgstr "" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "" + +msgid "Integer" +msgstr "ကိန်းပြည့်" + +msgid "Big (8 byte) integer" +msgstr "" + +msgid "IPv4 address" +msgstr "အိုင်ပီဗီ၄လိပ်စာ" + +msgid "IP address" +msgstr "အိုင်ပီလိပ်စာ" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "" + +msgid "Boolean (Either True, False or None)" +msgstr "" + +msgid "Positive integer" +msgstr "" + +msgid "Positive small integer" +msgstr "" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "" + +msgid "Small integer" +msgstr "" + +msgid "Text" +msgstr "စာသား" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" + +msgid "Time" +msgstr "" + +msgid "URL" +msgstr "ယူအာအယ်" + +msgid "Raw binary data" +msgstr "" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "" + +msgid "Universally unique identifier" +msgstr "" + +msgid "File" +msgstr "ဖိုင်" + +msgid "Image" +msgstr "ပံု" + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "" + +msgid "Foreign Key (type determined by related field)" +msgstr "" + +msgid "One-to-one relationship" +msgstr "" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "" + +msgid "Many-to-many relationship" +msgstr "" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr "" + +msgid "This field is required." +msgstr "" + +msgid "Enter a whole number." +msgstr "" + +msgid "Enter a valid date." +msgstr "" + +msgid "Enter a valid time." +msgstr "" + +msgid "Enter a valid date/time." +msgstr "" + +msgid "Enter a valid duration." +msgstr "" + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "" + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" + +msgid "No file was submitted." +msgstr "" + +msgid "The submitted file is empty." +msgstr "" + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" + +msgid "Enter a list of values." +msgstr "" + +msgid "Enter a complete value." +msgstr "" + +msgid "Enter a valid UUID." +msgstr "" + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr "" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "" + +msgid "ManagementForm data is missing or has been tampered with" +msgstr "" + +#, python-format +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "" + +#, python-format +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "" + +msgid "Order" +msgstr "မှာကြား" + +msgid "Delete" +msgstr "ပယ်ဖျက်" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "" + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" + +msgid "Please correct the duplicate values below." +msgstr "" + +msgid "The inline value did not match the parent instance." +msgstr "" + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "" + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" + +msgid "Clear" +msgstr "" + +msgid "Currently" +msgstr "" + +msgid "Change" +msgstr "" + +msgid "Unknown" +msgstr "အမည်မသိ" + +msgid "Yes" +msgstr "ဟုတ်" + +msgid "No" +msgstr "မဟုတ်" + +msgid "Year" +msgstr "" + +msgid "Month" +msgstr "" + +msgid "Day" +msgstr "" + +msgid "yes,no,maybe" +msgstr "" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d ဘိုက်များ" + +#, python-format +msgid "%s KB" +msgstr "%s ကီလိုဘိုက်" + +#, python-format +msgid "%s MB" +msgstr "%s မက်ဂါဘိုက်" + +#, python-format +msgid "%s GB" +msgstr "%s ဂစ်ဂါဘိုက်" + +#, python-format +msgid "%s TB" +msgstr "%s တီရာဘိုက်" + +#, python-format +msgid "%s PB" +msgstr "%s ပီတာဘိုက်" + +msgid "p.m." +msgstr "ညနေ" + +msgid "a.m." +msgstr "မနက်" + +msgid "PM" +msgstr "ညနေ" + +msgid "AM" +msgstr "မနက်" + +msgid "midnight" +msgstr "သန်းခေါင်" + +msgid "noon" +msgstr "မွန်းတည့်" + +msgid "Monday" +msgstr "တနင်္လာနေ့" + +msgid "Tuesday" +msgstr "" + +msgid "Wednesday" +msgstr "" + +msgid "Thursday" +msgstr "" + +msgid "Friday" +msgstr "" + +msgid "Saturday" +msgstr "" + +msgid "Sunday" +msgstr "" + +msgid "Mon" +msgstr "" + +msgid "Tue" +msgstr "" + +msgid "Wed" +msgstr "" + +msgid "Thu" +msgstr "" + +msgid "Fri" +msgstr "" + +msgid "Sat" +msgstr "" + +msgid "Sun" +msgstr "" + +msgid "January" +msgstr "" + +msgid "February" +msgstr "" + +msgid "March" +msgstr "" + +msgid "April" +msgstr "" + +msgid "May" +msgstr "" + +msgid "June" +msgstr "" + +msgid "July" +msgstr "" + +msgid "August" +msgstr "" + +msgid "September" +msgstr "" + +msgid "October" +msgstr "" + +msgid "November" +msgstr "" + +msgid "December" +msgstr "" + +msgid "jan" +msgstr "" + +msgid "feb" +msgstr "" + +msgid "mar" +msgstr "" + +msgid "apr" +msgstr "" + +msgid "may" +msgstr "" + +msgid "jun" +msgstr "" + +msgid "jul" +msgstr "" + +msgid "aug" +msgstr "" + +msgid "sep" +msgstr "" + +msgid "oct" +msgstr "" + +msgid "nov" +msgstr "" + +msgid "dec" +msgstr "" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "" + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "" + +msgctxt "abbrev. month" +msgid "March" +msgstr "" + +msgctxt "abbrev. month" +msgid "April" +msgstr "" + +msgctxt "abbrev. month" +msgid "May" +msgstr "" + +msgctxt "abbrev. month" +msgid "June" +msgstr "" + +msgctxt "abbrev. month" +msgid "July" +msgstr "" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "" + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "" + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "" + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "" + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "" + +msgctxt "alt. month" +msgid "January" +msgstr "" + +msgctxt "alt. month" +msgid "February" +msgstr "" + +msgctxt "alt. month" +msgid "March" +msgstr "" + +msgctxt "alt. month" +msgid "April" +msgstr "" + +msgctxt "alt. month" +msgid "May" +msgstr "" + +msgctxt "alt. month" +msgid "June" +msgstr "" + +msgctxt "alt. month" +msgid "July" +msgstr "" + +msgctxt "alt. month" +msgid "August" +msgstr "" + +msgctxt "alt. month" +msgid "September" +msgstr "" + +msgctxt "alt. month" +msgid "October" +msgstr "" + +msgctxt "alt. month" +msgid "November" +msgstr "" + +msgctxt "alt. month" +msgid "December" +msgstr "" + +msgid "This is not a valid IPv6 address." +msgstr "" + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "" + +msgid "or" +msgstr "" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr "" + +#, python-format +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "" + +#, python-format +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "" + +#, python-format +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "" + +#, python-format +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "" + +#, python-format +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "" + +#, python-format +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "" + +msgid "0 minutes" +msgstr "" + +msgid "Forbidden" +msgstr "" + +msgid "CSRF verification failed. Request aborted." +msgstr "" + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your Web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" + +msgid "More information is available with DEBUG=True." +msgstr "" + +msgid "No year specified" +msgstr "" + +msgid "Date out of range" +msgstr "" + +msgid "No month specified" +msgstr "" + +msgid "No day specified" +msgstr "" + +msgid "No week specified" +msgstr "" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" + +msgid "Directory indexes are not allowed here." +msgstr "" + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "" + +#, python-format +msgid "Index of %(directory)s" +msgstr "" + +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" + +msgid "The install worked successfully! Congratulations!" +msgstr "" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" + +msgid "Django Documentation" +msgstr "" + +msgid "Topics, references, & how-to’s" +msgstr "" + +msgid "Tutorial: A Polling App" +msgstr "" + +msgid "Get started with Django" +msgstr "" + +msgid "Django Community" +msgstr "" + +msgid "Connect, get help, or contribute" +msgstr "" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/ne/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/ne/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..a9fd7f8c --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/ne/LC_MESSAGES/django 3.po @@ -0,0 +1,1253 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Claude Paroz , 2020 +# Jannis Leidel , 2014 +# Paras Nath Chaudhary , 2012 +# Sagar Chalise , 2011-2012,2015,2018 +# Sagar Chalise , 2015 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-05-19 20:23+0200\n" +"PO-Revision-Date: 2020-07-14 21:42+0000\n" +"Last-Translator: Transifex Bot <>\n" +"Language-Team: Nepali (http://www.transifex.com/django/django/language/ne/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ne\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Afrikaans" +msgstr "अफ्रिकन" + +msgid "Arabic" +msgstr "अरबिक" + +msgid "Algerian Arabic" +msgstr "" + +msgid "Asturian" +msgstr "अस्टुरियन" + +msgid "Azerbaijani" +msgstr "अजरबैजानी" + +msgid "Bulgarian" +msgstr "बुल्गेरियाली" + +msgid "Belarusian" +msgstr "बेलारुसियन" + +msgid "Bengali" +msgstr "बंगाली" + +msgid "Breton" +msgstr "ब्रेटोन" + +msgid "Bosnian" +msgstr "बोस्नियाली" + +msgid "Catalan" +msgstr "क्याटालान" + +msgid "Czech" +msgstr "चेक" + +msgid "Welsh" +msgstr "वेल्स" + +msgid "Danish" +msgstr "डेनिस" + +msgid "German" +msgstr "जर्मन" + +msgid "Lower Sorbian" +msgstr "तल्लो सोर्बियन" + +msgid "Greek" +msgstr "ग्रिक" + +msgid "English" +msgstr "अंग्रेजी" + +msgid "Australian English" +msgstr "अस्ट्रेलियाली अंग्रेजी" + +msgid "British English" +msgstr "बेलायती अंग्रेजी" + +msgid "Esperanto" +msgstr "इस्परा्न्तो" + +msgid "Spanish" +msgstr "स्पेनिस" + +msgid "Argentinian Spanish" +msgstr "अर्जेन्टिनाली स्पेनिस" + +msgid "Colombian Spanish" +msgstr "कोलम्बियाली स्पेनिस" + +msgid "Mexican Spanish" +msgstr "मेक्सिकन स्पेनिस" + +msgid "Nicaraguan Spanish" +msgstr "निकारागुँवा स्पेनिस" + +msgid "Venezuelan Spanish" +msgstr "भेनेजुएला स्पेनिस" + +msgid "Estonian" +msgstr "इस्टोनियन" + +msgid "Basque" +msgstr "बास्क" + +msgid "Persian" +msgstr "फारसी" + +msgid "Finnish" +msgstr "फिन्निस" + +msgid "French" +msgstr "फ्रान्सेली" + +msgid "Frisian" +msgstr "फ्रिसियन" + +msgid "Irish" +msgstr "आयरिस" + +msgid "Scottish Gaelic" +msgstr "स्कटीस गैलिक" + +msgid "Galician" +msgstr "ग्यलिसियन" + +msgid "Hebrew" +msgstr "हिब्रु" + +msgid "Hindi" +msgstr "हिन्दि " + +msgid "Croatian" +msgstr "क्रोषियन" + +msgid "Upper Sorbian" +msgstr "माथिल्लो सोर्बियन " + +msgid "Hungarian" +msgstr "हन्गेरियन" + +msgid "Armenian" +msgstr "अर्मेनियन" + +msgid "Interlingua" +msgstr "ईन्टरलिन्गुवा" + +msgid "Indonesian" +msgstr "इन्डोनेसियाली" + +msgid "Igbo" +msgstr "" + +msgid "Ido" +msgstr "आइडु" + +msgid "Icelandic" +msgstr "आइसल्यान्डिक" + +msgid "Italian" +msgstr "ईटालियन" + +msgid "Japanese" +msgstr "जापनिज" + +msgid "Georgian" +msgstr "जर्जीयन" + +msgid "Kabyle" +msgstr "कबायल" + +msgid "Kazakh" +msgstr "कजाक" + +msgid "Khmer" +msgstr "ख्मेर" + +msgid "Kannada" +msgstr "कन्नड" + +msgid "Korean" +msgstr "कोरियाली" + +msgid "Kyrgyz" +msgstr "" + +msgid "Luxembourgish" +msgstr "लक्जेमबर्गेली" + +msgid "Lithuanian" +msgstr "लिथुवानियाली" + +msgid "Latvian" +msgstr "लाट्भियन" + +msgid "Macedonian" +msgstr "म्यासेडोनियन" + +msgid "Malayalam" +msgstr "मलायलम" + +msgid "Mongolian" +msgstr "मंगोलियन" + +msgid "Marathi" +msgstr "मराठी" + +msgid "Burmese" +msgstr "बर्मेली" + +msgid "Norwegian Bokmål" +msgstr "नर्वे बक्मल" + +msgid "Nepali" +msgstr "नेपाली" + +msgid "Dutch" +msgstr "डच" + +msgid "Norwegian Nynorsk" +msgstr "नर्वेली न्योर्स्क" + +msgid "Ossetic" +msgstr "ओसेटिक" + +msgid "Punjabi" +msgstr "पञ्जावी" + +msgid "Polish" +msgstr "पोलिस" + +msgid "Portuguese" +msgstr "पुर्तगाली" + +msgid "Brazilian Portuguese" +msgstr "ब्राजिली पुर्तगाली" + +msgid "Romanian" +msgstr "रोमानियाली" + +msgid "Russian" +msgstr "रुसी" + +msgid "Slovak" +msgstr "सलोभाक" + +msgid "Slovenian" +msgstr "स्लोभेनियाली" + +msgid "Albanian" +msgstr "अल्बानियाली" + +msgid "Serbian" +msgstr "सर्वियाली" + +msgid "Serbian Latin" +msgstr "सर्वियाली ल्याटिन" + +msgid "Swedish" +msgstr "स्विडिस" + +msgid "Swahili" +msgstr "स्वाहिली" + +msgid "Tamil" +msgstr "तामिल" + +msgid "Telugu" +msgstr "तेलुगु" + +msgid "Tajik" +msgstr "" + +msgid "Thai" +msgstr "थाई" + +msgid "Turkmen" +msgstr "" + +msgid "Turkish" +msgstr "टर्किस" + +msgid "Tatar" +msgstr "टाटर" + +msgid "Udmurt" +msgstr "उद्मुर्ट" + +msgid "Ukrainian" +msgstr "युक्रेनि" + +msgid "Urdu" +msgstr "उर्दु" + +msgid "Uzbek" +msgstr "उज्बेक" + +msgid "Vietnamese" +msgstr "भियतनामी" + +msgid "Simplified Chinese" +msgstr "सरल चिनि" + +msgid "Traditional Chinese" +msgstr "प्राचिन चिनि" + +msgid "Messages" +msgstr "सुचनाहरु" + +msgid "Site Maps" +msgstr "साइट म्याप्स" + +msgid "Static Files" +msgstr "स्टेेटिक फाइलहरु" + +msgid "Syndication" +msgstr "सिन्डिकेसन" + +msgid "That page number is not an integer" +msgstr "पृष्ठ नं अंक होइन ।" + +msgid "That page number is less than 1" +msgstr "पृष्ठ नं १ भन्दा कम भयो ।" + +msgid "That page contains no results" +msgstr "पृष्ठमा नतिजा छैन ।" + +msgid "Enter a valid value." +msgstr "उपयुक्त मान राख्नुहोस ।" + +msgid "Enter a valid URL." +msgstr "उपयुक्त URL राख्नुहोस ।" + +msgid "Enter a valid integer." +msgstr "उपयुक्त अंक राख्नुहोस ।" + +msgid "Enter a valid email address." +msgstr "सही ई-मेल ठेगाना राख्नु होस ।" + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" + +msgid "Enter a valid IPv4 address." +msgstr "उपयुक्त IPv4 ठेगाना राख्नुहोस" + +msgid "Enter a valid IPv6 address." +msgstr "उपयुक्त IPv6 ठेगाना राख्नुहोस ।" + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "उपयुक्त IPv4 वा IPv6 ठेगाना राख्नुहोस ।" + +msgid "Enter only digits separated by commas." +msgstr "कम्मा सहितका वर्ण मात्र राख्नुहोस ।" + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "यो मान %(limit_value)s छ भन्ने निश्चित गर्नुहोस । (यो %(show_value)s हो ।)" + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "यो मान %(limit_value)s भन्दा कम अथवा बराबर छ भन्ने निश्चित गर्नुहोस ।" + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "यो मान %(limit_value)s भन्दा बढी अथवा बराबर छ भन्ने निशचित गर्नुहोस ।" + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"यो मान कम्तिमा पनि %(limit_value)d अक्षर छ भन्ने निश्चित गर्नुहोस । (यसमा " +"%(show_value)d छ ।)" +msgstr[1] "" +"यो मान कम्तिमा पनि %(limit_value)d अक्षरहरु छ भन्ने निश्चित गर्नुहोस । (यसमा " +"%(show_value)d छ ।)" + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"यो मान बढिमा पनि %(limit_value)d अक्षर छ भन्ने निश्चित गर्नुहोस । (यसमा " +"%(show_value)d छ ।)" +msgstr[1] "" +"यो मान बढिमा पनि %(limit_value)d अक्षरहरु छ भन्ने निश्चित गर्नुहोस । (यसमा " +"%(show_value)d छ ।)" + +msgid "Enter a number." +msgstr "संख्या राख्नुहोस ।" + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "जम्मा %(max)s भन्दा बढी अक्षर नभएको निश्चित पार्नु होस ।" +msgstr[1] "जम्मा %(max)s भन्दा बढी अक्षरहरु नभएको निश्चित पार्नु होस ।" + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "दशमलव पछि %(max)s भन्दा बढी अक्षर नभएको निश्चित पार्नु होस ।" +msgstr[1] "दशमलव पछि %(max)s भन्दा बढी अक्षरहरु नभएको निश्चित पार्नु होस ।" + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "दशमलव अघि %(max)s भन्दा बढी अक्षर नभएको निश्चित पार्नु होस ।" +msgstr[1] "दशमलव अघि %(max)s भन्दा बढी अक्षरहरु नभएको निश्चित पार्नु होस ।" + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" + +msgid "Null characters are not allowed." +msgstr "शून्य मान अनुमति छैन।" + +msgid "and" +msgstr "र" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "%(field_labels)s भएको %(model_name)s बनि सकेको छ । " + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "%(value)r मान उपयुक्त छनोट होइन ।" + +msgid "This field cannot be null." +msgstr "यो फाँट शून्य हुन सक्दैन ।" + +msgid "This field cannot be blank." +msgstr "यो फाँट खाली हुन सक्दैन ।" + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "%(field_label)s भएको %(model_name)s पहिलै विद्धमान छ ।" + +#. Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. +#. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"%(date_field_label)s %(lookup_type)s को लागि %(field_label)s अनुपम हुनु पर्दछ ।" + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "फाँटको प्रकार: %(field_type)s" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "" + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "" + +msgid "Boolean (Either True or False)" +msgstr "बुलियन (True अथवा False)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "वर्ण (%(max_length)s सम्म)" + +msgid "Comma-separated integers" +msgstr "कम्माले छुट्याइएका अंकहरु ।" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" + +msgid "Date (without time)" +msgstr "मिति (समय रहित)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" + +msgid "Date (with time)" +msgstr "मिति (समय सहित)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "" + +msgid "Decimal number" +msgstr "दश्मलव संख्या" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" + +msgid "Duration" +msgstr "अवधि" + +msgid "Email address" +msgstr "ई-मेल ठेगाना" + +msgid "File path" +msgstr "फाइलको मार्ग" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "" + +msgid "Floating point number" +msgstr "दश्मलव हुने संख्या" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "" + +msgid "Integer" +msgstr "अंक" + +msgid "Big (8 byte) integer" +msgstr "ठूलो (८ बाइटको) अंक" + +msgid "IPv4 address" +msgstr "आइ.पी.भी४ ठेगाना" + +msgid "IP address" +msgstr "IP ठेगाना" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "" + +msgid "Boolean (Either True, False or None)" +msgstr "बुलियन (True, False अथवा None)" + +msgid "Positive big integer" +msgstr "" + +msgid "Positive integer" +msgstr "सकारात्मक पूर्णांक" + +msgid "Positive small integer" +msgstr "सानो जोड अङ्क" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "स्लग(%(max_length)s सम्म)" + +msgid "Small integer" +msgstr "सानो अङ्क" + +msgid "Text" +msgstr "पाठ" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" + +msgid "Time" +msgstr "समय" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "र बाइनरी डाटा" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "" + +msgid "Universally unique identifier" +msgstr "" + +msgid "File" +msgstr "फाइल" + +msgid "Image" +msgstr "चित्र" + +msgid "A JSON object" +msgstr "" + +msgid "Value must be valid JSON." +msgstr "" + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "" + +msgid "Foreign Key (type determined by related field)" +msgstr "फोरेन कि (प्रकार नातागत फाँटले जनाउछ)" + +msgid "One-to-one relationship" +msgstr "एक-देखि-एक नाता" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "%(from)s-%(to)s सम्बन्ध" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "%(from)s-%(to)s सम्बन्धहरु" + +msgid "Many-to-many relationship" +msgstr "अनेक-देखि-अनेक नाता" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "यो फाँट अनिवार्य छ ।" + +msgid "Enter a whole number." +msgstr "संख्या राख्नुहोस ।" + +msgid "Enter a valid date." +msgstr "उपयुक्त मिति राख्नुहोस ।" + +msgid "Enter a valid time." +msgstr "उपयुक्त समय राख्नुहोस ।" + +msgid "Enter a valid date/time." +msgstr "उपयुक्त मिति/समय राख्नुहोस ।" + +msgid "Enter a valid duration." +msgstr "उपयुक्त अवधि राख्नुहोस ।" + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "दिन गन्ती {min_days} र {max_days} बीचमा हुनु पर्छ । " + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "कुनै फाईल पेश गरिएको छैन । फारममा ईनकोडिङको प्रकार जाँच गर्नुहोस । " + +msgid "No file was submitted." +msgstr "कुनै फाईल पेश गरिएको छैन ।" + +msgid "The submitted file is empty." +msgstr "पेश गरिएको फाइल खाली छ ।" + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"यो फाइलको नाममा बाढीमा %(max)d अङ्क भएको निश्चित गर्नु होस । (यसमा %(length)d छ " +"।)" +msgstr[1] "" +"यो फाइलको नाममा बढी मा %(max)d अङ्कहरू भएको निश्चित गर्नु होस । (यसमा %(length)d " +"छ ।)" + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "दुवै नछान्नुहोस, कि त फाइल पेश गर्नुहोस वा चेक बाकस मा छान्नुहोस ।" + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"उपयुक्त चित्र अपलोड गर्नुहोस । तपाइले अपलोड गर्नु भएको फाइल चित्र होइन वा बिग्रेको चित्र " +"हो ।" + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "उपयुक्त विकल्प छान्नुहोस । %(value)s प्रस्तावित विकल्प होइन ।" + +msgid "Enter a list of values." +msgstr "मानहरु राख्नुहोस" + +msgid "Enter a complete value." +msgstr "पुरा मान राख्नु होस ।" + +msgid "Enter a valid UUID." +msgstr "उपयुक्त UUID राख्नु होस ।" + +msgid "Enter a valid JSON." +msgstr "" + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(लुकेका %(name)s) %(error)s" + +msgid "ManagementForm data is missing or has been tampered with" +msgstr "म्यानेजमेन्ट फारम डाटा चलाइएको वा नभरेको पाइयो ।" + +#, python-format +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "कृपया %d अथवा सो भन्दा थोरै फारम बुझाउनु होस ।" +msgstr[1] "कृपया %d अथवा सो भन्दा थोरै फारमहरु बुझाउनु होस ।" + +#, python-format +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "कृपया %d अथवा सो भन्दा धेरै फारम बुझाउनु होस ।" +msgstr[1] "कृपया %d अथवा सो भन्दा धेरै फारमहरु बुझाउनु होस ।" + +msgid "Order" +msgstr "क्रम" + +msgid "Delete" +msgstr "मेट्नुहोस" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "कृपया %(field)s का लागि दोहोरिइका तथ्याङ्कहरु सच्याउनुहोस ।" + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "कृपया %(field)s का लागि दोहोरिइका तथ्याङ्कहरु नौलो तथ्याङ्क सहित सच्याउनुहोस ।" + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"कृपया %(field_name)s का लागि दोहोरिइका तथ्याङ्कहरु सच्याउनुहोस जसमा " +"%(date_field)sको %(lookup)s नौलो हुनुपर्दछ ।" + +msgid "Please correct the duplicate values below." +msgstr "कृपया तलका दोहोरिइका मानहरु सच्याउनुहोस ।" + +msgid "The inline value did not match the parent instance." +msgstr "" + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "उपयुक्त विकल्प छान्नुहोस । छानिएको विकल्प प्रस्तावित विकल्प होइन ।" + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "" + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" + +msgid "Clear" +msgstr "सबै खाली गर्नु होस ।" + +msgid "Currently" +msgstr "अहिले" + +msgid "Change" +msgstr "फेर्नुहोस" + +msgid "Unknown" +msgstr "अज्ञात" + +msgid "Yes" +msgstr "हुन्छ" + +msgid "No" +msgstr "होइन" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "हो,होइन,सायद" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d बाइट" +msgstr[1] "%(size)d बाइटहरु" + +#, python-format +msgid "%s KB" +msgstr "%s किलोबाइट" + +#, python-format +msgid "%s MB" +msgstr "%s मेगाबाइट" + +#, python-format +msgid "%s GB" +msgstr "%s गिगाबाइट" + +#, python-format +msgid "%s TB" +msgstr "%s टेराबाइट" + +#, python-format +msgid "%s PB" +msgstr "%s पिटाबाइट" + +msgid "p.m." +msgstr "p.m." + +msgid "a.m." +msgstr "a.m." + +msgid "PM" +msgstr "PM" + +msgid "AM" +msgstr "AM" + +msgid "midnight" +msgstr "मध्यरात" + +msgid "noon" +msgstr "मध्यान्ह" + +msgid "Monday" +msgstr "सोमवार" + +msgid "Tuesday" +msgstr "मंगलवार" + +msgid "Wednesday" +msgstr "बुधवार" + +msgid "Thursday" +msgstr "बिहीवार" + +msgid "Friday" +msgstr "शुक्रवार" + +msgid "Saturday" +msgstr "शनिवार" + +msgid "Sunday" +msgstr "आइतवार" + +msgid "Mon" +msgstr "सोम" + +msgid "Tue" +msgstr "मंगल" + +msgid "Wed" +msgstr "बुध" + +msgid "Thu" +msgstr "बिहि" + +msgid "Fri" +msgstr "शुक्र" + +msgid "Sat" +msgstr "शनि" + +msgid "Sun" +msgstr "आइत" + +msgid "January" +msgstr "जनवरी" + +msgid "February" +msgstr "फेब्रुअरी" + +msgid "March" +msgstr "मार्च" + +msgid "April" +msgstr "अप्रिल" + +msgid "May" +msgstr "मई" + +msgid "June" +msgstr "जुन" + +msgid "July" +msgstr "जुलै" + +msgid "August" +msgstr "अगस्त" + +msgid "September" +msgstr "सेप्टेम्बर" + +msgid "October" +msgstr "अक्टुवर" + +msgid "November" +msgstr "नभम्वर" + +msgid "December" +msgstr "डिसम्वर" + +msgid "jan" +msgstr "जनवरी" + +msgid "feb" +msgstr "फेब्रुअरी" + +msgid "mar" +msgstr "मार्च" + +msgid "apr" +msgstr "अप्रिल" + +msgid "may" +msgstr "मई" + +msgid "jun" +msgstr "जुन" + +msgid "jul" +msgstr "जुलै" + +msgid "aug" +msgstr "अग्सत" + +msgid "sep" +msgstr "सेप्तेम्बर" + +msgid "oct" +msgstr "अक्टुवर" + +msgid "nov" +msgstr "नभम्वर" + +msgid "dec" +msgstr "डिसम्वर" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "जनवरी" + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "फेब्रुअरी" + +msgctxt "abbrev. month" +msgid "March" +msgstr "मार्च" + +msgctxt "abbrev. month" +msgid "April" +msgstr "अप्रिल" + +msgctxt "abbrev. month" +msgid "May" +msgstr "मई" + +msgctxt "abbrev. month" +msgid "June" +msgstr "जुन" + +msgctxt "abbrev. month" +msgid "July" +msgstr "जुलै" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "अगस्त" + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "सेप्तेम्बर" + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "अक्टुवर" + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "नभम्वर" + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "डिसम्वर" + +msgctxt "alt. month" +msgid "January" +msgstr "जनवरी" + +msgctxt "alt. month" +msgid "February" +msgstr "फेब्रुअरी" + +msgctxt "alt. month" +msgid "March" +msgstr "मार्च" + +msgctxt "alt. month" +msgid "April" +msgstr "अप्रिल" + +msgctxt "alt. month" +msgid "May" +msgstr "मई" + +msgctxt "alt. month" +msgid "June" +msgstr "जुन" + +msgctxt "alt. month" +msgid "July" +msgstr "जुलै" + +msgctxt "alt. month" +msgid "August" +msgstr "अगस्त" + +msgctxt "alt. month" +msgid "September" +msgstr "सेप्टेम्बर" + +msgctxt "alt. month" +msgid "October" +msgstr "अक्टुवर" + +msgctxt "alt. month" +msgid "November" +msgstr "नभम्वर" + +msgctxt "alt. month" +msgid "December" +msgstr "डिसम्वर" + +msgid "This is not a valid IPv6 address." +msgstr "यो उपयुक्त IPv6 ठेगाना होइन ।" + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "अथवा" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr "," + +#, python-format +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d वर्ष" +msgstr[1] "%d वर्षहरु" + +#, python-format +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d महिना" +msgstr[1] "%d महिनाहरु" + +#, python-format +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d सप्ताह" +msgstr[1] "%d सप्ताहहरु" + +#, python-format +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d दिन" +msgstr[1] "%d दिनहरु" + +#, python-format +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d घण्टा" +msgstr[1] "%d घण्टाहरु" + +#, python-format +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d मिनट" +msgstr[1] "%d मिनटहरु" + +msgid "Forbidden" +msgstr "निषेधित" + +msgid "CSRF verification failed. Request aborted." +msgstr "CSRF प्रमाणीकरण भएन । अनुरोध विफल ।" + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your Web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" + +msgid "More information is available with DEBUG=True." +msgstr "DEBUG=True ले ज्यादा सुचना प्रदान गर्दछ ।" + +msgid "No year specified" +msgstr "साल तोकिएको छैन ।" + +msgid "Date out of range" +msgstr "मिति मिलेन ।" + +msgid "No month specified" +msgstr "महिना तोकिएको छैन ।" + +msgid "No day specified" +msgstr "दिन तोकिएको छैन ।" + +msgid "No week specified" +msgstr "साता तोकिएको छैन ।" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "%(verbose_name_plural)s उपलब्ध छैन ।" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"%(class_name)s.allow_future 'False' हुनाले आगामी %(verbose_name_plural)s उपलब्ध " +"छैन ।" + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "%(verbose_name)s भेटिएन ।" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "रद्द पृष्ठ (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" + +msgid "Directory indexes are not allowed here." +msgstr "डाइरेक्टरी इन्डेक्सहरु यहाँ अनुमति छैन ।" + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "" + +#, python-format +msgid "Index of %(directory)s" +msgstr "%(directory)s को सूची" + +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "ज्याङ्गो : वेब साइट र एप्लिकेसन बनाउन सहयोगी औजार " + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"ज्याङ्गो %(version)s को परिवर्तन तथा विशेषता यहाँ हेर्नु होस" + +msgid "The install worked successfully! Congratulations!" +msgstr "बधाई छ । स्थापना भएको छ ।" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" + +msgid "Django Documentation" +msgstr "ज्याङ्गो दस्तावेज ।" + +msgid "Topics, references, & how-to’s" +msgstr "" + +msgid "Tutorial: A Polling App" +msgstr "मतदान एप उदाहरण " + +msgid "Get started with Django" +msgstr "ज्याङ्गो सुरु गर्नु होस ।" + +msgid "Django Community" +msgstr "ज्याङ्गो समुदाय" + +msgid "Connect, get help, or contribute" +msgstr "सहयोग अथवा योगदान गरी जोडिनु होस" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/pl/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/pl/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..a5c6b8e0 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/pl/LC_MESSAGES/django 3.po @@ -0,0 +1,1402 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# 8ffa075ab2f53c280beb2c066769d1ac_169beb5 <462ee687bbf3107fab5af73e8cc690d0_217822>, 2014 +# Adam Stachowicz , 2015 +# angularcircle, 2011,2013 +# angularcircle, 2011,2013 +# angularcircle, 2014 +# Dariusz Paluch , 2015 +# Darek505, 2022 +# Jannis Leidel , 2011 +# Janusz Harkot , 2014-2015 +# Kacper Krupa , 2013 +# Karol , 2012 +# 0d5641585fd67fbdb97037c19ab83e4c_18c98b0 , 2011 +# 0d5641585fd67fbdb97037c19ab83e4c_18c98b0 , 2011 +# Łukasz Rekucki (lqc) , 2011 +# Maciej Olko , 2016-2021 +# Maciej Olko , 2023 +# Maciej Olko , 2015 +# Mariusz Felisiak , 2020-2021 +# Michał Pasternak , 2013 +# c10516f0462e552b4c3672569f0745a7_cc5cca2 <841826256cd8f47d0e443806a8e56601_19204>, 2012 +# Piotr Meuś , 2014 +# c10516f0462e552b4c3672569f0745a7_cc5cca2 <841826256cd8f47d0e443806a8e56601_19204>, 2012 +# Quadric , 2014 +# Radek Czajka , 2013 +# Radek Czajka , 2013 +# Roman Barczyński, 2012 +# 8ffa075ab2f53c280beb2c066769d1ac_169beb5 <462ee687bbf3107fab5af73e8cc690d0_217822>, 2014 +# Tomasz Kajtoch , 2016-2017 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 06:49+0000\n" +"Last-Translator: Maciej Olko , 2023\n" +"Language-Team: Polish (http://www.transifex.com/django/django/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && " +"(n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && " +"n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" + +msgid "Afrikaans" +msgstr "afrykanerski" + +msgid "Arabic" +msgstr "arabski" + +msgid "Algerian Arabic" +msgstr "algierski arabski" + +msgid "Asturian" +msgstr "asturyjski" + +msgid "Azerbaijani" +msgstr "azerski" + +msgid "Bulgarian" +msgstr "bułgarski" + +msgid "Belarusian" +msgstr "białoruski" + +msgid "Bengali" +msgstr "bengalski" + +msgid "Breton" +msgstr "bretoński" + +msgid "Bosnian" +msgstr "bośniacki" + +msgid "Catalan" +msgstr "kataloński" + +msgid "Central Kurdish (Sorani)" +msgstr "sorani" + +msgid "Czech" +msgstr "czeski" + +msgid "Welsh" +msgstr "walijski" + +msgid "Danish" +msgstr "duński" + +msgid "German" +msgstr "niemiecki" + +msgid "Lower Sorbian" +msgstr "dolnołużycki" + +msgid "Greek" +msgstr "grecki" + +msgid "English" +msgstr "angielski" + +msgid "Australian English" +msgstr "australijski angielski" + +msgid "British English" +msgstr "brytyjski angielski" + +msgid "Esperanto" +msgstr "esperanto" + +msgid "Spanish" +msgstr "hiszpański" + +msgid "Argentinian Spanish" +msgstr "hiszpański argentyński" + +msgid "Colombian Spanish" +msgstr "hiszpański kolumbijski" + +msgid "Mexican Spanish" +msgstr "hiszpański meksykański" + +msgid "Nicaraguan Spanish" +msgstr "hiszpański nikaraguański" + +msgid "Venezuelan Spanish" +msgstr "hiszpański wenezuelski" + +msgid "Estonian" +msgstr "estoński" + +msgid "Basque" +msgstr "baskijski" + +msgid "Persian" +msgstr "perski" + +msgid "Finnish" +msgstr "fiński" + +msgid "French" +msgstr "francuski" + +msgid "Frisian" +msgstr "fryzyjski" + +msgid "Irish" +msgstr "irlandzki" + +msgid "Scottish Gaelic" +msgstr "Szkocki gaelicki" + +msgid "Galician" +msgstr "galicyjski" + +msgid "Hebrew" +msgstr "hebrajski" + +msgid "Hindi" +msgstr "hindi" + +msgid "Croatian" +msgstr "chorwacki" + +msgid "Upper Sorbian" +msgstr "górnołużycki" + +msgid "Hungarian" +msgstr "węgierski" + +msgid "Armenian" +msgstr "ormiański" + +msgid "Interlingua" +msgstr "interlingua" + +msgid "Indonesian" +msgstr "indonezyjski" + +msgid "Igbo" +msgstr "igbo" + +msgid "Ido" +msgstr "ido" + +msgid "Icelandic" +msgstr "islandzki" + +msgid "Italian" +msgstr "włoski" + +msgid "Japanese" +msgstr "japoński" + +msgid "Georgian" +msgstr "gruziński" + +msgid "Kabyle" +msgstr "kabylski" + +msgid "Kazakh" +msgstr "kazachski" + +msgid "Khmer" +msgstr "khmerski" + +msgid "Kannada" +msgstr "kannada" + +msgid "Korean" +msgstr "koreański" + +msgid "Kyrgyz" +msgstr "kirgiski" + +msgid "Luxembourgish" +msgstr "luksemburski" + +msgid "Lithuanian" +msgstr "litewski" + +msgid "Latvian" +msgstr "łotewski" + +msgid "Macedonian" +msgstr "macedoński" + +msgid "Malayalam" +msgstr "malajski" + +msgid "Mongolian" +msgstr "mongolski" + +msgid "Marathi" +msgstr "marathi" + +msgid "Malay" +msgstr "malajski" + +msgid "Burmese" +msgstr "birmański" + +msgid "Norwegian Bokmål" +msgstr "norweski (bokmål)" + +msgid "Nepali" +msgstr "nepalski" + +msgid "Dutch" +msgstr "holenderski" + +msgid "Norwegian Nynorsk" +msgstr "norweski (nynorsk)" + +msgid "Ossetic" +msgstr "osetyjski" + +msgid "Punjabi" +msgstr "pendżabski" + +msgid "Polish" +msgstr "polski" + +msgid "Portuguese" +msgstr "portugalski" + +msgid "Brazilian Portuguese" +msgstr "portugalski brazylijski" + +msgid "Romanian" +msgstr "rumuński" + +msgid "Russian" +msgstr "rosyjski" + +msgid "Slovak" +msgstr "słowacki" + +msgid "Slovenian" +msgstr "słoweński" + +msgid "Albanian" +msgstr "albański" + +msgid "Serbian" +msgstr "serbski" + +msgid "Serbian Latin" +msgstr "serbski (łaciński)" + +msgid "Swedish" +msgstr "szwedzki" + +msgid "Swahili" +msgstr "suahili" + +msgid "Tamil" +msgstr "tamilski" + +msgid "Telugu" +msgstr "telugu" + +msgid "Tajik" +msgstr "tadżycki" + +msgid "Thai" +msgstr "tajski" + +msgid "Turkmen" +msgstr "turkmeński" + +msgid "Turkish" +msgstr "turecki" + +msgid "Tatar" +msgstr "tatarski" + +msgid "Udmurt" +msgstr "udmurcki" + +msgid "Ukrainian" +msgstr "ukraiński" + +msgid "Urdu" +msgstr "urdu" + +msgid "Uzbek" +msgstr "uzbecki" + +msgid "Vietnamese" +msgstr "wietnamski" + +msgid "Simplified Chinese" +msgstr "chiński uproszczony" + +msgid "Traditional Chinese" +msgstr "chiński tradycyjny" + +msgid "Messages" +msgstr "Wiadomości" + +msgid "Site Maps" +msgstr "Mapy stron" + +msgid "Static Files" +msgstr "Pliki statyczne" + +msgid "Syndication" +msgstr "Syndykacja treści" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "…" + +msgid "That page number is not an integer" +msgstr "Ten numer strony nie jest liczbą całkowitą" + +msgid "That page number is less than 1" +msgstr "Ten numer strony jest mniejszy niż 1" + +msgid "That page contains no results" +msgstr "Ta strona nie zawiera wyników" + +msgid "Enter a valid value." +msgstr "Wpisz poprawną wartość." + +msgid "Enter a valid URL." +msgstr "Wpisz poprawny URL." + +msgid "Enter a valid integer." +msgstr "Wprowadź poprawną liczbę całkowitą." + +msgid "Enter a valid email address." +msgstr "Wprowadź poprawny adres email." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" +"Wpisz poprawny „slug” zawierający litery, cyfry, podkreślenia i myślniki." + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"Wpisz poprawny „slug” zawierający litery Unicode, cyfry, podkreślenia i " +"myślniki." + +msgid "Enter a valid IPv4 address." +msgstr "Wprowadź poprawny adres IPv4." + +msgid "Enter a valid IPv6 address." +msgstr "Wprowadź poprawny adres IPv6." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "Wprowadź poprawny adres IPv4 lub IPv6." + +msgid "Enter only digits separated by commas." +msgstr "Wpisz tylko cyfry oddzielone przecinkami." + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "Upewnij się, że ta wartość jest %(limit_value)s (jest %(show_value)s)." + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "Upewnij się, że ta wartość jest mniejsza lub równa %(limit_value)s." + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "Upewnij się, że ta wartość jest większa lub równa %(limit_value)s." + +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "" +"Upewnij się, że ta wartość jest wielokrotnością wielkości " +"kroku%(limit_value)s." + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Upewnij się, że ta wartość ma przynajmniej %(limit_value)d znak (obecnie ma " +"%(show_value)d)." +msgstr[1] "" +"Upewnij się, że ta wartość ma przynajmniej %(limit_value)d znaki (obecnie ma " +"%(show_value)d)." +msgstr[2] "" +"Upewnij się, że ta wartość ma przynajmniej %(limit_value)d znaków (obecnie " +"ma %(show_value)d)." +msgstr[3] "" +"Upewnij się, że ta wartość ma przynajmniej %(limit_value)d znaków (obecnie " +"ma %(show_value)d)." + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Upewnij się, że ta wartość ma co najwyżej %(limit_value)d znak (obecnie ma " +"%(show_value)d)." +msgstr[1] "" +"Upewnij się, że ta wartość ma co najwyżej %(limit_value)d znaki (obecnie ma " +"%(show_value)d)." +msgstr[2] "" +"Upewnij się, że ta wartość ma co najwyżej %(limit_value)d znaków (obecnie ma " +"%(show_value)d)." +msgstr[3] "" +"Upewnij się, że ta wartość ma co najwyżej %(limit_value)d znaków (obecnie ma " +"%(show_value)d)." + +msgid "Enter a number." +msgstr "Wpisz liczbę." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "Upewnij się, że łącznie nie ma więcej niż %(max)s cyfry." +msgstr[1] "Upewnij się, że łącznie nie ma więcej niż %(max)s cyfry." +msgstr[2] "Upewnij się, że łącznie nie ma więcej niż %(max)s cyfr." +msgstr[3] "Upewnij się, że łącznie nie ma więcej niż %(max)s cyfr." + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "" +"Upewnij się, że liczba ma nie więcej niż %(max)s cyfrę po przecinku." +msgstr[1] "" +"Upewnij się, że liczba ma nie więcej niż %(max)s cyfry po przecinku." +msgstr[2] "Upewnij się, że liczba ma nie więcej niż %(max)s cyfr po przecinku." +msgstr[3] "Upewnij się, że liczba ma nie więcej niż %(max)s cyfr po przecinku." + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +"Upewnij się, że liczba ma nie więcej niż %(max)s cyfrę przed przecinkiem." +msgstr[1] "" +"Upewnij się, że liczba ma nie więcej niż %(max)s cyfry przed przecinkiem." +msgstr[2] "" +"Upewnij się, że liczba ma nie więcej niż %(max)s cyfr przed przecinkiem." +msgstr[3] "" +"Upewnij się, że liczba ma nie więcej niż %(max)s cyfr przed przecinkiem." + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"Rozszerzenie pliku „%(extension)s” jest niedozwolone. Dozwolone rozszerzenia " +"to: %(allowed_extensions)s." + +msgid "Null characters are not allowed." +msgstr "Znaki null są niedozwolone." + +msgid "and" +msgstr "i" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "%(model_name)s z tymi %(field_labels)s już istnieje." + +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "Ograniczenie \"%(name)s\" zostało naruszone." + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "Wartość %(value)r nie jest poprawnym wyborem." + +msgid "This field cannot be null." +msgstr "To pole nie może być puste." + +msgid "This field cannot be blank." +msgstr "To pole nie może być puste." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "Istnieje już %(model_name)s z tą wartością pola %(field_label)s." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"Wartość pola %(field_label)s musi być unikatowa dla %(date_field_label)s " +"%(lookup_type)s." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "Pole typu: %(field_type)s" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "Wartością „%(value)s” musi być True albo False." + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "Wartością „%(value)s” musi być True, False lub None." + +msgid "Boolean (Either True or False)" +msgstr "Wartość logiczna (True lub False – prawda lub fałsz)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "Ciąg znaków (do %(max_length)s znaków)" + +msgid "String (unlimited)" +msgstr "Ciąg znaków (bez limitu)" + +msgid "Comma-separated integers" +msgstr "Liczby całkowite rozdzielone przecinkami" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" +"Wartość „%(value)s” ma nieprawidłowy format daty. Musi być ona w formacie " +"YYYY-MM-DD." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" +"Wartość „%(value)s” ma prawidłowy format (YYYY-MM-DD), ale jest " +"nieprawidłową datą." + +msgid "Date (without time)" +msgstr "Data (bez godziny)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"Wartość „%(value)s” ma nieprawidłowy format. Musi być ona w formacie YYYY-MM-" +"DD HH:MM[:ss[.uuuuuu]][TZ]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"Wartość „%(value)s” ma prawidłowy format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]), ale jest nieprawidłową datą/godziną." + +msgid "Date (with time)" +msgstr "Data (z godziną)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "Wartością „%(value)s” musi być liczba dziesiętna." + +msgid "Decimal number" +msgstr "Liczba dziesiętna" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" +"Wartość „%(value)s” ma błędny format. Poprawny format to [DD] [HH:[MM:]]ss[." +"uuuuuu]." + +msgid "Duration" +msgstr "Czas trwania" + +msgid "Email address" +msgstr "Adres e-mail" + +msgid "File path" +msgstr "Ścieżka do pliku" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "Wartością „%(value)s” musi być liczba zmiennoprzecinkowa." + +msgid "Floating point number" +msgstr "Liczba zmiennoprzecinkowa" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "Wartością „%(value)s” musi być liczba całkowita." + +msgid "Integer" +msgstr "Liczba całkowita" + +msgid "Big (8 byte) integer" +msgstr "Duża liczba całkowita (8 bajtów)" + +msgid "Small integer" +msgstr "Mała liczba całkowita" + +msgid "IPv4 address" +msgstr "adres IPv4" + +msgid "IP address" +msgstr "Adres IP" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "Wartością „%(value)s” musi być None, True lub False." + +msgid "Boolean (Either True, False or None)" +msgstr "Wartość logiczna (True, False, None – prawda, fałsz lub nic)" + +msgid "Positive big integer" +msgstr "Dodatnia duża liczba całkowita" + +msgid "Positive integer" +msgstr "Dodatnia liczba całkowita" + +msgid "Positive small integer" +msgstr "Dodatnia mała liczba całkowita" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "Slug (do %(max_length)s znaków)" + +msgid "Text" +msgstr "Tekst" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" +"Wartość „%(value)s” ma nieprawidłowy format. Musi być ona w formacie HH:MM[:" +"ss[.uuuuuu]]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" +"Wartość „%(value)s” ma prawidłowy format (HH:MM[:ss[.uuuuuu]]), ale jest " +"nieprawidłową wartością czasu." + +msgid "Time" +msgstr "Czas" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "Dane w postaci binarnej" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "Wartość „%(value)s” nie jest poprawnym UUID-em." + +msgid "Universally unique identifier" +msgstr "Uniwersalnie unikalny identyfikator" + +msgid "File" +msgstr "Plik" + +msgid "Image" +msgstr "Plik graficzny" + +msgid "A JSON object" +msgstr "Obiekt JSON" + +msgid "Value must be valid JSON." +msgstr "Wartość musi być poprawnym JSON-em." + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "%(model)s z polem %(field)s o wartości %(value)r nie istnieje." + +msgid "Foreign Key (type determined by related field)" +msgstr "Klucz obcy (typ określony przez pole powiązane)" + +msgid "One-to-one relationship" +msgstr "Powiązanie jeden do jednego" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "powiązanie %(from)s do %(to)s" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "powiązania %(from)s do %(to)s" + +msgid "Many-to-many relationship" +msgstr "Powiązanie wiele-do-wielu" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "To pole jest wymagane." + +msgid "Enter a whole number." +msgstr "Wpisz liczbę całkowitą." + +msgid "Enter a valid date." +msgstr "Wpisz poprawną datę." + +msgid "Enter a valid time." +msgstr "Wpisz poprawną godzinę." + +msgid "Enter a valid date/time." +msgstr "Wpisz poprawną datę/godzinę." + +msgid "Enter a valid duration." +msgstr "Wpisz poprawny czas trwania." + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "Liczba dni musi wynosić między {min_days} a {max_days}." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "Nie wysłano żadnego pliku. Sprawdź typ kodowania formularza." + +msgid "No file was submitted." +msgstr "Żaden plik nie został przesłany." + +msgid "The submitted file is empty." +msgstr "Wysłany plik jest pusty." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"Upewnij się, że nazwa pliku ma co najwyżej %(max)d znak (obecnie ma " +"%(length)d)." +msgstr[1] "" +"Upewnij się, że nazwa pliku ma co najwyżej %(max)d znaki (obecnie ma " +"%(length)d)." +msgstr[2] "" +"Upewnij się, że nazwa pliku ma co najwyżej %(max)d znaków (obecnie ma " +"%(length)d)." +msgstr[3] "" +"Upewnij się, że nazwa pliku ma co najwyżej %(max)d znaków (obecnie ma " +"%(length)d)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "Prześlij plik lub zaznacz by usunąć, ale nie oba na raz." + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Prześlij poprawny plik graficzny. Aktualnie przesłany plik nie jest " +"grafiką lub jest uszkodzony." + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "Wybierz poprawną wartość. %(value)s nie jest żadną z dostępnych opcji." + +msgid "Enter a list of values." +msgstr "Podaj listę wartości." + +msgid "Enter a complete value." +msgstr "Wprowadź kompletną wartość." + +msgid "Enter a valid UUID." +msgstr "Wpisz poprawny UUID." + +msgid "Enter a valid JSON." +msgstr "Wpisz poprawny JSON." + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(Ukryte pole %(name)s) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" +"Brakuje danych ManagementForm lub zostały one naruszone. Brakujące pola: " +"%(field_names)s. Złóż zgłoszenie błędu, jeśli problem się powtarza." + +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "Proszę wysłać co najwyżej %(num)dformularz." +msgstr[1] "Proszę wysłać co najwyżej %(num)d formularze." +msgstr[2] "Proszę wysłać co najwyżej %(num)dformularzy." +msgstr[3] "Proszę wysłać co najwyżej %(num)dformularzy." + +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "Proszę wysłać co najmniej %(num)d formularz." +msgstr[1] "Proszę wysłać co najmniej %(num)dformularze." +msgstr[2] "Proszę wysłać co najmniej %(num)d formularzy." +msgstr[3] "Proszę wysłać co najmniej %(num)d formularzy." + +msgid "Order" +msgstr "Kolejność" + +msgid "Delete" +msgstr "Usuń" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "Popraw zduplikowane dane w %(field)s." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "Popraw zduplikowane dane w %(field)s, które muszą być unikalne." + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"Popraw zduplikowane dane w %(field_name)s, które wymaga unikalności dla " +"%(lookup)s w polu %(date_field)s." + +msgid "Please correct the duplicate values below." +msgstr "Popraw poniższe zduplikowane wartości." + +msgid "The inline value did not match the parent instance." +msgstr "Wartość inline nie pasuje do obiektu rodzica." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "Wybierz poprawną wartość. Podana nie jest jednym z dostępnych wyborów." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "„%(pk)s” nie jest poprawną wartością." + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"%(datetime)s nie mógł zostać zinterpretowany w strefie czasowej " +"%(current_timezone)s; może być niejednoznaczny lub może nie istnieć." + +msgid "Clear" +msgstr "Wyczyść" + +msgid "Currently" +msgstr "Teraz" + +msgid "Change" +msgstr "Zmień" + +msgid "Unknown" +msgstr "Nieznany" + +msgid "Yes" +msgstr "Tak" + +msgid "No" +msgstr "Nie" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "tak,nie,może" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d bajt" +msgstr[1] "%(size)d bajty" +msgstr[2] "%(size)d bajtów" +msgstr[3] "%(size)d bajtów" + +#, python-format +msgid "%s KB" +msgstr "%s KB" + +#, python-format +msgid "%s MB" +msgstr "%s MB" + +#, python-format +msgid "%s GB" +msgstr "%s GB" + +#, python-format +msgid "%s TB" +msgstr "%s TB" + +#, python-format +msgid "%s PB" +msgstr "%s PB" + +msgid "p.m." +msgstr "po południu" + +msgid "a.m." +msgstr "rano" + +msgid "PM" +msgstr "po południu" + +msgid "AM" +msgstr "rano" + +msgid "midnight" +msgstr "północ" + +msgid "noon" +msgstr "południe" + +msgid "Monday" +msgstr "Poniedziałek" + +msgid "Tuesday" +msgstr "Wtorek" + +msgid "Wednesday" +msgstr "Środa" + +msgid "Thursday" +msgstr "Czwartek" + +msgid "Friday" +msgstr "Piątek" + +msgid "Saturday" +msgstr "Sobota" + +msgid "Sunday" +msgstr "Niedziela" + +msgid "Mon" +msgstr "Pon" + +msgid "Tue" +msgstr "Wt" + +msgid "Wed" +msgstr "Śr" + +msgid "Thu" +msgstr "Czw" + +msgid "Fri" +msgstr "Pt" + +msgid "Sat" +msgstr "So" + +msgid "Sun" +msgstr "Nd" + +msgid "January" +msgstr "Styczeń" + +msgid "February" +msgstr "Luty" + +msgid "March" +msgstr "Marzec" + +msgid "April" +msgstr "Kwiecień" + +msgid "May" +msgstr "Maj" + +msgid "June" +msgstr "Czerwiec" + +msgid "July" +msgstr "Lipiec" + +msgid "August" +msgstr "Sierpień" + +msgid "September" +msgstr "Wrzesień" + +msgid "October" +msgstr "Październik" + +msgid "November" +msgstr "Listopad" + +msgid "December" +msgstr "Grudzień" + +msgid "jan" +msgstr "sty" + +msgid "feb" +msgstr "lut" + +msgid "mar" +msgstr "mar" + +msgid "apr" +msgstr "kwi" + +msgid "may" +msgstr "maj" + +msgid "jun" +msgstr "cze" + +msgid "jul" +msgstr "lip" + +msgid "aug" +msgstr "sie" + +msgid "sep" +msgstr "wrz" + +msgid "oct" +msgstr "paź" + +msgid "nov" +msgstr "lis" + +msgid "dec" +msgstr "gru" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "Sty." + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "Lut." + +msgctxt "abbrev. month" +msgid "March" +msgstr "Mar." + +msgctxt "abbrev. month" +msgid "April" +msgstr "Kwi." + +msgctxt "abbrev. month" +msgid "May" +msgstr "Maj" + +msgctxt "abbrev. month" +msgid "June" +msgstr "Cze." + +msgctxt "abbrev. month" +msgid "July" +msgstr "Lip." + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "Sie." + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "Wrz." + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "Paź." + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "Lis." + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "Gru" + +msgctxt "alt. month" +msgid "January" +msgstr "stycznia" + +msgctxt "alt. month" +msgid "February" +msgstr "lutego" + +msgctxt "alt. month" +msgid "March" +msgstr "marca" + +msgctxt "alt. month" +msgid "April" +msgstr "kwietnia" + +msgctxt "alt. month" +msgid "May" +msgstr "maja" + +msgctxt "alt. month" +msgid "June" +msgstr "czerwca" + +msgctxt "alt. month" +msgid "July" +msgstr "lipca" + +msgctxt "alt. month" +msgid "August" +msgstr "sierpnia" + +msgctxt "alt. month" +msgid "September" +msgstr "września" + +msgctxt "alt. month" +msgid "October" +msgstr "października" + +msgctxt "alt. month" +msgid "November" +msgstr "listopada" + +msgctxt "alt. month" +msgid "December" +msgstr "grudnia" + +msgid "This is not a valid IPv6 address." +msgstr "To nie jest poprawny adres IPv6." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "lub" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "%(num)d rok" +msgstr[1] "%(num)d lata" +msgstr[2] "%(num)d lat" +msgstr[3] "%(num)d roku" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "%(num)d miesiąc" +msgstr[1] "%(num)d miesiące" +msgstr[2] "%(num)d miesięcy" +msgstr[3] "%(num)d miesiąca" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "%(num)d tydzień" +msgstr[1] "%(num)d tygodnie" +msgstr[2] "%(num)d tygodni" +msgstr[3] "%(num)d tygodnia" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "%(num)d dzień" +msgstr[1] "%(num)d dni" +msgstr[2] "%(num)d dni" +msgstr[3] "%(num)d dnia" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "%(num)d godzina" +msgstr[1] "%(num)d godziny" +msgstr[2] "%(num)d godzin" +msgstr[3] "%(num)d godziny" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "%(num)d minuta" +msgstr[1] "%(num)d minuty" +msgstr[2] "%(num)d minut" +msgstr[3] "%(num)d minut" + +msgid "Forbidden" +msgstr "Dostęp zabroniony" + +msgid "CSRF verification failed. Request aborted." +msgstr "Weryfikacja CSRF nie powiodła się. Żądanie zostało przerwane." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" +"Widzisz tę wiadomość, ponieważ ta witryna HTTPS wymaga, aby przeglądarka " +"wysłała „nagłówek Referer”, a żaden nie został wysłany. Nagłówek ten jest " +"wymagany ze względów bezpieczeństwa, aby upewnić się, że twoja przeglądarka " +"nie została przechwycona przez osoby trzecie." + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"Jeżeli nagłówki „Referer” w Twojej przeglądarce są wyłączone, to proszę " +"włącz je ponownie. Przynajmniej dla tej strony, połączeń HTTPS lub zapytań " +"typu „same-origin”." + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"Jeśli używasz taga lub " +"umieszczasz nagłówek „Referrer-Policy: no-referrer”, prosimy je usunąć. " +"Ochrona przed atakami CSRF wymaga nagłówka „Referer”, aby wykonać ścisłe " +"sprawdzenie referera HTTP. Jeśli zależy ci na prywatności, użyj alternatyw " +"takich jak dla linków do stron osób trzecich." + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"Widzisz tą wiadomość, ponieważ ta witryna wymaga ciasteczka CSRF do " +"przesyłania formularza. Ciasteczko to jest wymagane ze względów " +"bezpieczeństwa, aby upewnić się, że Twoja przeglądarka nie została " +"przechwycona przez osoby trzecie." + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" +"Jeżeli ciasteczka w Twojej przeglądarce są wyłączone, to proszę włącz je " +"ponownie. Przynajmniej dla tej strony lub żądań typu „same-origin”." + +msgid "More information is available with DEBUG=True." +msgstr "Więcej informacji jest dostępnych po ustawieniu DEBUG=True." + +msgid "No year specified" +msgstr "Nie określono roku" + +msgid "Date out of range" +msgstr "Data poza zakresem" + +msgid "No month specified" +msgstr "Nie określono miesiąca" + +msgid "No day specified" +msgstr "Nie określono dnia" + +msgid "No week specified" +msgstr "Nie określono tygodnia" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "%(verbose_name_plural)s nie są dostępne" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"Wyświetlanie %(verbose_name_plural)s z datą przyszłą jest niedostępne, gdyż " +"atrybut '%(class_name)s.allow_future' ma wartość 'False'." + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" +"Ciąg znaków „%(datestr)s” jest niezgodny z podanym formatem daty „%(format)s”" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "Nie znaleziono %(verbose_name)s spełniających wybrane kryteria" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" +"Podanego numeru strony nie można przekształcić na liczbę całkowitą, nie " +"przyjął on również wartości „last” oznaczającej ostatnią stronę z dostępnego " +"zakresu." + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Nieprawidłowy numer strony (%(page_number)s): %(message)s " + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" +"Lista nie zawiera żadnych elementów, a atrybut „%(class_name)s.allow_empty” " +"ma wartość False." + +msgid "Directory indexes are not allowed here." +msgstr "Wyświetlanie zawartości katalogu jest tu niedozwolone." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "„%(path)s” nie istnieje" + +#, python-format +msgid "Index of %(directory)s" +msgstr "Zawartość %(directory)s " + +msgid "The install worked successfully! Congratulations!" +msgstr "Instalacja przebiegła pomyślnie! Gratulacje!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"Zobacz informacje o wydaniu dla Django " +"%(version)s" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not " +"configured any URLs." +msgstr "" +"Widzisz tę stronę, ponieważ w swoim pliku ustawień masz DEBUG=True i nie skonfigurowałeś " +"żadnych URL-i." + +msgid "Django Documentation" +msgstr "Dokumentacja Django" + +msgid "Topics, references, & how-to’s" +msgstr "Przewodniki tematyczne, podręczniki i przewodniki „jak to zrobić”" + +msgid "Tutorial: A Polling App" +msgstr "Samouczek: Aplikacja ankietowa" + +msgid "Get started with Django" +msgstr "Pierwsze kroki z Django" + +msgid "Django Community" +msgstr "Społeczność Django" + +msgid "Connect, get help, or contribute" +msgstr "Nawiąż kontakt, uzyskaj pomoc lub wnieś swój wkład" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/pt_BR/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/pt_BR/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..6d1c776e --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/pt_BR/LC_MESSAGES/django 3.po @@ -0,0 +1,1381 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Allisson Azevedo , 2014 +# Amanda Savluchinske , 2019 +# amcorreia , 2018 +# andrewsmedina , 2014-2015 +# Arthur Silva , 2017 +# bruno.devpod , 2014 +# Camilo B. Moreira , 2017 +# Carlos C. Leite , 2020 +# Carlos C. Leite , 2016,2019 +# Filipe Cifali , 2016 +# Claudio Rogerio Carvalho Filho , 2020 +# dudanogueira , 2012 +# dudanogueira , 2019 +# Elyézer Rezende , 2013 +# Fábio C. Barrionuevo da Luz , 2014-2015 +# Felipe Rodrigues , 2016 +# Filipe Cifali , 2019 +# Gladson , 2013 +# semente, 2011-2014 +# Guilherme, 2022 +# Heron Fonsaca, 2022 +# Igor Cavalcante , 2017 +# Jannis Leidel , 2011 +# Jonas Rodrigues, 2023 +# Lucas Infante , 2015 +# Luiz Boaretto , 2017 +# Marssal Jr. , 2022 +# Marcelo Moro Brondani , 2018 +# Mariusz Felisiak , 2021 +# Rafael Fontenelle , 2021-2022 +# Samuel Nogueira Bacelar , 2020 +# Sandro , 2011 +# Sergio Garcia , 2015 +# Tânia Andrea , 2017 +# Wiliam Souza , 2015 +# Francisco Petry Rauber , 2018 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 06:49+0000\n" +"Last-Translator: Jonas Rodrigues, 2023\n" +"Language-Team: Portuguese (Brazil) (http://www.transifex.com/django/django/" +"language/pt_BR/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pt_BR\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +msgid "Afrikaans" +msgstr "Africânder" + +msgid "Arabic" +msgstr "Árabe" + +msgid "Algerian Arabic" +msgstr "Árabe Argelino" + +msgid "Asturian" +msgstr "Asturiano" + +msgid "Azerbaijani" +msgstr "Azerbaijão" + +msgid "Bulgarian" +msgstr "Búlgaro" + +msgid "Belarusian" +msgstr "Bielorrussa" + +msgid "Bengali" +msgstr "Bengali" + +msgid "Breton" +msgstr "Bretão" + +msgid "Bosnian" +msgstr "Bósnio" + +msgid "Catalan" +msgstr "Catalão" + +msgid "Central Kurdish (Sorani)" +msgstr "Curdo Central (Sorâni)" + +msgid "Czech" +msgstr "Tcheco" + +msgid "Welsh" +msgstr "Galês" + +msgid "Danish" +msgstr "Dinamarquês" + +msgid "German" +msgstr "Alemão" + +msgid "Lower Sorbian" +msgstr "Sorábio Baixo" + +msgid "Greek" +msgstr "Grego" + +msgid "English" +msgstr "Inglês" + +msgid "Australian English" +msgstr "Inglês Australiano" + +msgid "British English" +msgstr "Inglês Britânico" + +msgid "Esperanto" +msgstr "Esperanto" + +msgid "Spanish" +msgstr "Espanhol" + +msgid "Argentinian Spanish" +msgstr "Espanhol Argentino" + +msgid "Colombian Spanish" +msgstr "Espanhol Colombiano" + +msgid "Mexican Spanish" +msgstr "Espanhol Mexicano" + +msgid "Nicaraguan Spanish" +msgstr "Espanhol Nicaraguense" + +msgid "Venezuelan Spanish" +msgstr "Espanhol Venuzuelano" + +msgid "Estonian" +msgstr "Estoniano" + +msgid "Basque" +msgstr "Basco" + +msgid "Persian" +msgstr "Persa" + +msgid "Finnish" +msgstr "Finlandês" + +msgid "French" +msgstr "Francês" + +msgid "Frisian" +msgstr "Frísia" + +msgid "Irish" +msgstr "Irlandês" + +msgid "Scottish Gaelic" +msgstr "Gaélico Escocês" + +msgid "Galician" +msgstr "Galiciano" + +msgid "Hebrew" +msgstr "Hebraico" + +msgid "Hindi" +msgstr "Hindi" + +msgid "Croatian" +msgstr "Croata" + +msgid "Upper Sorbian" +msgstr "Sorábio Alto" + +msgid "Hungarian" +msgstr "Húngaro" + +msgid "Armenian" +msgstr "Armênio" + +msgid "Interlingua" +msgstr "Interlíngua" + +msgid "Indonesian" +msgstr "Indonésio" + +msgid "Igbo" +msgstr "Igbo" + +msgid "Ido" +msgstr "Ido" + +msgid "Icelandic" +msgstr "Islandês" + +msgid "Italian" +msgstr "Italiano" + +msgid "Japanese" +msgstr "Japonês" + +msgid "Georgian" +msgstr "Georgiano" + +msgid "Kabyle" +msgstr "Cabila" + +msgid "Kazakh" +msgstr "Cazaque" + +msgid "Khmer" +msgstr "Khmer" + +msgid "Kannada" +msgstr "Canarês" + +msgid "Korean" +msgstr "Coreano" + +msgid "Kyrgyz" +msgstr "Quirguiz" + +msgid "Luxembourgish" +msgstr "Luxemburguês" + +msgid "Lithuanian" +msgstr "Lituano" + +msgid "Latvian" +msgstr "Letão" + +msgid "Macedonian" +msgstr "Macedônio" + +msgid "Malayalam" +msgstr "Malaiala" + +msgid "Mongolian" +msgstr "Mongol" + +msgid "Marathi" +msgstr "Marathi" + +msgid "Malay" +msgstr "Malaia" + +msgid "Burmese" +msgstr "Birmanês" + +msgid "Norwegian Bokmål" +msgstr "Dano-norueguês" + +msgid "Nepali" +msgstr "Nepalês" + +msgid "Dutch" +msgstr "Neerlandês" + +msgid "Norwegian Nynorsk" +msgstr "Novo Norueguês" + +msgid "Ossetic" +msgstr "Osseto" + +msgid "Punjabi" +msgstr "Punjabi" + +msgid "Polish" +msgstr "Polonês" + +msgid "Portuguese" +msgstr "Português" + +msgid "Brazilian Portuguese" +msgstr "Português Brasileiro" + +msgid "Romanian" +msgstr "Romeno" + +msgid "Russian" +msgstr "Russo" + +msgid "Slovak" +msgstr "Eslovaco" + +msgid "Slovenian" +msgstr "Esloveno" + +msgid "Albanian" +msgstr "Albanesa" + +msgid "Serbian" +msgstr "Sérvio" + +msgid "Serbian Latin" +msgstr "Sérvio Latino" + +msgid "Swedish" +msgstr "Sueco" + +msgid "Swahili" +msgstr "Suaíli" + +msgid "Tamil" +msgstr "Tâmil" + +msgid "Telugu" +msgstr "Telugu" + +msgid "Tajik" +msgstr "Tadjique" + +msgid "Thai" +msgstr "Tailandês" + +msgid "Turkmen" +msgstr "Turcomano" + +msgid "Turkish" +msgstr "Turco" + +msgid "Tatar" +msgstr "Tatar" + +msgid "Udmurt" +msgstr "Udmurt" + +msgid "Ukrainian" +msgstr "Ucraniano" + +msgid "Urdu" +msgstr "Urdu" + +msgid "Uzbek" +msgstr "Uzbeque" + +msgid "Vietnamese" +msgstr "Vietnamita" + +msgid "Simplified Chinese" +msgstr "Chinês Simplificado" + +msgid "Traditional Chinese" +msgstr "Chinês Tradicional" + +msgid "Messages" +msgstr "Mensagens" + +msgid "Site Maps" +msgstr "Site Maps" + +msgid "Static Files" +msgstr "Arquivos Estáticos" + +msgid "Syndication" +msgstr "Syndication" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "…" + +msgid "That page number is not an integer" +msgstr "Esse número de página não é um número inteiro" + +msgid "That page number is less than 1" +msgstr "Esse número de página é menor que 1" + +msgid "That page contains no results" +msgstr "Essa página não contém resultados" + +msgid "Enter a valid value." +msgstr "Informe um valor válido." + +msgid "Enter a valid URL." +msgstr "Informe uma URL válida." + +msgid "Enter a valid integer." +msgstr "Insira um número inteiro válido." + +msgid "Enter a valid email address." +msgstr "Informe um endereço de email válido." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" +"Informe um “slug” válido tendo letras, números, \"underscores\" e hífens." + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"Informe um “slug” válido tendo letras em Unicode, números, \"underscores\" e " +"hífens." + +msgid "Enter a valid IPv4 address." +msgstr "Insira um endereço IPv4 válido." + +msgid "Enter a valid IPv6 address." +msgstr "Insira um endereço IPv6 válido." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "Insira um endereço IPv4 ou IPv6 válido." + +msgid "Enter only digits separated by commas." +msgstr "Insira apenas dígitos separados por vírgulas." + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "Certifique-se de que o valor é %(limit_value)s (ele é %(show_value)s)." + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "Certifique-se que este valor seja menor ou igual a %(limit_value)s." + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "Certifique-se que este valor seja maior ou igual a %(limit_value)s." + +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "" +"Certifique-se que este valor seja múltiplo do tamanho do passo " +"%(limit_value)s." + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Certifique-se de que o valor tenha no mínimo %(limit_value)d caractere (ele " +"possui %(show_value)d)." +msgstr[1] "" +"Certifique-se de que o valor tenha no mínimo %(limit_value)d caracteres (ele " +"possui %(show_value)d)." +msgstr[2] "" +"Certifique-se de que o valor tenha no mínimo %(limit_value)d caracteres (ele " +"possui %(show_value)d)." + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Certifique-se de que o valor tenha no máximo %(limit_value)d caractere (ele " +"possui %(show_value)d)." +msgstr[1] "" +"Certifique-se de que o valor tenha no máximo %(limit_value)d caracteres (ele " +"possui %(show_value)d)." +msgstr[2] "" +"Certifique-se de que o valor tenha no máximo %(limit_value)d caracteres (ele " +"possui %(show_value)d)." + +msgid "Enter a number." +msgstr "Informe um número." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "Certifique-se de que não tenha mais de %(max)s dígito no total." +msgstr[1] "Certifique-se de que não tenha mais de %(max)s dígitos no total." +msgstr[2] "Certifique-se de que não tenha mais de %(max)s dígitos no total." + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "Certifique-se de que não tenha mais de %(max)s casa decimal." +msgstr[1] "Certifique-se de que não tenha mais de %(max)s casas decimais." +msgstr[2] "Certifique-se de que não tenha mais de %(max)s casas decimais." + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +"Certifique-se de que não tenha mais de %(max)s dígito antes do ponto decimal." +msgstr[1] "" +"Certifique-se de que não tenha mais de %(max)s dígitos antes do ponto " +"decimal." +msgstr[2] "" +"Certifique-se de que não tenha mais de %(max)s dígitos antes do ponto " +"decimal." + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"A extensão de arquivo “%(extension)s” não é permitida. As extensões válidas " +"são: %(allowed_extensions)s ." + +msgid "Null characters are not allowed." +msgstr "Caracteres nulos não são permitidos." + +msgid "and" +msgstr "e" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "%(model_name)s com este %(field_labels)s já existe." + +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "Restrição \"%(name)s\" foi violada." + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "Valor %(value)r não é uma opção válida." + +msgid "This field cannot be null." +msgstr "Este campo não pode ser nulo." + +msgid "This field cannot be blank." +msgstr "Este campo não pode estar vazio." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "%(model_name)s com este %(field_label)s já existe." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"%(field_label)s deve ser único para %(date_field_label)s %(lookup_type)s." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "Campo do tipo: %(field_type)s" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "o valor “%(value)s” deve ser True ou False." + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "o valor “%(value)s” deve ser True, False ou None." + +msgid "Boolean (Either True or False)" +msgstr "Booleano (Verdadeiro ou Falso)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "String (até %(max_length)s)" + +msgid "String (unlimited)" +msgstr "String (ilimitado)" + +msgid "Comma-separated integers" +msgstr "Inteiros separados por vírgula" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" +"O valor \"%(value)s\" tem um formato de data inválido. Deve ser no formato " +"YYYY-MM-DD." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" +"O valor “%(value)s” tem o formato correto (YYYY-MM-DD) mas uma data inválida." + +msgid "Date (without time)" +msgstr "Data (sem hora)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"O valor “%(value)s” tem um formato inválido. Deve estar no formato YYYY-MM-" +"DD HH:MM[:ss[.uuuuuu]][TZ]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"O valor “%(value)s” está no formato correto. (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) mas é uma data/hora inválida" + +msgid "Date (with time)" +msgstr "Data (com hora)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "O valor “%(value)s” deve ser um número decimal." + +msgid "Decimal number" +msgstr "Número decimal" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" +"O valor “%(value)s” está em um formato inválido. Deve ser no formato [DD] " +"[[HH:]MM:]ss[.uuuuuu]." + +msgid "Duration" +msgstr "Duração" + +msgid "Email address" +msgstr "Endereço de e-mail" + +msgid "File path" +msgstr "Caminho do arquivo" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "O valor “%(value)s” deve ser um float." + +msgid "Floating point number" +msgstr "Número de ponto flutuante" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "O valor “%(value)s” deve ser inteiro." + +msgid "Integer" +msgstr "Inteiro" + +msgid "Big (8 byte) integer" +msgstr "Inteiro grande (8 byte)" + +msgid "Small integer" +msgstr "Inteiro curto" + +msgid "IPv4 address" +msgstr "Endereço IPv4" + +msgid "IP address" +msgstr "Endereço IP" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "O valor “%(value)s” deve ser None, True ou False." + +msgid "Boolean (Either True, False or None)" +msgstr "Booleano (Verdadeiro, Falso ou Nada)" + +msgid "Positive big integer" +msgstr "Inteiro grande positivo" + +msgid "Positive integer" +msgstr "Inteiro positivo" + +msgid "Positive small integer" +msgstr "Inteiro curto positivo" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "Slug (até %(max_length)s)" + +msgid "Text" +msgstr "Texto" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" +"O valor “%(value)s” tem um formato inválido. Deve estar no formato HH:MM[:" +"ss[.uuuuuu]]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" +"O valor “%(value)s” está no formato correto (HH:MM[:ss[.uuuuuu]]) mas é uma " +"hora inválida." + +msgid "Time" +msgstr "Hora" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "Dados binários bruto" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "O valor “%(value)s” não é um UUID válido" + +msgid "Universally unique identifier" +msgstr "Identificador único universal" + +msgid "File" +msgstr "Arquivo" + +msgid "Image" +msgstr "Imagem" + +msgid "A JSON object" +msgstr "Um objeto JSON" + +msgid "Value must be valid JSON." +msgstr "O valor deve ser um JSON válido." + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "A instância de %(model)s com %(field)s %(value)r não existe." + +msgid "Foreign Key (type determined by related field)" +msgstr "Chave Estrangeira (tipo determinado pelo campo relacionado)" + +msgid "One-to-one relationship" +msgstr "Relacionamento um-para-um" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "Relacionamento %(from)s-%(to)s" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "Relacionamentos %(from)s-%(to)s" + +msgid "Many-to-many relationship" +msgstr "Relacionamento muitos-para-muitos" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "Este campo é obrigatório." + +msgid "Enter a whole number." +msgstr "Informe um número inteiro." + +msgid "Enter a valid date." +msgstr "Informe uma data válida." + +msgid "Enter a valid time." +msgstr "Informe uma hora válida." + +msgid "Enter a valid date/time." +msgstr "Informe uma data/hora válida." + +msgid "Enter a valid duration." +msgstr "Insira uma duração válida." + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "O número de dias deve ser entre {min_days} e {max_days}." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "Nenhum arquivo enviado. Verifique o tipo de codificação do formulário." + +msgid "No file was submitted." +msgstr "Nenhum arquivo foi enviado." + +msgid "The submitted file is empty." +msgstr "O arquivo enviado está vazio." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"Certifique-se de que o arquivo tenha no máximo %(max)d caractere (ele possui " +"%(length)d)." +msgstr[1] "" +"Certifique-se de que o arquivo tenha no máximo %(max)d caracteres (ele " +"possui %(length)d)." +msgstr[2] "" +"Certifique-se de que o arquivo tenha no máximo %(max)d caracteres (ele " +"possui %(length)d)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "Por favor, envie um arquivo ou marque o checkbox, mas não ambos." + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Envie uma imagem válida. O arquivo enviado não é uma imagem ou está " +"corrompido." + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "Faça uma escolha válida. %(value)s não é uma das escolhas disponíveis." + +msgid "Enter a list of values." +msgstr "Informe uma lista de valores." + +msgid "Enter a complete value." +msgstr "Insira um valor completo." + +msgid "Enter a valid UUID." +msgstr "Insira um UUID válido." + +msgid "Enter a valid JSON." +msgstr "Insira um JSON válido" + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(Campo oculto %(name)s) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" +"Dados de ManagementForm estão faltando ou foram adulterados. Campos " +"ausentes: %(field_names)s. Você pode precisar enviar um relatório de bug se " +"o problema persistir." + +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "Por favor, envie no máximo %(num)d formulário." +msgstr[1] "Por favor, envie no máximo %(num)d formulários." +msgstr[2] "Por favor, envie no máximo %(num)d formulários." + +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "Por favor, envie ao menos %(num)d formulário." +msgstr[1] "Por favor, envie ao menos %(num)d formulários." +msgstr[2] "Por favor, envie ao menos %(num)d formulários." + +msgid "Order" +msgstr "Ordem" + +msgid "Delete" +msgstr "Remover" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "Por favor, corrija o valor duplicado para %(field)s." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" +"Por favor, corrija o valor duplicado para %(field)s, o qual deve ser único." + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"Por favor, corrija o dado duplicado para %(field_name)s, o qual deve ser " +"único para %(lookup)s em %(date_field)s." + +msgid "Please correct the duplicate values below." +msgstr "Por favor, corrija os valores duplicados abaixo." + +msgid "The inline value did not match the parent instance." +msgstr "O valor na linha não correspondeu com a instância pai." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "Faça uma escolha válida. Sua escolha não é uma das disponíveis." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "“%(pk)s” não é um valor válido." + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"%(datetime)s não pode ser interpretada dentro da fuso horário " +"%(current_timezone)s; está ambíguo ou não existe." + +msgid "Clear" +msgstr "Limpar" + +msgid "Currently" +msgstr "Atualmente" + +msgid "Change" +msgstr "Modificar" + +msgid "Unknown" +msgstr "Desconhecido" + +msgid "Yes" +msgstr "Sim" + +msgid "No" +msgstr "Não" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "sim,não,talvez" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d byte" +msgstr[1] "%(size)d bytes" +msgstr[2] "%(size)d bytes" + +#, python-format +msgid "%s KB" +msgstr "%s KB" + +#, python-format +msgid "%s MB" +msgstr "%s MB" + +#, python-format +msgid "%s GB" +msgstr "%s GB" + +#, python-format +msgid "%s TB" +msgstr "%s TB" + +#, python-format +msgid "%s PB" +msgstr "%s PB" + +msgid "p.m." +msgstr "p.m." + +msgid "a.m." +msgstr "a.m." + +msgid "PM" +msgstr "PM" + +msgid "AM" +msgstr "AM" + +msgid "midnight" +msgstr "meia-noite" + +msgid "noon" +msgstr "meio-dia" + +msgid "Monday" +msgstr "Segunda-feira" + +msgid "Tuesday" +msgstr "Terça-feira" + +msgid "Wednesday" +msgstr "Quarta-feira" + +msgid "Thursday" +msgstr "Quinta-feira" + +msgid "Friday" +msgstr "Sexta-feira" + +msgid "Saturday" +msgstr "Sábado" + +msgid "Sunday" +msgstr "Domingo" + +msgid "Mon" +msgstr "Seg" + +msgid "Tue" +msgstr "Ter" + +msgid "Wed" +msgstr "Qua" + +msgid "Thu" +msgstr "Qui" + +msgid "Fri" +msgstr "Sex" + +msgid "Sat" +msgstr "Sab" + +msgid "Sun" +msgstr "Dom" + +msgid "January" +msgstr "Janeiro" + +msgid "February" +msgstr "Fevereiro" + +msgid "March" +msgstr "Março" + +msgid "April" +msgstr "Abril" + +msgid "May" +msgstr "Maio" + +msgid "June" +msgstr "Junho" + +msgid "July" +msgstr "Julho" + +msgid "August" +msgstr "Agosto" + +msgid "September" +msgstr "Setembro" + +msgid "October" +msgstr "Outubro" + +msgid "November" +msgstr "Novembro" + +msgid "December" +msgstr "Dezembro" + +msgid "jan" +msgstr "jan" + +msgid "feb" +msgstr "fev" + +msgid "mar" +msgstr "mar" + +msgid "apr" +msgstr "abr" + +msgid "may" +msgstr "mai" + +msgid "jun" +msgstr "jun" + +msgid "jul" +msgstr "jul" + +msgid "aug" +msgstr "ago" + +msgid "sep" +msgstr "set" + +msgid "oct" +msgstr "out" + +msgid "nov" +msgstr "nov" + +msgid "dec" +msgstr "dez" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "Jan." + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "Fev." + +msgctxt "abbrev. month" +msgid "March" +msgstr "Março" + +msgctxt "abbrev. month" +msgid "April" +msgstr "Abril" + +msgctxt "abbrev. month" +msgid "May" +msgstr "Maio" + +msgctxt "abbrev. month" +msgid "June" +msgstr "Junho" + +msgctxt "abbrev. month" +msgid "July" +msgstr "Julho" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "Ago." + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "Set." + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "Out." + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "Nov." + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "Dez." + +msgctxt "alt. month" +msgid "January" +msgstr "Janeiro" + +msgctxt "alt. month" +msgid "February" +msgstr "Fevereiro" + +msgctxt "alt. month" +msgid "March" +msgstr "Março" + +msgctxt "alt. month" +msgid "April" +msgstr "Abril" + +msgctxt "alt. month" +msgid "May" +msgstr "Maio" + +msgctxt "alt. month" +msgid "June" +msgstr "Junho" + +msgctxt "alt. month" +msgid "July" +msgstr "Julho" + +msgctxt "alt. month" +msgid "August" +msgstr "Agosto" + +msgctxt "alt. month" +msgid "September" +msgstr "Setembro" + +msgctxt "alt. month" +msgid "October" +msgstr "Outubro" + +msgctxt "alt. month" +msgid "November" +msgstr "Novembro" + +msgctxt "alt. month" +msgid "December" +msgstr "Dezembro" + +msgid "This is not a valid IPv6 address." +msgstr "Este não é um endereço IPv6 válido." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr " %(truncated_text)s…" + +msgid "or" +msgstr "ou" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "%(num)d ano" +msgstr[1] "%(num)d anos" +msgstr[2] "%(num)d anos" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "%(num)d mês" +msgstr[1] "%(num)d meses" +msgstr[2] "%(num)d meses" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "%(num)d semana" +msgstr[1] "%(num)d semanas" +msgstr[2] "%(num)d semanas" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "%(num)d dia" +msgstr[1] "%(num)d dias" +msgstr[2] "%(num)d dias" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "%(num)d hora" +msgstr[1] "%(num)d horas" +msgstr[2] "%(num)d horas" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "%(num)d minuto" +msgstr[1] "%(num)d minutos" +msgstr[2] "%(num)d minutos" + +msgid "Forbidden" +msgstr "Proibido" + +msgid "CSRF verification failed. Request aborted." +msgstr "Verificação CSRF falhou. Pedido cancelado." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" +"Você está vendo esta mensagem porque este site HTTPS requer que um " +"“cabeçalho Refer” seja enviado pelo seu navegador da web, mas nenhum foi " +"enviado. Este cabeçalho é necessário por motivos de segurança, para garantir " +"que seu navegador não seja invadido por terceiros." + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"Se você configurou seu browser para desabilitar os cabeçalhos “Referer”, por " +"favor reabilite-os, ao menos para este site, ou para conexões HTTPS, ou para " +"requisições “same-origin”." + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"Se estiver usando a tag ou " +"incluindo o cabeçalho “Referrer-Policy: no-referrer”, por favor remova-os. A " +"proteção CSRF requer o cabeçalho “Referer” para fazer a checagem de " +"referência. Se estiver preocupado com privacidade, use alternativas como para links de sites de terceiros." + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"Você está vendo esta mensagem, porque este site requer um cookie CSRF no " +"envio de formulários. Este cookie é necessário por razões de segurança, para " +"garantir que o seu browser não está sendo sequestrado por terceiros." + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" +"Se você configurou seu browser para desabilitar cookies, por favor reabilite-" +"os, ao menos para este site ou para requisições do tipo \"same-origin\"." + +msgid "More information is available with DEBUG=True." +msgstr "Mais informações estão disponíveis com DEBUG=True." + +msgid "No year specified" +msgstr "Ano não especificado" + +msgid "Date out of range" +msgstr "Data fora de alcance" + +msgid "No month specified" +msgstr "Mês não especificado" + +msgid "No day specified" +msgstr "Dia não especificado" + +msgid "No week specified" +msgstr "Semana não especificada" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "Nenhum(a) %(verbose_name_plural)s disponível" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"%(verbose_name_plural)s futuros não disponíveis pois %(class_name)s." +"allow_future é False." + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" +"String de data com formato inválido “%(datestr)s” dado o formato “%(format)s”" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "%(verbose_name)s não encontrado de acordo com a consulta" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "Página não é “last”, e também não pode ser convertida para um int." + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Página inválida (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "Lista vazia e o \"%(class_name)s.allow_empty\" está como False." + +msgid "Directory indexes are not allowed here." +msgstr "Índices de diretório não são permitidos aqui." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "\"%(path)s\" não existe" + +#, python-format +msgid "Index of %(directory)s" +msgstr "Índice de %(directory)s " + +msgid "The install worked successfully! Congratulations!" +msgstr "A instalação foi com sucesso! Parabéns!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"Ver as notas de lançamento do Django " +"%(version)s" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not " +"configured any URLs." +msgstr "" +"Você está vendo esta página pois possui DEBUG=True no seu arquivo de configurações e não " +"configurou nenhuma URL." + +msgid "Django Documentation" +msgstr "Documentação do Django" + +msgid "Topics, references, & how-to’s" +msgstr "Tópicos, referências, & how-to’s" + +msgid "Tutorial: A Polling App" +msgstr "Tutorial: Um aplicativo de votação" + +msgid "Get started with Django" +msgstr "Comece a usar Django" + +msgid "Django Community" +msgstr "Comunidade Django" + +msgid "Connect, get help, or contribute" +msgstr "Conecte-se, obtenha ajuda ou contribua" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/ru/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/ru/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..e6b88096 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/ru/LC_MESSAGES/django 3.po @@ -0,0 +1,1399 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Mingun , 2014 +# Anton Bazhanov , 2017 +# Denis Darii , 2011 +# Dimmus , 2011 +# eigrad , 2012 +# Eugene , 2013 +# Eugene Morozov , 2021 +# eXtractor , 2015 +# crazyzubr , 2020 +# Igor Melnyk, 2014 +# Ivan Khomutov , 2017 +# Jannis Leidel , 2011 +# lilo.panic, 2016 +# Mikhail Zholobov , 2013 +# Nikolay Korotkiy , 2018 +# Panasoft, 2021 +# Вася Аникин , 2017 +# SeryiMysh , 2020 +# Алексей Борискин , 2013-2017,2019-2020,2022 +# Bobsans , 2016,2018 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:23-0500\n" +"PO-Revision-Date: 2022-05-25 06:49+0000\n" +"Last-Translator: Алексей Борискин , " +"2013-2017,2019-2020,2022\n" +"Language-Team: Russian (http://www.transifex.com/django/django/language/" +"ru/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ru\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || " +"(n%100>=11 && n%100<=14)? 2 : 3);\n" + +msgid "Afrikaans" +msgstr "Бурский" + +msgid "Arabic" +msgstr "Арабский" + +msgid "Algerian Arabic" +msgstr "Алжирский арабский" + +msgid "Asturian" +msgstr "Астурийский" + +msgid "Azerbaijani" +msgstr "Азербайджанский" + +msgid "Bulgarian" +msgstr "Болгарский" + +msgid "Belarusian" +msgstr "Белоруский" + +msgid "Bengali" +msgstr "Бенгальский" + +msgid "Breton" +msgstr "Бретонский" + +msgid "Bosnian" +msgstr "Боснийский" + +msgid "Catalan" +msgstr "Каталанский" + +msgid "Czech" +msgstr "Чешский" + +msgid "Welsh" +msgstr "Уэльский" + +msgid "Danish" +msgstr "Датский" + +msgid "German" +msgstr "Немецкий" + +msgid "Lower Sorbian" +msgstr "Нижнелужицкий" + +msgid "Greek" +msgstr "Греческий" + +msgid "English" +msgstr "Английский" + +msgid "Australian English" +msgstr "Австралийский английский" + +msgid "British English" +msgstr "Британский английский" + +msgid "Esperanto" +msgstr "Эсперанто" + +msgid "Spanish" +msgstr "Испанский" + +msgid "Argentinian Spanish" +msgstr "Аргентинский испанский" + +msgid "Colombian Spanish" +msgstr "Колумбийский испанский" + +msgid "Mexican Spanish" +msgstr "Мексиканский испанский" + +msgid "Nicaraguan Spanish" +msgstr "Никарагуанский испанский" + +msgid "Venezuelan Spanish" +msgstr "Венесуэльский Испанский" + +msgid "Estonian" +msgstr "Эстонский" + +msgid "Basque" +msgstr "Баскский" + +msgid "Persian" +msgstr "Персидский" + +msgid "Finnish" +msgstr "Финский" + +msgid "French" +msgstr "Французский" + +msgid "Frisian" +msgstr "Фризский" + +msgid "Irish" +msgstr "Ирландский" + +msgid "Scottish Gaelic" +msgstr "Шотландский гэльский" + +msgid "Galician" +msgstr "Галисийский" + +msgid "Hebrew" +msgstr "Иврит" + +msgid "Hindi" +msgstr "Хинди" + +msgid "Croatian" +msgstr "Хорватский" + +msgid "Upper Sorbian" +msgstr "Верхнелужицкий" + +msgid "Hungarian" +msgstr "Венгерский" + +msgid "Armenian" +msgstr "Армянский" + +msgid "Interlingua" +msgstr "Интерлингва" + +msgid "Indonesian" +msgstr "Индонезийский" + +msgid "Igbo" +msgstr "Игбо" + +msgid "Ido" +msgstr "Идо" + +msgid "Icelandic" +msgstr "Исландский" + +msgid "Italian" +msgstr "Итальянский" + +msgid "Japanese" +msgstr "Японский" + +msgid "Georgian" +msgstr "Грузинский" + +msgid "Kabyle" +msgstr "Кабильский" + +msgid "Kazakh" +msgstr "Казахский" + +msgid "Khmer" +msgstr "Кхмерский" + +msgid "Kannada" +msgstr "Каннада" + +msgid "Korean" +msgstr "Корейский" + +msgid "Kyrgyz" +msgstr "Киргизский" + +msgid "Luxembourgish" +msgstr "Люксембургский" + +msgid "Lithuanian" +msgstr "Литовский" + +msgid "Latvian" +msgstr "Латвийский" + +msgid "Macedonian" +msgstr "Македонский" + +msgid "Malayalam" +msgstr "Малаялам" + +msgid "Mongolian" +msgstr "Монгольский" + +msgid "Marathi" +msgstr "Маратхи" + +msgid "Malay" +msgstr "Малайский" + +msgid "Burmese" +msgstr "Бирманский" + +msgid "Norwegian Bokmål" +msgstr "Норвежский (Букмол)" + +msgid "Nepali" +msgstr "Непальский" + +msgid "Dutch" +msgstr "Голландский" + +msgid "Norwegian Nynorsk" +msgstr "Норвежский (Нюнорск)" + +msgid "Ossetic" +msgstr "Осетинский" + +msgid "Punjabi" +msgstr "Панджаби" + +msgid "Polish" +msgstr "Польский" + +msgid "Portuguese" +msgstr "Португальский" + +msgid "Brazilian Portuguese" +msgstr "Бразильский португальский" + +msgid "Romanian" +msgstr "Румынский" + +msgid "Russian" +msgstr "Русский" + +msgid "Slovak" +msgstr "Словацкий" + +msgid "Slovenian" +msgstr "Словенский" + +msgid "Albanian" +msgstr "Албанский" + +msgid "Serbian" +msgstr "Сербский" + +msgid "Serbian Latin" +msgstr "Сербский (латиница)" + +msgid "Swedish" +msgstr "Шведский" + +msgid "Swahili" +msgstr "Суахили" + +msgid "Tamil" +msgstr "Тамильский" + +msgid "Telugu" +msgstr "Телугу" + +msgid "Tajik" +msgstr "Таджикский" + +msgid "Thai" +msgstr "Тайский" + +msgid "Turkmen" +msgstr "Туркменский" + +msgid "Turkish" +msgstr "Турецкий" + +msgid "Tatar" +msgstr "Татарский" + +msgid "Udmurt" +msgstr "Удмуртский" + +msgid "Ukrainian" +msgstr "Украинский" + +msgid "Urdu" +msgstr "Урду" + +msgid "Uzbek" +msgstr "Узбекский" + +msgid "Vietnamese" +msgstr "Вьетнамский" + +msgid "Simplified Chinese" +msgstr "Упрощенный китайский" + +msgid "Traditional Chinese" +msgstr "Традиционный китайский" + +msgid "Messages" +msgstr "Сообщения" + +msgid "Site Maps" +msgstr "Карта сайта" + +msgid "Static Files" +msgstr "Статические файлы" + +msgid "Syndication" +msgstr "Ленты новостей" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "…" + +msgid "That page number is not an integer" +msgstr "Номер страницы не является натуральным числом" + +msgid "That page number is less than 1" +msgstr "Номер страницы меньше 1" + +msgid "That page contains no results" +msgstr "Страница не содержит результатов" + +msgid "Enter a valid value." +msgstr "Введите правильное значение." + +msgid "Enter a valid URL." +msgstr "Введите правильный URL." + +msgid "Enter a valid integer." +msgstr "Введите правильное число." + +msgid "Enter a valid email address." +msgstr "Введите правильный адрес электронной почты." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" +"Значение должно состоять только из латинских букв, цифр, знаков " +"подчеркивания или дефиса." + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"Значение должно состоять только из символов входящих в стандарт Юникод, " +"цифр, символов подчёркивания или дефисов." + +msgid "Enter a valid IPv4 address." +msgstr "Введите правильный IPv4 адрес." + +msgid "Enter a valid IPv6 address." +msgstr "Введите действительный IPv6 адрес." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "Введите действительный IPv4 или IPv6 адрес." + +msgid "Enter only digits separated by commas." +msgstr "Введите цифры, разделенные запятыми." + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" +"Убедитесь, что это значение — %(limit_value)s (сейчас оно — %(show_value)s)." + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "Убедитесь, что это значение меньше либо равно %(limit_value)s." + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "Убедитесь, что это значение больше либо равно %(limit_value)s." + +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "Убедитесь, что это значение кратно числу %(limit_value)s." + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Убедитесь, что это значение содержит не менее %(limit_value)d символ (сейчас " +"%(show_value)d)." +msgstr[1] "" +"Убедитесь, что это значение содержит не менее %(limit_value)d символов " +"(сейчас %(show_value)d)." +msgstr[2] "" +"Убедитесь, что это значение содержит не менее %(limit_value)d символов " +"(сейчас %(show_value)d)." +msgstr[3] "" +"Убедитесь, что это значение содержит не менее %(limit_value)d символов " +"(сейчас %(show_value)d)." + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Убедитесь, что это значение содержит не более %(limit_value)d символ (сейчас " +"%(show_value)d)." +msgstr[1] "" +"Убедитесь, что это значение содержит не более %(limit_value)d символов " +"(сейчас %(show_value)d)." +msgstr[2] "" +"Убедитесь, что это значение содержит не более %(limit_value)d символов " +"(сейчас %(show_value)d)." +msgstr[3] "" +"Убедитесь, что это значение содержит не более %(limit_value)d символов " +"(сейчас %(show_value)d)." + +msgid "Enter a number." +msgstr "Введите число." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "Убедитесь, что вы ввели не более %(max)s цифры." +msgstr[1] "Убедитесь, что вы ввели не более %(max)s цифр." +msgstr[2] "Убедитесь, что вы ввели не более %(max)s цифр." +msgstr[3] "Убедитесь, что вы ввели не более %(max)s цифр." + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "Убедитесь, что вы ввели не более %(max)s цифры после запятой." +msgstr[1] "Убедитесь, что вы ввели не более %(max)s цифр после запятой." +msgstr[2] "Убедитесь, что вы ввели не более %(max)s цифр после запятой." +msgstr[3] "Убедитесь, что вы ввели не более %(max)s цифр после запятой." + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "Убедитесь, что вы ввели не более %(max)s цифры перед запятой." +msgstr[1] "Убедитесь, что вы ввели не более %(max)s цифр перед запятой." +msgstr[2] "Убедитесь, что вы ввели не более %(max)s цифр перед запятой." +msgstr[3] "Убедитесь, что вы ввели не более %(max)s цифр перед запятой." + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"Расширение файлов “%(extension)s” не поддерживается. Разрешенные расширения: " +"%(allowed_extensions)s." + +msgid "Null characters are not allowed." +msgstr "Данные содержат запрещённый символ: ноль-байт" + +msgid "and" +msgstr "и" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "" +"%(model_name)s с такими значениями полей %(field_labels)s уже существует." + +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "Нарушено ограничение \"%(name)s\"." + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "Значения %(value)r нет среди допустимых вариантов." + +msgid "This field cannot be null." +msgstr "Это поле не может иметь значение NULL." + +msgid "This field cannot be blank." +msgstr "Это поле не может быть пустым." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "%(model_name)s с таким %(field_label)s уже существует." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"Значение в поле «%(field_label)s» должно быть уникальным для фрагмента " +"«%(lookup_type)s» даты в поле %(date_field_label)s." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "Поле типа %(field_type)s" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "Значение “%(value)s” должно быть True или False." + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "Значение “%(value)s” должно быть True, False или None." + +msgid "Boolean (Either True or False)" +msgstr "Логическое (True или False)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "Строка (до %(max_length)s)" + +msgid "Comma-separated integers" +msgstr "Целые, разделенные запятыми" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" +"Значение “%(value)s” имеет неверный формат даты. Оно должно быть в формате " +"YYYY-MM-DD." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" +"Значение “%(value)s” имеет корректный формат (YYYY-MM-DD), но это " +"недействительная дата." + +msgid "Date (without time)" +msgstr "Дата (без указания времени)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"Значение “%(value)s” имеет неверный формат. Оно должно быть в формате YYYY-" +"MM-DD HH:MM[:ss[.uuuuuu]][TZ]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"Значение “%(value)s” имеет корректный формат (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]), но это недействительные дата/время." + +msgid "Date (with time)" +msgstr "Дата (с указанием времени)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "Значение “%(value)s” должно быть десятичным числом." + +msgid "Decimal number" +msgstr "Число с фиксированной запятой" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" +"Значение “%(value)s” имеет неверный формат. Оно должно быть в формате [DD] " +"[HH:[MM:]]ss[.uuuuuu]." + +msgid "Duration" +msgstr "Продолжительность" + +msgid "Email address" +msgstr "Адрес электронной почты" + +msgid "File path" +msgstr "Путь к файлу" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "Значение “%(value)s” должно быть числом с плавающей точкой." + +msgid "Floating point number" +msgstr "Число с плавающей запятой" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "Значение “%(value)s” должно быть целым числом." + +msgid "Integer" +msgstr "Целое" + +msgid "Big (8 byte) integer" +msgstr "Длинное целое (8 байт)" + +msgid "Small integer" +msgstr "Малое целое число" + +msgid "IPv4 address" +msgstr "IPv4 адрес" + +msgid "IP address" +msgstr "IP-адрес" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "Значение “%(value)s” должно быть None, True или False." + +msgid "Boolean (Either True, False or None)" +msgstr "Логическое (True, False или None)" + +msgid "Positive big integer" +msgstr "Положительное большое целое число" + +msgid "Positive integer" +msgstr "Положительное целое число" + +msgid "Positive small integer" +msgstr "Положительное малое целое число" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "Слаг (до %(max_length)s)" + +msgid "Text" +msgstr "Текст" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" +"Значение “%(value)s” имеет неверный формат. Оно должно быть в формате HH:MM[:" +"ss[.uuuuuu]]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" +"Значение “%(value)s” имеет корректный формат (HH:MM[:ss[.uuuuuu]]), но это " +"недействительное время." + +msgid "Time" +msgstr "Время" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "Необработанные двоичные данные" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "Значение “%(value)s” не является верным UUID-ом." + +msgid "Universally unique identifier" +msgstr "Поле для UUID, универсального уникального идентификатора" + +msgid "File" +msgstr "Файл" + +msgid "Image" +msgstr "Изображение" + +msgid "A JSON object" +msgstr "JSON-объект" + +msgid "Value must be valid JSON." +msgstr "Значение должно быть корректным JSON-ом." + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "" +"Объект модели %(model)s со значением поля %(field)s, равным %(value)r, не " +"существует." + +msgid "Foreign Key (type determined by related field)" +msgstr "Внешний Ключ (тип определен по связанному полю)" + +msgid "One-to-one relationship" +msgstr "Связь \"один к одному\"" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "Связь %(from)s-%(to)s" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "Связи %(from)s-%(to)s" + +msgid "Many-to-many relationship" +msgstr "Связь \"многие ко многим\"" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "Обязательное поле." + +msgid "Enter a whole number." +msgstr "Введите целое число." + +msgid "Enter a valid date." +msgstr "Введите правильную дату." + +msgid "Enter a valid time." +msgstr "Введите правильное время." + +msgid "Enter a valid date/time." +msgstr "Введите правильную дату и время." + +msgid "Enter a valid duration." +msgstr "Введите правильную продолжительность." + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "Количество дней должно быть в диапазоне от {min_days} до {max_days}." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "Ни одного файла не было отправлено. Проверьте тип кодировки формы." + +msgid "No file was submitted." +msgstr "Ни одного файла не было отправлено." + +msgid "The submitted file is empty." +msgstr "Отправленный файл пуст." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"Убедитесь, что это имя файла содержит не более %(max)d символ (сейчас " +"%(length)d)." +msgstr[1] "" +"Убедитесь, что это имя файла содержит не более %(max)d символов (сейчас " +"%(length)d)." +msgstr[2] "" +"Убедитесь, что это имя файла содержит не более %(max)d символов (сейчас " +"%(length)d)." +msgstr[3] "" +"Убедитесь, что это имя файла содержит не более %(max)d символов (сейчас " +"%(length)d)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" +"Пожалуйста, загрузите файл или поставьте флажок \"Очистить\", но не " +"совершайте оба действия одновременно." + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Загрузите правильное изображение. Файл, который вы загрузили, поврежден или " +"не является изображением." + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "Выберите корректный вариант. %(value)s нет среди допустимых значений." + +msgid "Enter a list of values." +msgstr "Введите список значений." + +msgid "Enter a complete value." +msgstr "Введите весь список значений." + +msgid "Enter a valid UUID." +msgstr "Введите правильный UUID." + +msgid "Enter a valid JSON." +msgstr "Введите корректный JSON." + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(Скрытое поле %(name)s) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" +"Данные ManagementForm отсутствуют или были подделаны. Отсутствующие поля: " +"%(field_names)s. Если проблема не исчезнет, вам может потребоваться " +"отправить отчет об ошибке." + +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "Пожалуйста, отправьте не больше %(num)d формы." +msgstr[1] "Пожалуйста, отправьте не больше %(num)d форм." +msgstr[2] "Пожалуйста, отправьте не больше %(num)d форм." +msgstr[3] "Пожалуйста, отправьте не больше %(num)d форм." + +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "Пожалуйста, отправьте %(num)d форму." +msgstr[1] "Пожалуйста, отправьте %(num)d формы." +msgstr[2] "Пожалуйста, отправьте %(num)d форм." +msgstr[3] "Пожалуйста, отправьте %(num)d форм." + +msgid "Order" +msgstr "Порядок" + +msgid "Delete" +msgstr "Удалить" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "Пожалуйста, измените повторяющееся значение в поле \"%(field)s\"." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" +"Пожалуйста, измените значение в поле %(field)s, оно должно быть уникальным." + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"Пожалуйста, измените значение в поле %(field_name)s, оно должно быть " +"уникальным для %(lookup)s в поле %(date_field)s." + +msgid "Please correct the duplicate values below." +msgstr "Пожалуйста, измените повторяющиеся значения ниже." + +msgid "The inline value did not match the parent instance." +msgstr "Значение во вложенной форме не совпадает со значением в базовой форме." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" +"Выберите корректный вариант. Вашего варианта нет среди допустимых значений." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "“%(pk)s” является неверным значением." + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"%(datetime)s не может быть интерпретирована в часовом поясе " +"%(current_timezone)s; дата может быть неоднозначной или оказаться " +"несуществующей." + +msgid "Clear" +msgstr "Очистить" + +msgid "Currently" +msgstr "На данный момент" + +msgid "Change" +msgstr "Изменить" + +msgid "Unknown" +msgstr "Неизвестно" + +msgid "Yes" +msgstr "Да" + +msgid "No" +msgstr "Нет" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "да,нет,может быть" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d байт" +msgstr[1] "%(size)d байта" +msgstr[2] "%(size)d байт" +msgstr[3] "%(size)d байт" + +#, python-format +msgid "%s KB" +msgstr "%s КБ" + +#, python-format +msgid "%s MB" +msgstr "%s МБ" + +#, python-format +msgid "%s GB" +msgstr "%s ГБ" + +#, python-format +msgid "%s TB" +msgstr "%s ТБ" + +#, python-format +msgid "%s PB" +msgstr "%s ПБ" + +msgid "p.m." +msgstr "п.п." + +msgid "a.m." +msgstr "д.п." + +msgid "PM" +msgstr "ПП" + +msgid "AM" +msgstr "ДП" + +msgid "midnight" +msgstr "полночь" + +msgid "noon" +msgstr "полдень" + +msgid "Monday" +msgstr "Понедельник" + +msgid "Tuesday" +msgstr "Вторник" + +msgid "Wednesday" +msgstr "Среда" + +msgid "Thursday" +msgstr "Четверг" + +msgid "Friday" +msgstr "Пятница" + +msgid "Saturday" +msgstr "Суббота" + +msgid "Sunday" +msgstr "Воскресенье" + +msgid "Mon" +msgstr "Пн" + +msgid "Tue" +msgstr "Вт" + +msgid "Wed" +msgstr "Ср" + +msgid "Thu" +msgstr "Чт" + +msgid "Fri" +msgstr "Пт" + +msgid "Sat" +msgstr "Сб" + +msgid "Sun" +msgstr "Вс" + +msgid "January" +msgstr "Январь" + +msgid "February" +msgstr "Февраль" + +msgid "March" +msgstr "Март" + +msgid "April" +msgstr "Апрель" + +msgid "May" +msgstr "Май" + +msgid "June" +msgstr "Июнь" + +msgid "July" +msgstr "Июль" + +msgid "August" +msgstr "Август" + +msgid "September" +msgstr "Сентябрь" + +msgid "October" +msgstr "Октябрь" + +msgid "November" +msgstr "Ноябрь" + +msgid "December" +msgstr "Декабрь" + +msgid "jan" +msgstr "янв" + +msgid "feb" +msgstr "фев" + +msgid "mar" +msgstr "мар" + +msgid "apr" +msgstr "апр" + +msgid "may" +msgstr "май" + +msgid "jun" +msgstr "июн" + +msgid "jul" +msgstr "июл" + +msgid "aug" +msgstr "авг" + +msgid "sep" +msgstr "сен" + +msgid "oct" +msgstr "окт" + +msgid "nov" +msgstr "ноя" + +msgid "dec" +msgstr "дек" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "Янв." + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "Фев." + +msgctxt "abbrev. month" +msgid "March" +msgstr "Март" + +msgctxt "abbrev. month" +msgid "April" +msgstr "Апрель" + +msgctxt "abbrev. month" +msgid "May" +msgstr "Май" + +msgctxt "abbrev. month" +msgid "June" +msgstr "Июнь" + +msgctxt "abbrev. month" +msgid "July" +msgstr "Июль" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "Авг." + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "Сен." + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "Окт." + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "Ноя." + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "Дек." + +msgctxt "alt. month" +msgid "January" +msgstr "января" + +msgctxt "alt. month" +msgid "February" +msgstr "февраля" + +msgctxt "alt. month" +msgid "March" +msgstr "марта" + +msgctxt "alt. month" +msgid "April" +msgstr "апреля" + +msgctxt "alt. month" +msgid "May" +msgstr "мая" + +msgctxt "alt. month" +msgid "June" +msgstr "июня" + +msgctxt "alt. month" +msgid "July" +msgstr "июля" + +msgctxt "alt. month" +msgid "August" +msgstr "августа" + +msgctxt "alt. month" +msgid "September" +msgstr "сентября" + +msgctxt "alt. month" +msgid "October" +msgstr "октября" + +msgctxt "alt. month" +msgid "November" +msgstr "ноября" + +msgctxt "alt. month" +msgid "December" +msgstr "декабря" + +msgid "This is not a valid IPv6 address." +msgstr "Значение не является корректным адресом IPv6." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "или" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "%(num)d год" +msgstr[1] "%(num)d года" +msgstr[2] "%(num)d лет" +msgstr[3] "%(num)d лет" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "%(num)d месяц" +msgstr[1] "%(num)d месяца" +msgstr[2] "%(num)d месяцев" +msgstr[3] "%(num)d месяцев" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "%(num)d неделя" +msgstr[1] "%(num)d недели" +msgstr[2] "%(num)d недель" +msgstr[3] "%(num)d недель" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "%(num)d день" +msgstr[1] "%(num)d дня" +msgstr[2] "%(num)d дней" +msgstr[3] "%(num)d дней" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "%(num)d час" +msgstr[1] "%(num)d часа" +msgstr[2] "%(num)d часов" +msgstr[3] "%(num)d часов" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "%(num)d минута" +msgstr[1] "%(num)d минуты" +msgstr[2] "%(num)d минут" +msgstr[3] "%(num)d минут" + +msgid "Forbidden" +msgstr "Ошибка доступа" + +msgid "CSRF verification failed. Request aborted." +msgstr "Ошибка проверки CSRF. Запрос отклонён." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" +"Вы видите это сообщение потому что этот сайт работает по защищённому " +"протоколу HTTPS и требует, чтобы при запросе вашим браузером был передан " +"заголовок \"Referer\", но он не был передан. Этот заголовок необходим из " +"соображений безопасности: мы должны убедиться что запрос оправляете именно " +"вы." + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"Если вы настроили свой браузер таким образом, чтобы запретить ему передавать " +"заголовок “Referer”, пожалуйста, разрешите ему отсылать данный заголовок по " +"крайней мере для данного сайта, или для всех HTTPS-соединений, или для " +"запросов, домен и порт назначения совпадают с доменом и портом текущей " +"страницы." + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"Если Вы используете HTML-тэг или добавили HTTP-заголовок “Referrer-Policy: no-referrer”, " +"пожалуйста удалите их. CSRF защите необходим заголовок “Referer” для строгой " +"проверки адреса ссылающейся страницы. Если Вы беспокоитесь о приватности, " +"используйте альтернативы, например , для ссылок на " +"сайты третьих лиц." + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"Вы видите это сообщение, потому что данный сайт требует, чтобы при отправке " +"форм была отправлена и CSRF-cookie. Данный тип cookie необходим по " +"соображениям безопасности, чтобы убедиться, что ваш браузер не был взломан и " +"не выполняет от вашего лица действий, запрограммированных третьими лицами." + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" +"Если в вашем браузере отключены cookie, пожалуйста, включите эту функцию " +"вновь, по крайней мере для этого сайта, или для \"same-orign\" запросов." + +msgid "More information is available with DEBUG=True." +msgstr "" +"В отладочном режиме доступно больше информации. Включить отладочный режим " +"можно, установив значение переменной DEBUG=True." + +msgid "No year specified" +msgstr "Не указан год" + +msgid "Date out of range" +msgstr "Дата выходит за пределы диапазона" + +msgid "No month specified" +msgstr "Не указан месяц" + +msgid "No day specified" +msgstr "Не указан день" + +msgid "No week specified" +msgstr "Не указана неделя" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "%(verbose_name_plural)s не доступен" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"Будущие %(verbose_name_plural)s недоступны, потому что %(class_name)s." +"allow_future выставлен в значение \"Ложь\"." + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" +"Не удалось распознать строку с датой “%(datestr)s”, в заданном формате " +"“%(format)s”" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "Не найден ни один %(verbose_name)s, соответствующий запросу" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" +"Номер страницы не содержит особое значение “last” и его не удалось " +"преобразовать к целому числу." + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Неправильная страница (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" +"Список пуст, но “%(class_name)s.allow_empty” выставлено в значение \"Ложь\", " +"что запрещает показывать пустые списки." + +msgid "Directory indexes are not allowed here." +msgstr "Просмотр списка файлов директории здесь не разрешен." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "“%(path)s” не существует" + +#, python-format +msgid "Index of %(directory)s" +msgstr "Список файлов директории %(directory)s" + +msgid "The install worked successfully! Congratulations!" +msgstr "Установка прошла успешно! Поздравляем!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"Посмотреть примечания к выпуску для Django " +"%(version)s" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not " +"configured any URLs." +msgstr "" +"Вы видите данную страницу, потому что указали DEBUG=True в файле настроек и не настроили ни одного " +"обработчика URL-адресов." + +msgid "Django Documentation" +msgstr "Документация Django" + +msgid "Topics, references, & how-to’s" +msgstr "Разделы, справочник, & примеры" + +msgid "Tutorial: A Polling App" +msgstr "Руководство: Приложение для голосования" + +msgid "Get started with Django" +msgstr "Начало работы с Django" + +msgid "Django Community" +msgstr "Сообщество Django" + +msgid "Connect, get help, or contribute" +msgstr "Присоединяйтесь, получайте помощь или помогайте в разработке" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/sk/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/sk/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..e6f2c0c7 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/sk/LC_MESSAGES/django 3.po @@ -0,0 +1,1354 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Jannis Leidel , 2011 +# 18f25ad6fa9930fc67cb11aca9d16a27, 2012-2013 +# Marian Andre , 2013,2015,2017-2018 +# Martin Kosír, 2011 +# Martin Tóth , 2017 +# Peter Kuma, 2021 +# Peter Stríž , 2020 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-09-21 10:22+0200\n" +"PO-Revision-Date: 2021-11-18 21:19+0000\n" +"Last-Translator: Transifex Bot <>\n" +"Language-Team: Slovak (http://www.transifex.com/django/django/language/sk/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: sk\n" +"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n " +">= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n" + +msgid "Afrikaans" +msgstr "afrikánsky" + +msgid "Arabic" +msgstr "arabský" + +msgid "Algerian Arabic" +msgstr "alžírsky arabsky" + +msgid "Asturian" +msgstr "astúrsky" + +msgid "Azerbaijani" +msgstr "azerbajdžansky" + +msgid "Bulgarian" +msgstr "bulharsky" + +msgid "Belarusian" +msgstr "bielorusky" + +msgid "Bengali" +msgstr "bengálsky" + +msgid "Breton" +msgstr "bretónsky" + +msgid "Bosnian" +msgstr "bosniansky" + +msgid "Catalan" +msgstr "katalánsky" + +msgid "Czech" +msgstr "česky" + +msgid "Welsh" +msgstr "walesky" + +msgid "Danish" +msgstr "dánsky" + +msgid "German" +msgstr "nemecky" + +msgid "Lower Sorbian" +msgstr "dolnolužická srbčina" + +msgid "Greek" +msgstr "grécky" + +msgid "English" +msgstr "anglicky" + +msgid "Australian English" +msgstr "austrálskou angličtinou" + +msgid "British English" +msgstr "britskou angličtinou" + +msgid "Esperanto" +msgstr "esperantsky" + +msgid "Spanish" +msgstr "španielsky" + +msgid "Argentinian Spanish" +msgstr "argentínska španielčina" + +msgid "Colombian Spanish" +msgstr "kolumbijská španielčina" + +msgid "Mexican Spanish" +msgstr "mexická španielčina" + +msgid "Nicaraguan Spanish" +msgstr "nikaragujská španielčina" + +msgid "Venezuelan Spanish" +msgstr "venezuelská španielčina" + +msgid "Estonian" +msgstr "estónsky" + +msgid "Basque" +msgstr "baskicky" + +msgid "Persian" +msgstr "perzsky" + +msgid "Finnish" +msgstr "fínsky" + +msgid "French" +msgstr "francúzsky" + +msgid "Frisian" +msgstr "frízsky" + +msgid "Irish" +msgstr "írsky" + +msgid "Scottish Gaelic" +msgstr "škótska gaelčina" + +msgid "Galician" +msgstr "galícijsky" + +msgid "Hebrew" +msgstr "hebrejsky" + +msgid "Hindi" +msgstr "hindsky" + +msgid "Croatian" +msgstr "chorvátsky" + +msgid "Upper Sorbian" +msgstr "hornolužická srbčina" + +msgid "Hungarian" +msgstr "maďarsky" + +msgid "Armenian" +msgstr "arménsky" + +msgid "Interlingua" +msgstr "interlinguánsky" + +msgid "Indonesian" +msgstr "indonézsky" + +msgid "Igbo" +msgstr "igbožsky" + +msgid "Ido" +msgstr "ido" + +msgid "Icelandic" +msgstr "islandsky" + +msgid "Italian" +msgstr "taliansky" + +msgid "Japanese" +msgstr "japonsky" + +msgid "Georgian" +msgstr "gruzínsky" + +msgid "Kabyle" +msgstr "kabylsky" + +msgid "Kazakh" +msgstr "kazašsky" + +msgid "Khmer" +msgstr "kmérsky" + +msgid "Kannada" +msgstr "kannadsky" + +msgid "Korean" +msgstr "kórejsky" + +msgid "Kyrgyz" +msgstr "kirgizsky" + +msgid "Luxembourgish" +msgstr "luxembursky" + +msgid "Lithuanian" +msgstr "litovsky" + +msgid "Latvian" +msgstr "lotyšsky" + +msgid "Macedonian" +msgstr "macedónsky" + +msgid "Malayalam" +msgstr "malajalámsky" + +msgid "Mongolian" +msgstr "mongolsky" + +msgid "Marathi" +msgstr "maráthsky" + +msgid "Malay" +msgstr "" + +msgid "Burmese" +msgstr "barmsky" + +msgid "Norwegian Bokmål" +msgstr "nórsky (Bokmål)" + +msgid "Nepali" +msgstr "nepálsky" + +msgid "Dutch" +msgstr "holandsky" + +msgid "Norwegian Nynorsk" +msgstr "nórsky (Nynorsk)" + +msgid "Ossetic" +msgstr "osetsky" + +msgid "Punjabi" +msgstr "pandžábsky" + +msgid "Polish" +msgstr "poľsky" + +msgid "Portuguese" +msgstr "portugalsky" + +msgid "Brazilian Portuguese" +msgstr "portugalsky (Brazília)" + +msgid "Romanian" +msgstr "rumunsky" + +msgid "Russian" +msgstr "rusky" + +msgid "Slovak" +msgstr "slovensky" + +msgid "Slovenian" +msgstr "slovinsky" + +msgid "Albanian" +msgstr "albánsky" + +msgid "Serbian" +msgstr "srbsky" + +msgid "Serbian Latin" +msgstr "srbsky (Latin)" + +msgid "Swedish" +msgstr "švédsky" + +msgid "Swahili" +msgstr "svahilsky" + +msgid "Tamil" +msgstr "tamilsky" + +msgid "Telugu" +msgstr "telugsky" + +msgid "Tajik" +msgstr "tadžiksky" + +msgid "Thai" +msgstr "thajsky" + +msgid "Turkmen" +msgstr "turkménsky" + +msgid "Turkish" +msgstr "turecky" + +msgid "Tatar" +msgstr "tatársky" + +msgid "Udmurt" +msgstr "udmurtsky" + +msgid "Ukrainian" +msgstr "ukrajinsky" + +msgid "Urdu" +msgstr "urdsky" + +msgid "Uzbek" +msgstr "uzbecky" + +msgid "Vietnamese" +msgstr "vietnamsky" + +msgid "Simplified Chinese" +msgstr "čínsky (zjednodušene)" + +msgid "Traditional Chinese" +msgstr "čínsky (tradične)" + +msgid "Messages" +msgstr "Správy" + +msgid "Site Maps" +msgstr "Mapy Sídla" + +msgid "Static Files" +msgstr "Statické Súbory" + +msgid "Syndication" +msgstr "Syndikácia" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "…" + +msgid "That page number is not an integer" +msgstr "Číslo stránky nie je celé číslo" + +msgid "That page number is less than 1" +msgstr "Číslo stránky je menšie ako 1" + +msgid "That page contains no results" +msgstr "Stránka neobsahuje žiadne výsledky" + +msgid "Enter a valid value." +msgstr "Zadajte platnú hodnotu." + +msgid "Enter a valid URL." +msgstr "Zadajte platnú URL adresu." + +msgid "Enter a valid integer." +msgstr "Zadajte platné celé číslo." + +msgid "Enter a valid email address." +msgstr "Zadajte platnú e-mailovú adresu." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" +"Zadajte platnú skratku pozostávajúcu z písmen, čísel, podčiarkovníkov alebo " +"pomlčiek." + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"Zadajte platnú skratku pozostávajúcu z písmen Unicode, čísel, " +"podčiarkovníkov alebo pomlčiek." + +msgid "Enter a valid IPv4 address." +msgstr "Zadajte platnú IPv4 adresu." + +msgid "Enter a valid IPv6 address." +msgstr "Zadajte platnú IPv6 adresu." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "Zadajte platnú IPv4 alebo IPv6 adresu." + +msgid "Enter only digits separated by commas." +msgstr "Zadajte len číslice oddelené čiarkami." + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "Uistite sa, že táto hodnota je %(limit_value)s (je to %(show_value)s)." + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "Uistite sa, že táto hodnota je menšia alebo rovná %(limit_value)s." + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "Uistite sa, že hodnota je väčšia alebo rovná %(limit_value)s." + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Uistite sa, že zadaná hodnota má najmenej %(limit_value)d znak (má " +"%(show_value)d)." +msgstr[1] "" +"Uistite sa, že zadaná hodnota má najmenej %(limit_value)d znaky (má " +"%(show_value)d)." +msgstr[2] "" +"Uistite sa, že zadaná hodnota má najmenej %(limit_value)d znakov (má " +"%(show_value)d)." +msgstr[3] "" +"Uistite sa, že zadaná hodnota má najmenej %(limit_value)d znakov (má " +"%(show_value)d)." + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Uistite sa, že táto hodnota má najviac %(limit_value)d znak (má " +"%(show_value)d)." +msgstr[1] "" +"Uistite sa, že táto hodnota má najviac %(limit_value)d znaky (má " +"%(show_value)d)." +msgstr[2] "" +"Uistite sa, že táto hodnota má najviac %(limit_value)d znakov (má " +"%(show_value)d)." +msgstr[3] "" +"Uistite sa, že táto hodnota má najviac %(limit_value)d znakov (má " +"%(show_value)d)." + +msgid "Enter a number." +msgstr "Zadajte číslo." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "Uistite sa, že nie je zadaných celkovo viac ako %(max)s číslica." +msgstr[1] "Uistite sa, že nie je zadaných celkovo viac ako %(max)s číslice." +msgstr[2] "Uistite sa, že nie je zadaných celkovo viac ako %(max)s číslic." +msgstr[3] "Uistite sa, že nie je zadaných celkovo viac ako %(max)s číslic." + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "Uistite sa, že nie je zadané viac ako %(max)s desatinné miesto." +msgstr[1] "Uistite sa, že nie sú zadané viac ako %(max)s desatinné miesta." +msgstr[2] "Uistite sa, že nie je zadaných viac ako %(max)s desatinných miest." +msgstr[3] "Uistite sa, že nie je zadaných viac ako %(max)s desatinných miest." + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +"Uistite sa, že nie je zadaných viac ako %(max)s číslica pred desatinnou " +"čiarkou." +msgstr[1] "" +"Uistite sa, že nie sú zadané viac ako %(max)s číslice pred desatinnou " +"čiarkou." +msgstr[2] "" +"Uistite sa, že nie je zadaných viac ako %(max)s číslic pred desatinnou " +"čiarkou." +msgstr[3] "" +"Uistite sa, že nie je zadaných viac ako %(max)s číslic pred desatinnou " +"čiarkou." + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"Prípona súboru '%(extension)s' nie je povolená. Povolené prípony sú: " +"'%(allowed_extensions)s'." + +msgid "Null characters are not allowed." +msgstr "Znaky NULL nie sú povolené." + +msgid "and" +msgstr "a" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "%(model_name)s s týmto %(field_labels)s už existuje." + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "Hodnota %(value)r nie je platná možnosť." + +msgid "This field cannot be null." +msgstr "Toto pole nemôže byť prázdne." + +msgid "This field cannot be blank." +msgstr "Toto pole nemôže byť prázdne." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "%(model_name)s s týmto %(field_label)s už existuje." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. +#. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"%(field_label)s musí byť unikátne pre %(date_field_label)s %(lookup_type)s." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "Pole typu: %(field_type)s" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "'%(value)s' value musí byť True alebo False." + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "'%(value)s' musí byť True, False alebo None." + +msgid "Boolean (Either True or False)" +msgstr "Logická hodnota (buď True alebo False)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "Reťazec (až do %(max_length)s)" + +msgid "Comma-separated integers" +msgstr "Celé čísla oddelené čiarkou" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "'%(value)s' má neplatný tvar dátumu. Musí byť v tvare YYYY-MM-DD." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" +"'%(value)s' je v správnom tvare (YYYY-MM-DD), ale je to neplatný dátum." + +msgid "Date (without time)" +msgstr "Dátum (bez času)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"Hodnota “%(value)s” má neplatný tvar. Musí byť v tvare RRRR-MM-DD HH:MM[:" +"ss[.uuuuuu]][ČZ]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"Hodnota “%(value)s” má byť v tvare (RRRR-MM-DD HH:MM[:ss[.uuuuuu]][ČZ]), ale " +"toto je neplatný dátum/čas." + +msgid "Date (with time)" +msgstr "Dátum (a čas)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "'%(value)s' musí byť desatinné číslo." + +msgid "Decimal number" +msgstr "Desatinné číslo" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" +"Hodnota “%(value)s” má neplatný tvar. Musí byť v tvare [DD] [[HH:]MM:]ss[." +"uuuuuu]." + +msgid "Duration" +msgstr "Doba trvania" + +msgid "Email address" +msgstr "E-mailová adresa" + +msgid "File path" +msgstr "Cesta k súboru" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "Hodnota “%(value)s” musí byť desatinné číslo." + +msgid "Floating point number" +msgstr "Číslo s plávajúcou desatinnou čiarkou" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "'%(value)s' musí byť celé číslo." + +msgid "Integer" +msgstr "Celé číslo" + +msgid "Big (8 byte) integer" +msgstr "Veľké celé číslo (8 bajtov)" + +msgid "Small integer" +msgstr "Malé celé číslo" + +msgid "IPv4 address" +msgstr "IPv4 adresa" + +msgid "IP address" +msgstr "IP adresa" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "Hodnota “%(value)s” musí byť buď None, True alebo False." + +msgid "Boolean (Either True, False or None)" +msgstr "Logická hodnota (buď True, False alebo None)" + +msgid "Positive big integer" +msgstr "Kladné veľké celé číslo" + +msgid "Positive integer" +msgstr "Kladné celé číslo" + +msgid "Positive small integer" +msgstr "Malé kladné celé číslo" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "Identifikátor (najviac %(max_length)s)" + +msgid "Text" +msgstr "Text" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" +"Hodnota “%(value)s” má neplatný tvar. Musí byť v tvare HH:MM[:ss[.uuuuuu]]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" +"Hodnota “%(value)s” má mať správny tvar (HH:MM[:ss[.uuuuuu]]), ale toto je " +"neplatný čas." + +msgid "Time" +msgstr "Čas" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "Binárne údaje" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "“%(value)s” nie je platné UUID." + +msgid "Universally unique identifier" +msgstr "Úplne všade jedinečný identifikátor" + +msgid "File" +msgstr "Súbor" + +msgid "Image" +msgstr "Obrázok" + +msgid "A JSON object" +msgstr "Objekt typu JSON" + +msgid "Value must be valid JSON." +msgstr "Hodnota musí byť v platnom formáte JSON." + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "Inštancia modelu %(model)s s %(field)s %(value)r neexistuje." + +msgid "Foreign Key (type determined by related field)" +msgstr "Cudzí kľúč (typ určuje pole v relácii)" + +msgid "One-to-one relationship" +msgstr "Typ relácie: jedna k jednej" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "vzťah: %(from)s-%(to)s " + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "vzťahy: %(from)s-%(to)s" + +msgid "Many-to-many relationship" +msgstr "Typ relácie: M ku N" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "Toto pole je povinné." + +msgid "Enter a whole number." +msgstr "Zadajte celé číslo." + +msgid "Enter a valid date." +msgstr "Zadajte platný dátum." + +msgid "Enter a valid time." +msgstr "Zadajte platný čas." + +msgid "Enter a valid date/time." +msgstr "Zadajte platný dátum/čas." + +msgid "Enter a valid duration." +msgstr "Zadajte platnú dobu trvania." + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "Počet dní musí byť medzi {min_days} a {max_days}." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "Súbor nebol odoslaný. Skontrolujte typ kódovania vo formulári." + +msgid "No file was submitted." +msgstr "Žiaden súbor nebol odoslaný." + +msgid "The submitted file is empty." +msgstr "Odoslaný súbor je prázdny." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"Uistite sa, že názov súboru má najviac %(max)d znak (má %(length)d)." +msgstr[1] "" +"Uistite sa, že názov súboru má najviac %(max)d znaky (má %(length)d)." +msgstr[2] "" +"Uistite sa, že názov súboru má najviac %(max)d znakov (má %(length)d)." +msgstr[3] "" +"Uistite sa, že názov súboru má najviac %(max)d znakov (má %(length)d)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" +"Odošlite prosím súbor alebo zaškrtnite políčko pre vymazanie vstupného poľa, " +"nie oboje." + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Nahrajte platný obrázok. Súbor, ktorý ste odoslali nebol obrázok alebo bol " +"poškodený." + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "Vyberte platnú voľbu. %(value)s nepatrí medzi dostupné možnosti." + +msgid "Enter a list of values." +msgstr "Zadajte zoznam hodnôt." + +msgid "Enter a complete value." +msgstr "Zadajte úplnú hodnotu." + +msgid "Enter a valid UUID." +msgstr "Zadajte platné UUID." + +msgid "Enter a valid JSON." +msgstr "Zadajte platný JSON." + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(Skryté pole %(name)s) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" +"Dáta ManagementForm chýbajú alebo boli zmanipulované. Chýbajúce polia: " +"%(field_names)s. Možno budete musieť túto chybu nahlásiť, ak sa bude naďalej " +"vyskytovať." + +#, python-format +msgid "Please submit at most %d form." +msgid_plural "Please submit at most %d forms." +msgstr[0] "Prosím odošlite najviac %d formulár." +msgstr[1] "Prosím odošlite najviac %d formulárov." +msgstr[2] "Prosím odošlite najviac %d formulárov." +msgstr[3] "Prosím odošlite najviac %d formulárov." + +#, python-format +msgid "Please submit at least %d form." +msgid_plural "Please submit at least %d forms." +msgstr[0] "Prosím odošlite aspoň %d formulár." +msgstr[1] "Prosím odošlite aspoň %d formulárov." +msgstr[2] "Prosím odošlite aspoň %d formulárov." +msgstr[3] "Prosím odošlite aspoň %d formulárov." + +msgid "Order" +msgstr "Poradie" + +msgid "Delete" +msgstr "Odstrániť" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "Prosím, opravte duplicitné údaje pre %(field)s." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "Údaje pre %(field)s musia byť unikátne, prosím, opravte duplikáty." + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"Údaje pre %(field_name)s musia byť unikátne pre %(lookup)s v %(date_field)s, " +"prosím, opravte duplikáty." + +msgid "Please correct the duplicate values below." +msgstr "Prosím, opravte nižšie uvedené duplicitné hodnoty. " + +msgid "The inline value did not match the parent instance." +msgstr "Vnorená hodnota sa nezhoduje s nadradenou inštanciou." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" +"Vyberte platnú možnosť. Vybraná položka nepatrí medzi dostupné možnosti." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "\"%(pk)s\" nie je platná hodnota." + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"Hodnota %(datetime)s v časovej zóne %(current_timezone)s sa nedá " +"interpretovať; môže byť nejednoznačná alebo nemusí existovať." + +msgid "Clear" +msgstr "Vymazať" + +msgid "Currently" +msgstr "Súčasne" + +msgid "Change" +msgstr "Zmeniť" + +msgid "Unknown" +msgstr "Neznámy" + +msgid "Yes" +msgstr "Áno" + +msgid "No" +msgstr "Nie" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "áno,nie,možno" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d bajt" +msgstr[1] "%(size)d bajty" +msgstr[2] "%(size)d bajtov" +msgstr[3] "%(size)d bajtov" + +#, python-format +msgid "%s KB" +msgstr "%s KB" + +#, python-format +msgid "%s MB" +msgstr "%s MB" + +#, python-format +msgid "%s GB" +msgstr "%s GB" + +#, python-format +msgid "%s TB" +msgstr "%s TB" + +#, python-format +msgid "%s PB" +msgstr "%s PB" + +msgid "p.m." +msgstr "popoludní" + +msgid "a.m." +msgstr "predpoludním" + +msgid "PM" +msgstr "popoludní" + +msgid "AM" +msgstr "predpoludním" + +msgid "midnight" +msgstr "polnoc" + +msgid "noon" +msgstr "poludnie" + +msgid "Monday" +msgstr "pondelok" + +msgid "Tuesday" +msgstr "utorok" + +msgid "Wednesday" +msgstr "streda" + +msgid "Thursday" +msgstr "štvrtok" + +msgid "Friday" +msgstr "piatok" + +msgid "Saturday" +msgstr "sobota" + +msgid "Sunday" +msgstr "nedeľa" + +msgid "Mon" +msgstr "po" + +msgid "Tue" +msgstr "ut" + +msgid "Wed" +msgstr "st" + +msgid "Thu" +msgstr "št" + +msgid "Fri" +msgstr "pi" + +msgid "Sat" +msgstr "so" + +msgid "Sun" +msgstr "ne" + +msgid "January" +msgstr "január" + +msgid "February" +msgstr "február" + +msgid "March" +msgstr "marec" + +msgid "April" +msgstr "apríl" + +msgid "May" +msgstr "máj" + +msgid "June" +msgstr "jún" + +msgid "July" +msgstr "júl" + +msgid "August" +msgstr "august" + +msgid "September" +msgstr "september" + +msgid "October" +msgstr "október" + +msgid "November" +msgstr "november" + +msgid "December" +msgstr "december" + +msgid "jan" +msgstr "jan" + +msgid "feb" +msgstr "feb" + +msgid "mar" +msgstr "mar" + +msgid "apr" +msgstr "apr" + +msgid "may" +msgstr "máj" + +msgid "jun" +msgstr "jún" + +msgid "jul" +msgstr "júl" + +msgid "aug" +msgstr "aug" + +msgid "sep" +msgstr "sep" + +msgid "oct" +msgstr "okt" + +msgid "nov" +msgstr "nov" + +msgid "dec" +msgstr "dec" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "jan." + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "feb." + +msgctxt "abbrev. month" +msgid "March" +msgstr "mar." + +msgctxt "abbrev. month" +msgid "April" +msgstr "apr." + +msgctxt "abbrev. month" +msgid "May" +msgstr "máj" + +msgctxt "abbrev. month" +msgid "June" +msgstr "jún" + +msgctxt "abbrev. month" +msgid "July" +msgstr "júl" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "aug." + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "sep." + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "okt." + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "nov." + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "dec." + +msgctxt "alt. month" +msgid "January" +msgstr "január" + +msgctxt "alt. month" +msgid "February" +msgstr "február" + +msgctxt "alt. month" +msgid "March" +msgstr "marec" + +msgctxt "alt. month" +msgid "April" +msgstr "apríl" + +msgctxt "alt. month" +msgid "May" +msgstr "máj" + +msgctxt "alt. month" +msgid "June" +msgstr "jún" + +msgctxt "alt. month" +msgid "July" +msgstr "júl" + +msgctxt "alt. month" +msgid "August" +msgstr "august" + +msgctxt "alt. month" +msgid "September" +msgstr "september" + +msgctxt "alt. month" +msgid "October" +msgstr "október" + +msgctxt "alt. month" +msgid "November" +msgstr "november" + +msgctxt "alt. month" +msgid "December" +msgstr "december" + +msgid "This is not a valid IPv6 address." +msgstr "Toto nieje platná IPv6 adresa." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s..." + +msgid "or" +msgstr "alebo" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +msgid "Forbidden" +msgstr "Zakázané (Forbidden)" + +msgid "CSRF verification failed. Request aborted." +msgstr "CSRF verifikázia zlyhala. Požiadavka bola prerušená." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"Ak ste vo vašom prehliadači vypli hlavičky “Referer”, tak ich prosím " +"zapnite, alebo aspoň pre túto stránku, alebo pre HTTPS pripojenia, alebo pre " +"požiadavky “same-origin”." + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"Ak používate tag , alebo " +"vkladáte hlavičku 'Referrer-Policy: no-referrer', prosím odstránte ich. " +"Ochrana CSRF vyžaduje hlavičku “Referer” na striktnú kontrolu. Ak máte obavy " +"o súkromie, použite alternatívy ako pre linky na " +"iné stránky." + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"Túto správu vidíte, pretože táto lokalita vyžaduje CSRF cookie pri " +"odosielaní formulárov. Toto cookie je potrebné na zabezpečenie toho, že váš " +"prehliadač nie je zneužitý - \"hijack\"." + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" +"Ak ste vypli cookies vo vašom prehliadači, tak ich prosím zapnite, aspoň pre " +"túto stránku, alebo pre požiadavky “same-origin”." + +msgid "More information is available with DEBUG=True." +msgstr "Viac informácií bude dostupných s DEBUG=True." + +msgid "No year specified" +msgstr "Nešpecifikovaný rok" + +msgid "Date out of range" +msgstr "Dátum je mimo rozsahu" + +msgid "No month specified" +msgstr "Nešpecifikovaný mesiac" + +msgid "No day specified" +msgstr "Nešpecifikovaný deň" + +msgid "No week specified" +msgstr "Nešpecifikovaný týždeň" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "%(verbose_name_plural)s nie sú dostupné" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"Budúce %(verbose_name_plural)s nie sú dostupné pretože %(class_name)s." +"allow_future má hodnotu False. " + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "Neplatný dátumový reťazec “%(datestr)s” pre formát “%(format)s”" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "" +"Nebol nájdený žiadny %(verbose_name)s zodpovedajúci databázovému dopytu" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" +"Stránka nemá hodnotu “last” a taktiež nie je možné prekonvertovať hodnotu na " +"celé číslo." + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Nesprávna stránka (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "Zoznam je prázdny a hodnota “%(class_name)s.allow_empty” je False." + +msgid "Directory indexes are not allowed here." +msgstr "Výpis adresárov tu nieje povolený." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "\"%(path)s\" neexistuje" + +#, python-format +msgid "Index of %(directory)s" +msgstr "Výpis %(directory)s" + +msgid "The install worked successfully! Congratulations!" +msgstr "Inštalácia prebehla úspešne! Gratulujeme!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"Zobraziť poznámky k vydaniu pre Django " +"%(version)s" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" +"Táto stránka sa zobrazuje pretože máte DEBUG=True v súbore s nastaveniami a nie sú nakonfigurované žiadne " +"URL." + +msgid "Django Documentation" +msgstr "Dokumentácia Django" + +msgid "Topics, references, & how-to’s" +msgstr "Témy, referencie a návody" + +msgid "Tutorial: A Polling App" +msgstr "Tutoriál: Aplikácia \"Hlasovania\"" + +msgid "Get started with Django" +msgstr "Začíname s Django" + +msgid "Django Community" +msgstr "Komunita Django" + +msgid "Connect, get help, or contribute" +msgstr "Spojte sa, získajte pomoc, alebo prispejte" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/sq/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/sq/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..5970382f --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/sq/LC_MESSAGES/django 3.po @@ -0,0 +1,1334 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Besnik Bleta , 2011-2014 +# Besnik Bleta , 2020-2023 +# Besnik Bleta , 2015-2019 +# Jannis Leidel , 2011 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 06:49+0000\n" +"Last-Translator: Besnik Bleta , 2020-2023\n" +"Language-Team: Albanian (http://www.transifex.com/django/django/language/" +"sq/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: sq\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Afrikaans" +msgstr "Afrikaans" + +msgid "Arabic" +msgstr "Arabe" + +msgid "Algerian Arabic" +msgstr "Arabishte Algjeriane" + +msgid "Asturian" +msgstr "Asturiase" + +msgid "Azerbaijani" +msgstr "Azerbaixhanase" + +msgid "Bulgarian" +msgstr "Bulgare" + +msgid "Belarusian" +msgstr "Bjelloruse" + +msgid "Bengali" +msgstr "Bengaleze" + +msgid "Breton" +msgstr "Bretone" + +msgid "Bosnian" +msgstr "Boshnjake" + +msgid "Catalan" +msgstr "Katalane" + +msgid "Central Kurdish (Sorani)" +msgstr "Kurdishte e Qendrës (Sorani)" + +msgid "Czech" +msgstr "Çeke" + +msgid "Welsh" +msgstr "Uellsiane" + +msgid "Danish" +msgstr "Daneze" + +msgid "German" +msgstr "Gjermane" + +msgid "Lower Sorbian" +msgstr "Sorbiane e Poshtme" + +msgid "Greek" +msgstr "Greke" + +msgid "English" +msgstr "Angleze" + +msgid "Australian English" +msgstr "Angleze Australiane" + +msgid "British English" +msgstr "Angleze Britanike" + +msgid "Esperanto" +msgstr "Esperanto" + +msgid "Spanish" +msgstr "Spanjolle" + +msgid "Argentinian Spanish" +msgstr "Spanjolle Argjentinase" + +msgid "Colombian Spanish" +msgstr "Spanjolle Kolumbiane" + +msgid "Mexican Spanish" +msgstr "Spanjolle Meksikane" + +msgid "Nicaraguan Spanish" +msgstr "Spanjolle Nikaraguane" + +msgid "Venezuelan Spanish" +msgstr "Spanjolle Venezuelane" + +msgid "Estonian" +msgstr "Estoneze" + +msgid "Basque" +msgstr "Baske" + +msgid "Persian" +msgstr "Persiane" + +msgid "Finnish" +msgstr "Finlandeze" + +msgid "French" +msgstr "Frënge" + +msgid "Frisian" +msgstr "Frisiane" + +msgid "Irish" +msgstr "Irlandeze" + +msgid "Scottish Gaelic" +msgstr "Skoceze Gaelike" + +msgid "Galician" +msgstr "Galike" + +msgid "Hebrew" +msgstr "Hebraishte" + +msgid "Hindi" +msgstr "Indiane" + +msgid "Croatian" +msgstr "Kroate" + +msgid "Upper Sorbian" +msgstr "Sorbiane e Sipërme" + +msgid "Hungarian" +msgstr "Hungareze" + +msgid "Armenian" +msgstr "Armenisht" + +msgid "Interlingua" +msgstr "Interlingua" + +msgid "Indonesian" +msgstr "Indoneziane" + +msgid "Igbo" +msgstr "Igbo" + +msgid "Ido" +msgstr "Ido" + +msgid "Icelandic" +msgstr "Islandeze" + +msgid "Italian" +msgstr "Italiane" + +msgid "Japanese" +msgstr "Japoneze" + +msgid "Georgian" +msgstr "Gjeorgjiane" + +msgid "Kabyle" +msgstr "Kabilase" + +msgid "Kazakh" +msgstr "Kazake" + +msgid "Khmer" +msgstr "Khmere" + +msgid "Kannada" +msgstr "Kannada" + +msgid "Korean" +msgstr "Koreane" + +msgid "Kyrgyz" +msgstr "Kirgize" + +msgid "Luxembourgish" +msgstr "Luksemburgase" + +msgid "Lithuanian" +msgstr "Lituaneze" + +msgid "Latvian" +msgstr "Letoneze" + +msgid "Macedonian" +msgstr "Maqedone" + +msgid "Malayalam" +msgstr "Malajalame" + +msgid "Mongolian" +msgstr "Mongoliane" + +msgid "Marathi" +msgstr "Marati" + +msgid "Malay" +msgstr "" + +msgid "Burmese" +msgstr "Burmeze" + +msgid "Norwegian Bokmål" +msgstr "Norvegjeze Bokmal" + +msgid "Nepali" +msgstr "Nepaleze" + +msgid "Dutch" +msgstr "Holandeze" + +msgid "Norwegian Nynorsk" +msgstr "Norvegjeze Nynorsk" + +msgid "Ossetic" +msgstr "Osetishte" + +msgid "Punjabi" +msgstr "Panxhabe" + +msgid "Polish" +msgstr "Polake" + +msgid "Portuguese" +msgstr "Portugeze" + +msgid "Brazilian Portuguese" +msgstr "Portugeze Braziliane" + +msgid "Romanian" +msgstr "Rumune" + +msgid "Russian" +msgstr "Ruse" + +msgid "Slovak" +msgstr "Sllovake " + +msgid "Slovenian" +msgstr "Slovene" + +msgid "Albanian" +msgstr "Shqipe" + +msgid "Serbian" +msgstr "Serbe" + +msgid "Serbian Latin" +msgstr "Serbe Latine" + +msgid "Swedish" +msgstr "Suedeze" + +msgid "Swahili" +msgstr "Swahili" + +msgid "Tamil" +msgstr "Tamileze" + +msgid "Telugu" +msgstr "Telugu" + +msgid "Tajik" +msgstr "Taxhike" + +msgid "Thai" +msgstr "Tajlandeze" + +msgid "Turkmen" +msgstr "Turkmene" + +msgid "Turkish" +msgstr "Turke" + +msgid "Tatar" +msgstr "Tatare" + +msgid "Udmurt" +msgstr "Udmurt" + +msgid "Ukrainian" +msgstr "Ukrainase" + +msgid "Urdu" +msgstr "Urdu" + +msgid "Uzbek" +msgstr "Uzbeke" + +msgid "Vietnamese" +msgstr "Vietnameze" + +msgid "Simplified Chinese" +msgstr "Kineze e Thjeshtuar" + +msgid "Traditional Chinese" +msgstr "Kineze Tradicionale" + +msgid "Messages" +msgstr "Mesazhe" + +msgid "Site Maps" +msgstr "Harta Sajti" + +msgid "Static Files" +msgstr "Kartela Statike" + +msgid "Syndication" +msgstr "" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "…" + +msgid "That page number is not an integer" +msgstr "Ai numër faqeje s’është numër i plotë" + +msgid "That page number is less than 1" +msgstr "Ai numër faqeje është më i vogël se 1" + +msgid "That page contains no results" +msgstr "Ajo faqe s’përmban përfundime" + +msgid "Enter a valid value." +msgstr "Jepni një vlerë të vlefshme." + +msgid "Enter a valid URL." +msgstr "Jepni një URL të vlefshme." + +msgid "Enter a valid integer." +msgstr "Jepni një numër të plotë të vlefshëm." + +msgid "Enter a valid email address." +msgstr "Jepni një adresë email të vlefshme." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" +"Jepni një “slug” të vlefshëm, të përbërë nga shkronja, numra, nëvija ose " +"vija në mes." + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"Jepni një “slug” të vlefshëm, të përbërë nga shkronja, numra, nënvija ose " +"vija ndarëse Unikod." + +msgid "Enter a valid IPv4 address." +msgstr "Jepni një adresë IPv4 të vlefshme." + +msgid "Enter a valid IPv6 address." +msgstr "Jepni një adresë IPv6 të vlefshme." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "Jepni një adresë IPv4 ose IPv6 të vlefshme." + +msgid "Enter only digits separated by commas." +msgstr "Jepni vetëm shifra të ndara nga presje." + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "Siguroni që kjo vlerë të jetë %(limit_value)s (është %(show_value)s)." + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "Siguroni që kjo vlerë të jetë më e vogël ose baras me %(limit_value)s." + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "Siguroni që kjo vlerë është më e madhe ose baras me %(limit_value)s." + +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "" +"Garantoni që vlera të jetë një shumëfish i madhësisë së hapit " +"%(limit_value)s." + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Sigurohuni që kjo vlerë ka të paktën %(limit_value)d shenjë (ka " +"%(show_value)d)." +msgstr[1] "" +"Sigurohuni që kjo vlerë ka të paktën %(limit_value)d shenja (ka " +"%(show_value)d)." + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Sigurohuni që kjo vlerë ka të shumtën %(limit_value)d shenjë (ka " +"%(show_value)d)." +msgstr[1] "" +"Sigurohuni që kjo vlerë ka të shumtën %(limit_value)d shenja (ka " +"%(show_value)d)." + +msgid "Enter a number." +msgstr "Jepni një numër." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "Sigurohuni që s’ka më tepër se %(max)s shifër gjithsej." +msgstr[1] "Sigurohuni që s’ka më tepër se %(max)s shifra gjithsej." + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "Sigurohuni që s’ka më shumë se %(max)s vend dhjetor." +msgstr[1] "Sigurohuni që s’ka më shumë se %(max)s vende dhjetore." + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +"Sigurohuni që s’ka më tepër se %(max)s shifër para presjes dhjetore." +msgstr[1] "" +"Sigurohuni që s’ka më tepër se %(max)s shifra para presjes dhjetore." + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"Zgjatimi “%(extension)s” për kartela nuk lejohet. Zgjatime të lejuara janë: " +"%(allowed_extensions)s." + +msgid "Null characters are not allowed." +msgstr "Nuk lejohen shenja null." + +msgid "and" +msgstr "dhe " + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "Ka tashmë %(model_name)s me këtë %(field_labels)s." + +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "Është cenuar kufizimi “%(name)s”." + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "Vlera %(value)r s’është zgjedhje e vlefshme." + +msgid "This field cannot be null." +msgstr "Kjo fushë s’mund të përmbajë shenja null." + +msgid "This field cannot be blank." +msgstr "Kjo fushë s’mund të jetë e paplotësuar." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "Ka tashmë një %(model_name)s me këtë %(field_label)s." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"%(field_label)s duhet të jetë unike për %(date_field_label)s %(lookup_type)s." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "Fushë e llojit: %(field_type)s" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "Vlera “%(value)s” duhet të jetë ose True, ose False." + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "Vlera për “%(value)s” duhet të jetë ose True, ose False, ose None." + +msgid "Boolean (Either True or False)" +msgstr "Buleane (Ose True, ose False)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "Varg (deri në %(max_length)s)" + +msgid "String (unlimited)" +msgstr "Varg (i pakufizuar)" + +msgid "Comma-separated integers" +msgstr "Numra të plotë të ndarë me presje" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" +"Vlera “%(value)s” ka një format të pavlefshëm datash. Duhet të jetë në " +"formatin YYYY-MM-DD." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" +"Vlera “%(value)s” ka formatin e saktë (YYYY-MM-DD), por është datë e " +"pavlefshme." + +msgid "Date (without time)" +msgstr "Datë (pa kohë)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"Vlera “'%(value)s” ka një format të pavlefshëm. Duhet të jetë në formatin " +"YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"Vlera “%(value)s” ka format të saktë (YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]), " +"por është datë/kohë e pavlefshme." + +msgid "Date (with time)" +msgstr "Datë (me kohë)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "Vlera “%(value)s” duhet të jetë një numër dhjetor." + +msgid "Decimal number" +msgstr "Numër dhjetor" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" +"Vlera “%(value)s” ka format të pavlefshëm. Duhet të jetë në formatin [DD] " +"[HH:[MM:]]ss[.uuuuuu]." + +msgid "Duration" +msgstr "Kohëzgjatje" + +msgid "Email address" +msgstr "Adresë email" + +msgid "File path" +msgstr "Shteg kartele" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "" + +msgid "Floating point number" +msgstr "" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "Vlera “%(value)s” duhet të jetë numër i plotë." + +msgid "Integer" +msgstr "Numër i plotë" + +msgid "Big (8 byte) integer" +msgstr "Numër i plotë i madh (8 bajte)" + +msgid "Small integer" +msgstr "Numër i plotë i vogël" + +msgid "IPv4 address" +msgstr "Adresë IPv4" + +msgid "IP address" +msgstr "Adresë IP" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "Vlera “%(value)s” duhet të jetë ose None, ose True, ose False." + +msgid "Boolean (Either True, False or None)" +msgstr "Buleane (Ose True, ose False, ose None)" + +msgid "Positive big integer" +msgstr "Numër i plotë pozitiv i madh" + +msgid "Positive integer" +msgstr "Numër i plotë pozitiv" + +msgid "Positive small integer" +msgstr "Numër i plotë pozitiv i vogël" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "Identifikues (deri në %(max_length)s)" + +msgid "Text" +msgstr "Tekst" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" +"Vlera “%(value)s” ka format të pavlefshëm. Duhet të jetë në formatin HH:MM[:" +"ss[.uuuuuu]]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" +"Vlera “%(value)s” ka formatin e saktë (HH:MM[:ss[.uuuuuu]]) por është kohë e " +"pavlefshme." + +msgid "Time" +msgstr "Kohë" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "Të dhëna dyore të papërpunuara" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "“%(value)s” s’është UUID i vlefshëm." + +msgid "Universally unique identifier" +msgstr "Identifikues universalisht unik" + +msgid "File" +msgstr "Kartelë" + +msgid "Image" +msgstr "Figurë" + +msgid "A JSON object" +msgstr "Objekt JSON" + +msgid "Value must be valid JSON." +msgstr "Vlera duhet të jetë JSON i vlefshëm." + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "Instanca %(model)s me %(field)s %(value)r s’ekziston." + +msgid "Foreign Key (type determined by related field)" +msgstr "Kyç i Jashtëm (lloj i përcaktuar nga fusha përkatëse)" + +msgid "One-to-one relationship" +msgstr "Marrëdhënie një-për-një" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "Marrëdhënie %(from)s-%(to)s" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "Marrëdhënie %(from)s-%(to)s" + +msgid "Many-to-many relationship" +msgstr "Marrëdhënie shumë-për-shumë" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "Kjo fushë është e domosdoshme." + +msgid "Enter a whole number." +msgstr "Jepni një numër të tërë." + +msgid "Enter a valid date." +msgstr "Jepni një datë të vlefshme." + +msgid "Enter a valid time." +msgstr "Jepni një kohë të vlefshme." + +msgid "Enter a valid date/time." +msgstr "Jepni një datë/kohë të vlefshme." + +msgid "Enter a valid duration." +msgstr "Jepni një kohëzgjatje të vlefshme." + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "Numri i ditëve duhet të jetë mes {min_days} dhe {max_days}." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" +"S’u parashtrua ndonjë kartelë. Kontrolloni llojin e kodimit te formulari." + +msgid "No file was submitted." +msgstr "S’u parashtrua kartelë." + +msgid "The submitted file is empty." +msgstr "Kartela e parashtruar është e zbrazët." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"Sigurohuni që ky emër kartele ka të shumtën %(max)d shenjë (it has " +"%(length)d)." +msgstr[1] "" +"Sigurohuni që ky emër kartele ka të shumtën %(max)d shenja (it has " +"%(length)d)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" +"Ju lutemi, ose parashtroni një kartelë, ose i vini shenjë kutizës për " +"spastrim, jo që të dyja." + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Ngarkoni një figurë të vlefshme. Kartela që ngarkuat ose nuk qe figurë, ose " +"qe figurë e dëmtuar." + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" +"Përzgjidhni një zgjedhje të vlefshme. %(value)s s’është një nga zgjedhjet e " +"mundshme." + +msgid "Enter a list of values." +msgstr "Jepni një listë vlerash." + +msgid "Enter a complete value." +msgstr "Jepni një vlerë të plotë." + +msgid "Enter a valid UUID." +msgstr "Jepni një UUID të vlefshëm." + +msgid "Enter a valid JSON." +msgstr "Jepni një JSON të vlefshëm." + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(Fushë e fshehur %(name)s) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" +"Mungojnë të dhëna ManagementForm, ose në to janë futur hundët. Fusha që " +"mungojnë: %(field_names)s. Nëse problemi vazhdon, mund të duhet të " +"parashtroni një raport të mete." + +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "Ju lutemi, parashtroni e shumta %(num)d formular." +msgstr[1] "Ju lutemi, parashtroni e shumta %(num)d formularë." + +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "Ju lutemi, parashtroni të paktën %(num)d formular." +msgstr[1] "Ju lutemi, parashtroni të paktën %(num)d formularë." + +msgid "Order" +msgstr "Renditi" + +msgid "Delete" +msgstr "Fshije" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "Ju lutemi, ndreqni të dhënat e përsëdytura për %(field)s." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" +"Ju lutemi, ndreqni të dhënat e përsëdytura për %(field)s, të cilat duhet të " +"jenë unike." + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"Ju lutemi, ndreqni të dhënat e përsëdytura për %(field_name)s të cilat duhet " +"të jenë unike për %(lookup)s te %(date_field)s." + +msgid "Please correct the duplicate values below." +msgstr "Ju lutemi, ndreqni më poshtë vlerat e përsëdytura." + +msgid "The inline value did not match the parent instance." +msgstr "Vlera e brendshme s’u përputh me instancën prind." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" +"Përzgjidhni një zgjedhje të vlefshme. Ajo zgjedhje nuk është një nga " +"zgjedhjet e mundshme." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "“%(pk)s” s’është vlerë e vlefshme." + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"%(datetime)s s’u interpretua dot brenda zonës kohore %(current_timezone)s; " +"mund të jetë e dykuptimtë, ose mund të mos ekzistojë." + +msgid "Clear" +msgstr "Spastroje" + +msgid "Currently" +msgstr "Tani" + +msgid "Change" +msgstr "Ndryshoje" + +msgid "Unknown" +msgstr "E panjohur" + +msgid "Yes" +msgstr "Po" + +msgid "No" +msgstr "Jo" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "po,jo,ndoshta" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d bajt" +msgstr[1] "%(size)d bajte" + +#, python-format +msgid "%s KB" +msgstr "%s KB" + +#, python-format +msgid "%s MB" +msgstr "%s MB" + +#, python-format +msgid "%s GB" +msgstr "%s GB" + +#, python-format +msgid "%s TB" +msgstr "%s TB" + +#, python-format +msgid "%s PB" +msgstr "%s PB" + +msgid "p.m." +msgstr "p.m." + +msgid "a.m." +msgstr "a.m." + +msgid "PM" +msgstr "PM" + +msgid "AM" +msgstr "AM" + +msgid "midnight" +msgstr "mesnatë" + +msgid "noon" +msgstr "mesditë" + +msgid "Monday" +msgstr "E hënë" + +msgid "Tuesday" +msgstr "E martë" + +msgid "Wednesday" +msgstr "E mërkurë" + +msgid "Thursday" +msgstr "E enjte" + +msgid "Friday" +msgstr "E premte" + +msgid "Saturday" +msgstr "E shtunë" + +msgid "Sunday" +msgstr "E dielë" + +msgid "Mon" +msgstr "Hën" + +msgid "Tue" +msgstr "Mar" + +msgid "Wed" +msgstr "Mër" + +msgid "Thu" +msgstr "Enj" + +msgid "Fri" +msgstr "Pre" + +msgid "Sat" +msgstr "Sht" + +msgid "Sun" +msgstr "Die" + +msgid "January" +msgstr "Janar" + +msgid "February" +msgstr "Shkurt" + +msgid "March" +msgstr "Mars" + +msgid "April" +msgstr "Prill" + +msgid "May" +msgstr "Maj" + +msgid "June" +msgstr "Qershor" + +msgid "July" +msgstr "Korrik" + +msgid "August" +msgstr "Gusht" + +msgid "September" +msgstr "Shtator" + +msgid "October" +msgstr "Tetor" + +msgid "November" +msgstr "Nëntor" + +msgid "December" +msgstr "Dhjetor" + +msgid "jan" +msgstr "jan" + +msgid "feb" +msgstr "shk" + +msgid "mar" +msgstr "mar" + +msgid "apr" +msgstr "pri" + +msgid "may" +msgstr "maj" + +msgid "jun" +msgstr "qer" + +msgid "jul" +msgstr "kor" + +msgid "aug" +msgstr "gus" + +msgid "sep" +msgstr "sht" + +msgid "oct" +msgstr "tet" + +msgid "nov" +msgstr "nën" + +msgid "dec" +msgstr "dhj" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "Jan." + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "Shk." + +msgctxt "abbrev. month" +msgid "March" +msgstr "Mars" + +msgctxt "abbrev. month" +msgid "April" +msgstr "Prill" + +msgctxt "abbrev. month" +msgid "May" +msgstr "Maj" + +msgctxt "abbrev. month" +msgid "June" +msgstr "Qershor" + +msgctxt "abbrev. month" +msgid "July" +msgstr "Korrik" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "Gus." + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "Shta." + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "Tet." + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "Nën." + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "Dhj." + +msgctxt "alt. month" +msgid "January" +msgstr "Janar" + +msgctxt "alt. month" +msgid "February" +msgstr "Shkurt" + +msgctxt "alt. month" +msgid "March" +msgstr "Mars" + +msgctxt "alt. month" +msgid "April" +msgstr "Prill" + +msgctxt "alt. month" +msgid "May" +msgstr "Maj" + +msgctxt "alt. month" +msgid "June" +msgstr "Qershor" + +msgctxt "alt. month" +msgid "July" +msgstr "Korrik" + +msgctxt "alt. month" +msgid "August" +msgstr "Gusht" + +msgctxt "alt. month" +msgid "September" +msgstr "Shtator" + +msgctxt "alt. month" +msgid "October" +msgstr "Tetor" + +msgctxt "alt. month" +msgid "November" +msgstr "Nëntor" + +msgctxt "alt. month" +msgid "December" +msgstr "Dhjetor" + +msgid "This is not a valid IPv6 address." +msgstr "Kjo s’është adresë IPv6 e vlefshme." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "ose" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "%(num)d vit" +msgstr[1] "%(num)d vjet" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "%(num)d muaj" +msgstr[1] "%(num)d muaj" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "%(num)d javë" +msgstr[1] "%(num)d javë" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "%(num)d ditë" +msgstr[1] "%(num)d ditë" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "%(num)d orë" +msgstr[1] "%(num)d orë" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "%(num)d minutë" +msgstr[1] "%(num)d minuta" + +msgid "Forbidden" +msgstr "E ndaluar" + +msgid "CSRF verification failed. Request aborted." +msgstr "Verifikimi CSRF dështoi. Kërkesa u ndërpre." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" +"Këtë mesazh po e shihni ngaqë ky sajt HTTPS e ka të domosdoshme dërgimin e " +"“Referer header” te shfletuesi juaj, por s’u dërgua ndonjë i tillë. Kjo krye " +"është e domosdoshme për arsye sigurie, për të bërë të mundur që shfletuesi " +"juaj të mos komprometohet nga palë të treta." + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"Nëse e keni formësuar shfletuesin tuaj të çaktivizojë kryet “Referer”, ju " +"lutemi, riaktivizojini, ose për lidhje HTTPS, ose për kërkesa “same-origin”." + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"Nëse përdorni etiketën " +"etiketën ose përfshini kryet “Referrer-Policy: no-referrer”, ju lutemi, " +"hiqini. Mbrojtja CSRF lyp që kryet “Referer” të kryejnë kontroll strikt " +"referuesi. Nëse shqetësoheni për privatësinë, për lidhje te sajte palësh të " +"treta përdorni alternativa si ." + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"Këtë mesazh po e shihni ngaqë ky sajt lyp një cookie CSRF, kur parashtrohen " +"formularë. Kjo cookie është e domosdoshme për arsye sigurie, për të bërë të " +"mundur që shfletuesi juaj të mos komprometohet nga palë të treta." + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" +"Nëse e keni formësuar shfletuesin tuaj të çaktivizojë cookie-t, ju lutemi, " +"riaktivizojini, të paktën për këtë sajt, ose për kërkesa “same-origin”." + +msgid "More information is available with DEBUG=True." +msgstr "Më tepër të dhëna mund të gjeni me DEBUG=True." + +msgid "No year specified" +msgstr "Nuk është caktuar vit" + +msgid "Date out of range" +msgstr "Datë jashtë intervali" + +msgid "No month specified" +msgstr "Nuk është caktuar muaj" + +msgid "No day specified" +msgstr "Nuk është caktuar ditë" + +msgid "No week specified" +msgstr "Nuk është caktuar javë" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "Nuk ka %(verbose_name_plural)s të përcaktuar" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"%(verbose_name_plural)s i ardhshëm jo i passhëm, ngaqë %(class_name)s." +"allow_future është False." + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" +"U dha varg i pavlefshëm date “%(datestr)s” formati i dhënë “%(format)s”" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "S’u gjetën %(verbose_name)s me përputhje" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "Faqja nuk është “last”, as mund të shndërrohet në një int." + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Faqe e pavlefshme (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "Listë e zbrazët dhe “%(class_name)s.allow_empty” është False." + +msgid "Directory indexes are not allowed here." +msgstr "Këtu s’lejohen tregues drejtorish." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "“%(path)s” s’ekziston" + +#, python-format +msgid "Index of %(directory)s" +msgstr "Tregues i %(directory)s" + +msgid "The install worked successfully! Congratulations!" +msgstr "Instalimi funksionoi me sukses! Përgëzime!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"Shihni shënimet për hedhjen në qarkullim të " +"Django %(version)s" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not " +"configured any URLs." +msgstr "" +"Po e shihni këtë faqe ngaqë te kartela juaj e rregullimeve gjendet DEBUG=True dhe s’keni formësuar " +"ndonjë URL." + +msgid "Django Documentation" +msgstr "Dokumentim i Django-s" + +msgid "Topics, references, & how-to’s" +msgstr "Tema, referenca, & how-to" + +msgid "Tutorial: A Polling App" +msgstr "Përkujdesore: Një Aplikacion Për Sondazhe" + +msgid "Get started with Django" +msgstr "Si t’ia filloni me Django-n" + +msgid "Django Community" +msgstr "Bashkësia Django" + +msgid "Connect, get help, or contribute" +msgstr "Lidhuni, merrni ndihmë, ose jepni ndihmesë" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/ta/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/ta/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..b2f35fe6 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/ta/LC_MESSAGES/django 3.po @@ -0,0 +1,1230 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Claude Paroz , 2020 +# Jannis Leidel , 2011 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-05-19 20:23+0200\n" +"PO-Revision-Date: 2020-07-14 21:42+0000\n" +"Last-Translator: Transifex Bot <>\n" +"Language-Team: Tamil (http://www.transifex.com/django/django/language/ta/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ta\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Afrikaans" +msgstr "" + +msgid "Arabic" +msgstr "அரபிக்" + +msgid "Algerian Arabic" +msgstr "" + +msgid "Asturian" +msgstr "" + +msgid "Azerbaijani" +msgstr "" + +msgid "Bulgarian" +msgstr "" + +msgid "Belarusian" +msgstr "" + +msgid "Bengali" +msgstr "பெங்காலி" + +msgid "Breton" +msgstr "" + +msgid "Bosnian" +msgstr "" + +msgid "Catalan" +msgstr "" + +msgid "Czech" +msgstr "செக்" + +msgid "Welsh" +msgstr "வெல்ஸ்" + +msgid "Danish" +msgstr "டேனிஷ்" + +msgid "German" +msgstr "ஜெர்மன்" + +msgid "Lower Sorbian" +msgstr "" + +msgid "Greek" +msgstr "கிரேக்கம்" + +msgid "English" +msgstr "ஆங்கிலம்" + +msgid "Australian English" +msgstr "" + +msgid "British English" +msgstr "" + +msgid "Esperanto" +msgstr "" + +msgid "Spanish" +msgstr "ஸ்பானிஷ்" + +msgid "Argentinian Spanish" +msgstr "" + +msgid "Colombian Spanish" +msgstr "" + +msgid "Mexican Spanish" +msgstr "" + +msgid "Nicaraguan Spanish" +msgstr "" + +msgid "Venezuelan Spanish" +msgstr "" + +msgid "Estonian" +msgstr "" + +msgid "Basque" +msgstr "" + +msgid "Persian" +msgstr "" + +msgid "Finnish" +msgstr "பீனீஷ்" + +msgid "French" +msgstr "ப்ரென்சு" + +msgid "Frisian" +msgstr "" + +msgid "Irish" +msgstr "" + +msgid "Scottish Gaelic" +msgstr "" + +msgid "Galician" +msgstr "கலீஷீயன்" + +msgid "Hebrew" +msgstr "ஹீப்ரு" + +msgid "Hindi" +msgstr "" + +msgid "Croatian" +msgstr "" + +msgid "Upper Sorbian" +msgstr "" + +msgid "Hungarian" +msgstr "ஹங்கேரியன்" + +msgid "Armenian" +msgstr "" + +msgid "Interlingua" +msgstr "" + +msgid "Indonesian" +msgstr "" + +msgid "Igbo" +msgstr "" + +msgid "Ido" +msgstr "" + +msgid "Icelandic" +msgstr "ஐஸ்லான்டிக்" + +msgid "Italian" +msgstr "இத்தாலியன்" + +msgid "Japanese" +msgstr "ஜப்பானிய" + +msgid "Georgian" +msgstr "" + +msgid "Kabyle" +msgstr "" + +msgid "Kazakh" +msgstr "" + +msgid "Khmer" +msgstr "" + +msgid "Kannada" +msgstr "" + +msgid "Korean" +msgstr "" + +msgid "Kyrgyz" +msgstr "" + +msgid "Luxembourgish" +msgstr "" + +msgid "Lithuanian" +msgstr "" + +msgid "Latvian" +msgstr "" + +msgid "Macedonian" +msgstr "" + +msgid "Malayalam" +msgstr "" + +msgid "Mongolian" +msgstr "" + +msgid "Marathi" +msgstr "" + +msgid "Burmese" +msgstr "" + +msgid "Norwegian Bokmål" +msgstr "" + +msgid "Nepali" +msgstr "" + +msgid "Dutch" +msgstr "டச்சு" + +msgid "Norwegian Nynorsk" +msgstr "" + +msgid "Ossetic" +msgstr "" + +msgid "Punjabi" +msgstr "" + +msgid "Polish" +msgstr "" + +msgid "Portuguese" +msgstr "" + +msgid "Brazilian Portuguese" +msgstr "" + +msgid "Romanian" +msgstr "ரோமானியன்" + +msgid "Russian" +msgstr "ரஷ்யன்" + +msgid "Slovak" +msgstr "சுலோவாக்" + +msgid "Slovenian" +msgstr "ஸ்லோவேனியன்" + +msgid "Albanian" +msgstr "" + +msgid "Serbian" +msgstr "செர்பியன்" + +msgid "Serbian Latin" +msgstr "" + +msgid "Swedish" +msgstr "சுவிடிஷ்" + +msgid "Swahili" +msgstr "" + +msgid "Tamil" +msgstr "தமிழ்" + +msgid "Telugu" +msgstr "" + +msgid "Tajik" +msgstr "" + +msgid "Thai" +msgstr "" + +msgid "Turkmen" +msgstr "" + +msgid "Turkish" +msgstr "துருக்கிஷ்" + +msgid "Tatar" +msgstr "" + +msgid "Udmurt" +msgstr "" + +msgid "Ukrainian" +msgstr "உக்ரேனியன்" + +msgid "Urdu" +msgstr "" + +msgid "Uzbek" +msgstr "" + +msgid "Vietnamese" +msgstr "" + +msgid "Simplified Chinese" +msgstr "எளிய சீன மொழி" + +msgid "Traditional Chinese" +msgstr "மரபு சீன மொழி" + +msgid "Messages" +msgstr "" + +msgid "Site Maps" +msgstr "" + +msgid "Static Files" +msgstr "" + +msgid "Syndication" +msgstr "" + +msgid "That page number is not an integer" +msgstr "" + +msgid "That page number is less than 1" +msgstr "" + +msgid "That page contains no results" +msgstr "" + +msgid "Enter a valid value." +msgstr "" + +msgid "Enter a valid URL." +msgstr "" + +msgid "Enter a valid integer." +msgstr "" + +msgid "Enter a valid email address." +msgstr "" + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" + +msgid "Enter a valid IPv4 address." +msgstr "" + +msgid "Enter a valid IPv6 address." +msgstr "" + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "" + +msgid "Enter only digits separated by commas." +msgstr "இங்கு எண்களை மட்டுமே எழுதவும் காமவாள் தனிமைபடுத்தவும் " + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "" + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "" + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" + +msgid "Enter a number." +msgstr "" + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" + +msgid "Null characters are not allowed." +msgstr "" + +msgid "and" +msgstr "மற்றும்" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "" + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "" + +msgid "This field cannot be null." +msgstr "இந்த புலம் காலியாக இருக்கக் கூடாது" + +msgid "This field cannot be blank." +msgstr "" + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "" + +#. Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. +#. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "" + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "" + +msgid "Boolean (Either True or False)" +msgstr "பூலியன் (சரி அல்லது தவறு)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "" + +msgid "Comma-separated integers" +msgstr "கமாவாள் பிரிக்கப்பட்ட முழு எண்" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" + +msgid "Date (without time)" +msgstr "தேதி (நேரமில்லாமல்)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" + +msgid "Date (with time)" +msgstr "தேதி (நேரமுடன்)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "" + +msgid "Decimal number" +msgstr "தசம எண்கள்" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" + +msgid "Duration" +msgstr "" + +msgid "Email address" +msgstr "" + +msgid "File path" +msgstr "கோப்புப் பாதை" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "" + +msgid "Floating point number" +msgstr "" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "" + +msgid "Integer" +msgstr "முழு எண்" + +msgid "Big (8 byte) integer" +msgstr "" + +msgid "IPv4 address" +msgstr "" + +msgid "IP address" +msgstr "IP விலாசம்" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "" + +msgid "Boolean (Either True, False or None)" +msgstr "இலக்கு முறை (சரி, தவறு அல்லது ஒன்றும் இல்லை)" + +msgid "Positive big integer" +msgstr "" + +msgid "Positive integer" +msgstr "" + +msgid "Positive small integer" +msgstr "" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "" + +msgid "Small integer" +msgstr "" + +msgid "Text" +msgstr "உரை" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" + +msgid "Time" +msgstr "நேரம்" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "" + +msgid "Universally unique identifier" +msgstr "" + +msgid "File" +msgstr "" + +msgid "Image" +msgstr "" + +msgid "A JSON object" +msgstr "" + +msgid "Value must be valid JSON." +msgstr "" + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "" + +msgid "Foreign Key (type determined by related field)" +msgstr "" + +msgid "One-to-one relationship" +msgstr "" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "" + +msgid "Many-to-many relationship" +msgstr "" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr "" + +msgid "This field is required." +msgstr "இந்த புலத்தில் மதிப்பு தேவை" + +msgid "Enter a whole number." +msgstr "முழு எண் மட்டுமே எழுதவும்" + +msgid "Enter a valid date." +msgstr "" + +msgid "Enter a valid time." +msgstr "" + +msgid "Enter a valid date/time." +msgstr "" + +msgid "Enter a valid duration." +msgstr "" + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "" + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "அந்த பக்கத்தின் encoding வகையைப் பரிசோதிக்க.கோப்பு சமர்பிக்கப் பட்டவில்லை " + +msgid "No file was submitted." +msgstr "" + +msgid "The submitted file is empty." +msgstr "சமர்பிக்கப் பட்ட கோப்புக் காலியாக உள்ளது" + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +msgstr[1] "" + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"முறையான படம் மட்டுமே பதிவேற்றம் செய்யவும். நீங்கள் பதிவேற்றம் செய்த கோப்பு படம் அள்ளாத " +"அல்லது கெட்டுப்போன கோப்பாகும்" + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" + +msgid "Enter a list of values." +msgstr "" + +msgid "Enter a complete value." +msgstr "" + +msgid "Enter a valid UUID." +msgstr "" + +msgid "Enter a valid JSON." +msgstr "" + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr "" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "" + +msgid "ManagementForm data is missing or has been tampered with" +msgstr "" + +#, python-format +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "" +msgstr[1] "" + +msgid "Order" +msgstr "" + +msgid "Delete" +msgstr "நீக்குக" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "" + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" + +msgid "Please correct the duplicate values below." +msgstr "" + +msgid "The inline value did not match the parent instance." +msgstr "" + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "" + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" + +msgid "Clear" +msgstr "" + +msgid "Currently" +msgstr "" + +msgid "Change" +msgstr "மாற்றுக" + +msgid "Unknown" +msgstr "தெரியாத" + +msgid "Yes" +msgstr "ஆம்" + +msgid "No" +msgstr "இல்லை" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "ஆம்,இல்லை,இருக்கலாம்" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%s KB" +msgstr "" + +#, python-format +msgid "%s MB" +msgstr "" + +#, python-format +msgid "%s GB" +msgstr "" + +#, python-format +msgid "%s TB" +msgstr "" + +#, python-format +msgid "%s PB" +msgstr "" + +msgid "p.m." +msgstr "" + +msgid "a.m." +msgstr "" + +msgid "PM" +msgstr "" + +msgid "AM" +msgstr "" + +msgid "midnight" +msgstr "" + +msgid "noon" +msgstr "" + +msgid "Monday" +msgstr "திங்கள்" + +msgid "Tuesday" +msgstr "செவ்வாய்" + +msgid "Wednesday" +msgstr "புதன்" + +msgid "Thursday" +msgstr "வியாழன்" + +msgid "Friday" +msgstr "வெள்ளி" + +msgid "Saturday" +msgstr "சனி" + +msgid "Sunday" +msgstr "ஞாயிறு" + +msgid "Mon" +msgstr "" + +msgid "Tue" +msgstr "" + +msgid "Wed" +msgstr "" + +msgid "Thu" +msgstr "" + +msgid "Fri" +msgstr "" + +msgid "Sat" +msgstr "" + +msgid "Sun" +msgstr "" + +msgid "January" +msgstr "ஜனவரி" + +msgid "February" +msgstr "பிப்ரவரி" + +msgid "March" +msgstr "மார்ச்" + +msgid "April" +msgstr "ஏப்ரல்" + +msgid "May" +msgstr "மே" + +msgid "June" +msgstr "ஜூன்" + +msgid "July" +msgstr "ஜூலை" + +msgid "August" +msgstr "ஆகஸ்டு" + +msgid "September" +msgstr "செப்டம்பர்" + +msgid "October" +msgstr "அக்டோபர்" + +msgid "November" +msgstr "நவம்பர்" + +msgid "December" +msgstr "டிசம்பர்" + +msgid "jan" +msgstr "ஜன" + +msgid "feb" +msgstr "பிப்" + +msgid "mar" +msgstr "மார்" + +msgid "apr" +msgstr "ஏப்" + +msgid "may" +msgstr "மே" + +msgid "jun" +msgstr "ஜூன்" + +msgid "jul" +msgstr "ஜூலை" + +msgid "aug" +msgstr "ஆக" + +msgid "sep" +msgstr "செப்" + +msgid "oct" +msgstr "அக்" + +msgid "nov" +msgstr "நவ" + +msgid "dec" +msgstr "டிச" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "" + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "" + +msgctxt "abbrev. month" +msgid "March" +msgstr "மார்ச்" + +msgctxt "abbrev. month" +msgid "April" +msgstr "ஏப்ரல்" + +msgctxt "abbrev. month" +msgid "May" +msgstr "மே" + +msgctxt "abbrev. month" +msgid "June" +msgstr "ஜூன்" + +msgctxt "abbrev. month" +msgid "July" +msgstr "ஜூலை" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "" + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "" + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "" + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "" + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "" + +msgctxt "alt. month" +msgid "January" +msgstr "ஜனவரி" + +msgctxt "alt. month" +msgid "February" +msgstr "பிப்ரவரி" + +msgctxt "alt. month" +msgid "March" +msgstr "மார்ச்" + +msgctxt "alt. month" +msgid "April" +msgstr "ஏப்ரல்" + +msgctxt "alt. month" +msgid "May" +msgstr "மே" + +msgctxt "alt. month" +msgid "June" +msgstr "ஜூன்" + +msgctxt "alt. month" +msgid "July" +msgstr "ஜூலை" + +msgctxt "alt. month" +msgid "August" +msgstr "ஆகஸ்டு" + +msgctxt "alt. month" +msgid "September" +msgstr "செப்டம்பர்" + +msgctxt "alt. month" +msgid "October" +msgstr "அக்டோபர்" + +msgctxt "alt. month" +msgid "November" +msgstr "நவம்பர்" + +msgctxt "alt. month" +msgid "December" +msgstr "டிசம்பர்" + +msgid "This is not a valid IPv6 address." +msgstr "" + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "" + +msgid "or" +msgstr "" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr "" + +#, python-format +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "" +msgstr[1] "" + +msgid "Forbidden" +msgstr "" + +msgid "CSRF verification failed. Request aborted." +msgstr "" + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your Web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" + +msgid "More information is available with DEBUG=True." +msgstr "" + +msgid "No year specified" +msgstr "" + +msgid "Date out of range" +msgstr "" + +msgid "No month specified" +msgstr "" + +msgid "No day specified" +msgstr "" + +msgid "No week specified" +msgstr "" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" + +msgid "Directory indexes are not allowed here." +msgstr "" + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "" + +#, python-format +msgid "Index of %(directory)s" +msgstr "" + +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" + +msgid "The install worked successfully! Congratulations!" +msgstr "" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" + +msgid "Django Documentation" +msgstr "" + +msgid "Topics, references, & how-to’s" +msgstr "" + +msgid "Tutorial: A Polling App" +msgstr "" + +msgid "Get started with Django" +msgstr "" + +msgid "Django Community" +msgstr "" + +msgid "Connect, get help, or contribute" +msgstr "" diff --git a/virt/lib/python3.9/site-packages/django/conf/locale/uk/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/conf/locale/uk/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..31f94d84 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/conf/locale/uk/LC_MESSAGES/django 3.po @@ -0,0 +1,1365 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Oleksandr Chernihov , 2014 +# Boryslav Larin , 2011,2022 +# Denis Podlesniy , 2016 +# Igor Melnyk, 2014-2015,2017 +# Illia Volochii , 2019,2021-2023 +# Jannis Leidel , 2011 +# Kirill Gagarski , 2014 +# Max V. Stotsky , 2014 +# Mikhail Kolesnik , 2015 +# Mykola Zamkovoi , 2014 +# Alex Bolotov , 2013-2014 +# Roman Kozlovskyi , 2012 +# Sergiy Kuzmenko , 2011 +# tarasyyyk , 2018 +# tarasyyyk , 2019 +# Zoriana Zaiats, 2016-2017 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 06:49+0000\n" +"Last-Translator: Illia Volochii , 2019,2021-2023\n" +"Language-Team: Ukrainian (http://www.transifex.com/django/django/language/" +"uk/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: uk\n" +"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != " +"11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % " +"100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || " +"(n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n" + +msgid "Afrikaans" +msgstr "Африканська" + +msgid "Arabic" +msgstr "Арабська" + +msgid "Algerian Arabic" +msgstr "Алжирська арабська" + +msgid "Asturian" +msgstr "Астурійська" + +msgid "Azerbaijani" +msgstr "Азербайджанська" + +msgid "Bulgarian" +msgstr "Болгарська" + +msgid "Belarusian" +msgstr "Білоруська" + +msgid "Bengali" +msgstr "Бенгальська" + +msgid "Breton" +msgstr "Бретонська" + +msgid "Bosnian" +msgstr "Боснійська" + +msgid "Catalan" +msgstr "Каталонська" + +msgid "Central Kurdish (Sorani)" +msgstr "Центральнокурдська (сорані)" + +msgid "Czech" +msgstr "Чеська" + +msgid "Welsh" +msgstr "Валлійська" + +msgid "Danish" +msgstr "Датська" + +msgid "German" +msgstr "Німецька" + +msgid "Lower Sorbian" +msgstr "Нижньолужицька" + +msgid "Greek" +msgstr "Грецька" + +msgid "English" +msgstr "Англійська" + +msgid "Australian English" +msgstr "Австралійська англійська" + +msgid "British English" +msgstr "Англійська (Великобританія)" + +msgid "Esperanto" +msgstr "Есперанто" + +msgid "Spanish" +msgstr "Іспанська" + +msgid "Argentinian Spanish" +msgstr "Іспанська (Аргентина)" + +msgid "Colombian Spanish" +msgstr "Колумбійська іспанська" + +msgid "Mexican Spanish" +msgstr "Мексиканська іспанська" + +msgid "Nicaraguan Spanish" +msgstr "Нікарагуанська іспанська" + +msgid "Venezuelan Spanish" +msgstr "Венесуельська іспанська" + +msgid "Estonian" +msgstr "Естонська" + +msgid "Basque" +msgstr "Баскська" + +msgid "Persian" +msgstr "Перська" + +msgid "Finnish" +msgstr "Фінська" + +msgid "French" +msgstr "Французька" + +msgid "Frisian" +msgstr "Фризька" + +msgid "Irish" +msgstr "Ірландська" + +msgid "Scottish Gaelic" +msgstr "Шотландська ґельська" + +msgid "Galician" +msgstr "Галіційська" + +msgid "Hebrew" +msgstr "Іврит" + +msgid "Hindi" +msgstr "Хінді" + +msgid "Croatian" +msgstr "Хорватська" + +msgid "Upper Sorbian" +msgstr "Верхньолужицька" + +msgid "Hungarian" +msgstr "Угорська" + +msgid "Armenian" +msgstr "Вірменська" + +msgid "Interlingua" +msgstr "Інтерлінгва" + +msgid "Indonesian" +msgstr "Індонезійська" + +msgid "Igbo" +msgstr "Ігбо" + +msgid "Ido" +msgstr "Ідо" + +msgid "Icelandic" +msgstr "Ісландська" + +msgid "Italian" +msgstr "Італійська" + +msgid "Japanese" +msgstr "Японська" + +msgid "Georgian" +msgstr "Грузинська" + +msgid "Kabyle" +msgstr "Кабіли" + +msgid "Kazakh" +msgstr "Казахська" + +msgid "Khmer" +msgstr "Кхмерська" + +msgid "Kannada" +msgstr "Каннадська" + +msgid "Korean" +msgstr "Корейська" + +msgid "Kyrgyz" +msgstr "Киргизька" + +msgid "Luxembourgish" +msgstr "Люксембурзька" + +msgid "Lithuanian" +msgstr "Литовська" + +msgid "Latvian" +msgstr "Латвійська" + +msgid "Macedonian" +msgstr "Македонська" + +msgid "Malayalam" +msgstr "Малаялам" + +msgid "Mongolian" +msgstr "Монгольська" + +msgid "Marathi" +msgstr "Маратхі" + +msgid "Malay" +msgstr "Малайська" + +msgid "Burmese" +msgstr "Бірманська" + +msgid "Norwegian Bokmål" +msgstr "Норвезька (Букмол)" + +msgid "Nepali" +msgstr "Непальська" + +msgid "Dutch" +msgstr "Голландська" + +msgid "Norwegian Nynorsk" +msgstr "Норвезька (Нюнорськ)" + +msgid "Ossetic" +msgstr "Осетинська" + +msgid "Punjabi" +msgstr "Панджабі" + +msgid "Polish" +msgstr "Польська" + +msgid "Portuguese" +msgstr "Португальська" + +msgid "Brazilian Portuguese" +msgstr "Бразильська португальська" + +msgid "Romanian" +msgstr "Румунська" + +msgid "Russian" +msgstr "Російська" + +msgid "Slovak" +msgstr "Словацька" + +msgid "Slovenian" +msgstr "Словенська" + +msgid "Albanian" +msgstr "Албанська" + +msgid "Serbian" +msgstr "Сербська" + +msgid "Serbian Latin" +msgstr "Сербська (латинська)" + +msgid "Swedish" +msgstr "Шведська" + +msgid "Swahili" +msgstr "Суахілі" + +msgid "Tamil" +msgstr "Тамільська" + +msgid "Telugu" +msgstr "Телугу" + +msgid "Tajik" +msgstr "Таджицька" + +msgid "Thai" +msgstr "Тайська" + +msgid "Turkmen" +msgstr "Туркменська" + +msgid "Turkish" +msgstr "Турецька" + +msgid "Tatar" +msgstr "Татарська" + +msgid "Udmurt" +msgstr "Удмуртська" + +msgid "Ukrainian" +msgstr "Українська" + +msgid "Urdu" +msgstr "Урду" + +msgid "Uzbek" +msgstr "Узбецька" + +msgid "Vietnamese" +msgstr "В'єтнамська" + +msgid "Simplified Chinese" +msgstr "Китайська спрощена" + +msgid "Traditional Chinese" +msgstr "Китайська традиційна" + +msgid "Messages" +msgstr "Повідомлення" + +msgid "Site Maps" +msgstr "Мапи сайту" + +msgid "Static Files" +msgstr "Статичні файли" + +msgid "Syndication" +msgstr "Об'єднання" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +msgid "…" +msgstr "…" + +msgid "That page number is not an integer" +msgstr "Номер сторінки не є цілим числом" + +msgid "That page number is less than 1" +msgstr "Номер сторінки менше 1" + +msgid "That page contains no results" +msgstr "Сторінка не містить результатів" + +msgid "Enter a valid value." +msgstr "Введіть коректне значення." + +msgid "Enter a valid URL." +msgstr "Введіть коректний URL." + +msgid "Enter a valid integer." +msgstr "Введіть коректне ціле число." + +msgid "Enter a valid email address." +msgstr "Введіть коректну email адресу." + +#. Translators: "letters" means latin letters: a-z and A-Z. +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" + +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"Введіть коректне значення 'slug' (короткого заголовку), що може містити " +"тільки літери, числа, символи підкреслювання або дефіси." + +msgid "Enter a valid IPv4 address." +msgstr "Введіть коректну IPv4 адресу." + +msgid "Enter a valid IPv6 address." +msgstr "Введіть дійсну IPv6 адресу." + +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "Введіть дійсну IPv4 чи IPv6 адресу." + +msgid "Enter only digits separated by commas." +msgstr "Введіть тільки цифри, що розділені комами." + +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" +"Переконайтеся, що це значення дорівнює %(limit_value)s (зараз " +"%(show_value)s)." + +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "Переконайтеся, що це значення менше чи дорівнює %(limit_value)s." + +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "Переконайтеся, що це значення більше чи дорівнює %(limit_value)s." + +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "" + +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Переконайтеся, що це значення містить не менш ніж %(limit_value)d символ " +"(зараз %(show_value)d)." +msgstr[1] "" +"Переконайтеся, що це значення містить не менш ніж %(limit_value)d символів " +"(зараз %(show_value)d)." +msgstr[2] "" +"Переконайтеся, що це значення містить не менш ніж %(limit_value)d символів " +"(зараз %(show_value)d)." +msgstr[3] "" +"Переконайтеся, що це значення містить не менш ніж %(limit_value)d символів " +"(зараз %(show_value)d)." + +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Переконайтеся, що це значення містить не більше ніж %(limit_value)d символ " +"(зараз %(show_value)d)." +msgstr[1] "" +"Переконайтеся, що це значення містить не більше ніж %(limit_value)d символи " +"(зараз %(show_value)d)." +msgstr[2] "" +"Переконайтеся, що це значення містить не більше ніж %(limit_value)d символів " +"(зараз %(show_value)d)." +msgstr[3] "" +"Переконайтеся, що це значення містить не більше ніж %(limit_value)d символів " +"(зараз %(show_value)d)." + +msgid "Enter a number." +msgstr "Введіть число." + +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "Переконайтеся, що загалом тут не більше ніж %(max)s цифра." +msgstr[1] "Переконайтеся, що загалом тут не більше ніж %(max)s цифер." +msgstr[2] "Переконайтеся, що загалом тут не більше ніж %(max)s цифер." +msgstr[3] "Переконайтеся, що загалом тут не більше ніж %(max)s цифер." + +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "" +"Переконайтеся, що тут не більше ніж %(max)s цифра після десяткової коми." +msgstr[1] "" +"Переконайтеся, що тут не більше ніж %(max)s цифри після десяткової коми." +msgstr[2] "" +"Переконайтеся, що тут не більше ніж %(max)s цифер після десяткової коми." +msgstr[3] "" +"Переконайтеся, що тут не більше ніж %(max)s цифер після десяткової коми." + +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +"Переконайтеся, що тут не більше ніж %(max)s цифра до десяткової коми." +msgstr[1] "" +"Переконайтеся, що тут не більше ніж %(max)s цифри до десяткової коми." +msgstr[2] "" +"Переконайтеся, що тут не більше ніж %(max)s цифер до десяткової коми." +msgstr[3] "" +"Переконайтеся, що тут не більше ніж %(max)s цифер до десяткової коми." + +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" +"Розширення файлу '%(extension)s' не дозволено. Дозволені розширення: ' " +"%(allowed_extensions)s'." + +msgid "Null characters are not allowed." +msgstr "Символи Null не дозволені." + +msgid "and" +msgstr "та" + +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "%(model_name)s з таким %(field_labels)s вже існує." + +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "" + +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "Значення %(value)r не є дозволеним вибором." + +msgid "This field cannot be null." +msgstr "Це поле не може бути пустим." + +msgid "This field cannot be blank." +msgstr "Це поле не може бути порожнім." + +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "%(model_name)s з таким %(field_label)s вже існує." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"%(field_label)s повинне бути унікальним для %(date_field_label)s " +"%(lookup_type)s." + +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "Тип поля: %(field_type)s" + +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "" + +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "Значення \"%(value)s\" повинне бути True, False, або None." + +msgid "Boolean (Either True or False)" +msgstr "Булеве значення (True або False)" + +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "Рядок (до %(max_length)s)" + +msgid "String (unlimited)" +msgstr "Рядок (необмежений)" + +msgid "Comma-separated integers" +msgstr "Цілі, розділені комою" + +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" + +msgid "Date (without time)" +msgstr "Дата (без часу)" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"Значення \"%(value)s\" має невірний формат. Воно повинне бути у форматі YYYY-" +"MM-DD HH:MM[:ss[.uuuuuu]][TZ]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" + +msgid "Date (with time)" +msgstr "Дата (з часом)" + +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "" + +msgid "Decimal number" +msgstr "Десяткове число" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] [[HH:]MM:]ss[." +"uuuuuu] format." +msgstr "" + +msgid "Duration" +msgstr "Тривалість" + +msgid "Email address" +msgstr "E-mail адреса" + +msgid "File path" +msgstr "Шлях до файла" + +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "" + +msgid "Floating point number" +msgstr "Число з плаваючою комою" + +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "" + +msgid "Integer" +msgstr "Ціле число" + +msgid "Big (8 byte) integer" +msgstr "Велике (8 байтів) ціле число" + +msgid "Small integer" +msgstr "Мале ціле число" + +msgid "IPv4 address" +msgstr "IPv4 адреса" + +msgid "IP address" +msgstr "IP адреса" + +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "" + +msgid "Boolean (Either True, False or None)" +msgstr "Булеве значення (включаючи True, False або None)" + +msgid "Positive big integer" +msgstr "Додатнє велике ціле число" + +msgid "Positive integer" +msgstr "Додатнє ціле число" + +msgid "Positive small integer" +msgstr "Додатнє мале ціле число" + +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "Slug (до %(max_length)s)" + +msgid "Text" +msgstr "Текст" + +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" +"Значення \"%(value)s\" має невірний формат. Воно повинне бути у форматі HH:" +"MM[:ss[.uuuuuu]]." + +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" + +msgid "Time" +msgstr "Час" + +msgid "URL" +msgstr "URL" + +msgid "Raw binary data" +msgstr "Необроблені двійкові дані" + +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "“%(value)s” не є валідним UUID." + +msgid "Universally unique identifier" +msgstr "Універсальний унікальний ідентифікатор" + +msgid "File" +msgstr "Файл" + +msgid "Image" +msgstr "Зображення" + +msgid "A JSON object" +msgstr "JSON-об'єкт" + +msgid "Value must be valid JSON." +msgstr "Значення має бути коректним JSON." + +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "Екземпляр %(model)s з %(field)s %(value)r не існує." + +msgid "Foreign Key (type determined by related field)" +msgstr "Зовнішній ключ (тип визначається відповідно поля)" + +msgid "One-to-one relationship" +msgstr "Один-до-одного" + +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "%(from)s-%(to)s звязок" + +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "%(from)s-%(to)s звязки" + +msgid "Many-to-many relationship" +msgstr "Багато-до-багатьох" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the +#. label +msgid ":?.!" +msgstr ":?.!" + +msgid "This field is required." +msgstr "Це поле обов'язкове." + +msgid "Enter a whole number." +msgstr "Введіть ціле число." + +msgid "Enter a valid date." +msgstr "Введіть коректну дату." + +msgid "Enter a valid time." +msgstr "Введіть коректний час." + +msgid "Enter a valid date/time." +msgstr "Введіть коректну дату/час." + +msgid "Enter a valid duration." +msgstr "Введіть коректну тривалість." + +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "Кількість днів повинна бути від {min_days} до {max_days}." + +msgid "No file was submitted. Check the encoding type on the form." +msgstr "Файл не надіслано. Перевірте тип кодування форми." + +msgid "No file was submitted." +msgstr "Файл не було надіслано." + +msgid "The submitted file is empty." +msgstr "Переданий файл порожній." + +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"Переконайтеся, що це ім'я файлу містить не більше ніж з %(max)d символ " +"(зараз %(length)d)." +msgstr[1] "" +"Переконайтеся, що це ім'я файлу містить не більше ніж з %(max)d символи " +"(зараз %(length)d)." +msgstr[2] "" +"Переконайтеся, що це ім'я файлу містить не більше ніж з %(max)d символів " +"(зараз %(length)d)." +msgstr[3] "" +"Переконайтеся, що це ім'я файлу містить не більше ніж з %(max)d символів " +"(зараз %(length)d)." + +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" +"Будь ласка, або завантажте файл, або відмітьте прапорець очищення, а не " +"обидва варіанти одразу" + +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Завантажте правильний малюнок. Файл, який ви завантажили, не є малюнком, або " +"є зіпсованим малюнком." + +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "Зробить коректний вибір, %(value)s немає серед варіантів вибору." + +msgid "Enter a list of values." +msgstr "Введіть список значень." + +msgid "Enter a complete value." +msgstr "Введіть значення повністю." + +msgid "Enter a valid UUID." +msgstr "Введіть коректне значення UUID," + +msgid "Enter a valid JSON." +msgstr "Введіть коректний JSON." + +#. Translators: This is the default suffix added to form field labels +msgid ":" +msgstr ":" + +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(Приховане поле %(name)s) %(error)s" + +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" + +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +msgid "Order" +msgstr "Послідовність" + +msgid "Delete" +msgstr "Видалити" + +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "Будь ласка, виправте повторювані дані для поля %(field)s." + +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" +"Будь ласка, виправте повторювані дані для поля %(field)s, яке має бути " +"унікальним." + +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"Будь ласка, виправте повторювані дані для поля %(field_name)s, яке має бути " +"унікальним для вибірки %(lookup)s на %(date_field)s." + +msgid "Please correct the duplicate values below." +msgstr "Будь ласка, виправте повторювані значення нижче." + +msgid "The inline value did not match the parent instance." +msgstr "Зв'язане значення не відповідає батьківському екземпляру." + +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "Зробить коректний вибір. Такого варіанту нема серед доступних." + +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "" + +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" + +msgid "Clear" +msgstr "Очистити" + +msgid "Currently" +msgstr "Наразі" + +msgid "Change" +msgstr "Змінити" + +msgid "Unknown" +msgstr "Невідомо" + +msgid "Yes" +msgstr "Так" + +msgid "No" +msgstr "Ні" + +#. Translators: Please do not add spaces around commas. +msgid "yes,no,maybe" +msgstr "так,ні,можливо" + +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d байт" +msgstr[1] "%(size)d байти" +msgstr[2] "%(size)d байтів" +msgstr[3] "%(size)d байтів" + +#, python-format +msgid "%s KB" +msgstr "%s Кб" + +#, python-format +msgid "%s MB" +msgstr "%s Мб" + +#, python-format +msgid "%s GB" +msgstr "%s Гб" + +#, python-format +msgid "%s TB" +msgstr "%s Тб" + +#, python-format +msgid "%s PB" +msgstr "%s Пб" + +msgid "p.m." +msgstr "після полудня" + +msgid "a.m." +msgstr "до полудня" + +msgid "PM" +msgstr "після полудня" + +msgid "AM" +msgstr "до полудня" + +msgid "midnight" +msgstr "північ" + +msgid "noon" +msgstr "полудень" + +msgid "Monday" +msgstr "Понеділок" + +msgid "Tuesday" +msgstr "Вівторок" + +msgid "Wednesday" +msgstr "Середа" + +msgid "Thursday" +msgstr "Четвер" + +msgid "Friday" +msgstr "П'ятниця" + +msgid "Saturday" +msgstr "Субота" + +msgid "Sunday" +msgstr "Неділя" + +msgid "Mon" +msgstr "Пн" + +msgid "Tue" +msgstr "Вт" + +msgid "Wed" +msgstr "Ср" + +msgid "Thu" +msgstr "Чт" + +msgid "Fri" +msgstr "Пт" + +msgid "Sat" +msgstr "Сб" + +msgid "Sun" +msgstr "Нд" + +msgid "January" +msgstr "Січень" + +msgid "February" +msgstr "Лютий" + +msgid "March" +msgstr "Березень" + +msgid "April" +msgstr "Квітень" + +msgid "May" +msgstr "Травень" + +msgid "June" +msgstr "Червень" + +msgid "July" +msgstr "Липень" + +msgid "August" +msgstr "Серпень" + +msgid "September" +msgstr "Вересень" + +msgid "October" +msgstr "Жовтень" + +msgid "November" +msgstr "Листопад" + +msgid "December" +msgstr "Грудень" + +msgid "jan" +msgstr "січ" + +msgid "feb" +msgstr "лют" + +msgid "mar" +msgstr "бер" + +msgid "apr" +msgstr "кві" + +msgid "may" +msgstr "тра" + +msgid "jun" +msgstr "чер" + +msgid "jul" +msgstr "лип" + +msgid "aug" +msgstr "сер" + +msgid "sep" +msgstr "вер" + +msgid "oct" +msgstr "жов" + +msgid "nov" +msgstr "лис" + +msgid "dec" +msgstr "гру" + +msgctxt "abbrev. month" +msgid "Jan." +msgstr "Січ." + +msgctxt "abbrev. month" +msgid "Feb." +msgstr "Лют." + +msgctxt "abbrev. month" +msgid "March" +msgstr "Березень" + +msgctxt "abbrev. month" +msgid "April" +msgstr "Квітень" + +msgctxt "abbrev. month" +msgid "May" +msgstr "Травень" + +msgctxt "abbrev. month" +msgid "June" +msgstr "Червень" + +msgctxt "abbrev. month" +msgid "July" +msgstr "Липень" + +msgctxt "abbrev. month" +msgid "Aug." +msgstr "Сер." + +msgctxt "abbrev. month" +msgid "Sept." +msgstr "Вер." + +msgctxt "abbrev. month" +msgid "Oct." +msgstr "Жов." + +msgctxt "abbrev. month" +msgid "Nov." +msgstr "Лис." + +msgctxt "abbrev. month" +msgid "Dec." +msgstr "Гру." + +msgctxt "alt. month" +msgid "January" +msgstr "січня" + +msgctxt "alt. month" +msgid "February" +msgstr "лютого" + +msgctxt "alt. month" +msgid "March" +msgstr "березня" + +msgctxt "alt. month" +msgid "April" +msgstr "квітня" + +msgctxt "alt. month" +msgid "May" +msgstr "травня" + +msgctxt "alt. month" +msgid "June" +msgstr "червня" + +msgctxt "alt. month" +msgid "July" +msgstr "липня" + +msgctxt "alt. month" +msgid "August" +msgstr "серпня" + +msgctxt "alt. month" +msgid "September" +msgstr "вересня" + +msgctxt "alt. month" +msgid "October" +msgstr "жовтня" + +msgctxt "alt. month" +msgid "November" +msgstr "листопада" + +msgctxt "alt. month" +msgid "December" +msgstr "грудня" + +msgid "This is not a valid IPv6 address." +msgstr "Це не є правильною адресою IPv6." + +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "%(truncated_text)s…" + +msgid "or" +msgstr "або" + +#. Translators: This string is used as a separator between list elements +msgid ", " +msgstr ", " + +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "%(num)d рік" +msgstr[1] "%(num)d роки" +msgstr[2] "%(num)d років" +msgstr[3] "%(num)d років" + +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "%(num)d місяць" +msgstr[1] "%(num)d місяці" +msgstr[2] "%(num)d місяців" +msgstr[3] "%(num)d місяців" + +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "%(num)d тиждень" +msgstr[1] "%(num)d тижні" +msgstr[2] "%(num)d тижнів" +msgstr[3] "%(num)d тижнів" + +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "%(num)d день" +msgstr[1] "%(num)d дні" +msgstr[2] "%(num)d днів" +msgstr[3] "%(num)d днів" + +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "%(num)d година" +msgstr[1] "%(num)d години" +msgstr[2] "%(num)d годин" +msgstr[3] "%(num)d годин" + +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "%(num)d хвилина" +msgstr[1] "%(num)d хвилини" +msgstr[2] "%(num)d хвилин" +msgstr[3] "%(num)d хвилин" + +msgid "Forbidden" +msgstr "Заборонено" + +msgid "CSRF verification failed. Request aborted." +msgstr "Помилка перевірки CSRF. Запит відхилений." + +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" + +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" +"Якщо ви налаштували свій браузер таким чином, щоб заборонити йому передавати " +"заголовок «Referer», будь ласка, дозвольте йому відсилати даний заголовок " +"принаймні для даного сайту, або для всіх HTTPS-з'єднань, або для подібних " +"запитів." + +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" + +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"Ви бачите це повідомлення, тому що даний сайт вимагає, щоб при відправці " +"форм була відправлена ​​і CSRF-cookie. Даний тип cookie необхідний з міркувань " +"безпеки, щоб переконатися, що ваш браузер не був взламаний третьою стороною." + +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" + +msgid "More information is available with DEBUG=True." +msgstr "Більше інформації можна отримати з DEBUG=True." + +msgid "No year specified" +msgstr "Рік не вказано" + +msgid "Date out of range" +msgstr "Дата поза діапазоном" + +msgid "No month specified" +msgstr "Місяць не вказано" + +msgid "No day specified" +msgstr "День не вказано" + +msgid "No week specified" +msgstr "Тиждень не вказано" + +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "%(verbose_name_plural)s недоступні" + +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"Майбутні %(verbose_name_plural)s недоступні, тому що %(class_name)s." +"allow_future має нульове значення." + +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" + +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "Жодні %(verbose_name)s не були знайдені по запиту" + +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" + +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Невірна сторінка (%(page_number)s): %(message)s" + +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" + +msgid "Directory indexes are not allowed here." +msgstr "Перегляд вмісту каталогу не дозволено." + +#, python-format +msgid "“%(path)s” does not exist" +msgstr "\"%(path)s\" не існує" + +#, python-format +msgid "Index of %(directory)s" +msgstr "Вміст директорії %(directory)s" + +msgid "The install worked successfully! Congratulations!" +msgstr "Вітаємо, команда install завершилась успішно!" + +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"Нотатки релізу for Django %(version)s" + +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not " +"configured any URLs." +msgstr "" +"Ви бачите цю сторінку тому що змінна DEBUG встановлена на True у вашому файлі " +"конфігурації і ви не налаштували жодного URL." + +msgid "Django Documentation" +msgstr "Документація Django" + +msgid "Topics, references, & how-to’s" +msgstr "Статті, довідки та інструкції" + +msgid "Tutorial: A Polling App" +msgstr "Посібник: програма голосування" + +msgid "Get started with Django" +msgstr "Початок роботи з Django" + +msgid "Django Community" +msgstr "Спільнота Django" + +msgid "Connect, get help, or contribute" +msgstr "Отримати допомогу, чи допомогти" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/checks 3.py b/virt/lib/python3.9/site-packages/django/contrib/admin/checks 3.py new file mode 100644 index 00000000..8f7f71a6 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/checks 3.py @@ -0,0 +1,1350 @@ +import collections +from itertools import chain + +from django.apps import apps +from django.conf import settings +from django.contrib.admin.utils import NotRelationField, flatten, get_fields_from_path +from django.core import checks +from django.core.exceptions import FieldDoesNotExist +from django.db import models +from django.db.models.constants import LOOKUP_SEP +from django.db.models.expressions import Combinable +from django.forms.models import BaseModelForm, BaseModelFormSet, _get_foreign_key +from django.template import engines +from django.template.backends.django import DjangoTemplates +from django.utils.module_loading import import_string + + +def _issubclass(cls, classinfo): + """ + issubclass() variant that doesn't raise an exception if cls isn't a + class. + """ + try: + return issubclass(cls, classinfo) + except TypeError: + return False + + +def _contains_subclass(class_path, candidate_paths): + """ + Return whether or not a dotted class path (or a subclass of that class) is + found in a list of candidate paths. + """ + cls = import_string(class_path) + for path in candidate_paths: + try: + candidate_cls = import_string(path) + except ImportError: + # ImportErrors are raised elsewhere. + continue + if _issubclass(candidate_cls, cls): + return True + return False + + +def check_admin_app(app_configs, **kwargs): + from django.contrib.admin.sites import all_sites + + errors = [] + for site in all_sites: + errors.extend(site.check(app_configs)) + return errors + + +def check_dependencies(**kwargs): + """ + Check that the admin's dependencies are correctly installed. + """ + from django.contrib.admin.sites import all_sites + + if not apps.is_installed("django.contrib.admin"): + return [] + errors = [] + app_dependencies = ( + ("django.contrib.contenttypes", 401), + ("django.contrib.auth", 405), + ("django.contrib.messages", 406), + ) + for app_name, error_code in app_dependencies: + if not apps.is_installed(app_name): + errors.append( + checks.Error( + "'%s' must be in INSTALLED_APPS in order to use the admin " + "application." % app_name, + id="admin.E%d" % error_code, + ) + ) + for engine in engines.all(): + if isinstance(engine, DjangoTemplates): + django_templates_instance = engine.engine + break + else: + django_templates_instance = None + if not django_templates_instance: + errors.append( + checks.Error( + "A 'django.template.backends.django.DjangoTemplates' instance " + "must be configured in TEMPLATES in order to use the admin " + "application.", + id="admin.E403", + ) + ) + else: + if ( + "django.contrib.auth.context_processors.auth" + not in django_templates_instance.context_processors + and _contains_subclass( + "django.contrib.auth.backends.ModelBackend", + settings.AUTHENTICATION_BACKENDS, + ) + ): + errors.append( + checks.Error( + "'django.contrib.auth.context_processors.auth' must be " + "enabled in DjangoTemplates (TEMPLATES) if using the default " + "auth backend in order to use the admin application.", + id="admin.E402", + ) + ) + if ( + "django.contrib.messages.context_processors.messages" + not in django_templates_instance.context_processors + ): + errors.append( + checks.Error( + "'django.contrib.messages.context_processors.messages' must " + "be enabled in DjangoTemplates (TEMPLATES) in order to use " + "the admin application.", + id="admin.E404", + ) + ) + sidebar_enabled = any(site.enable_nav_sidebar for site in all_sites) + if ( + sidebar_enabled + and "django.template.context_processors.request" + not in django_templates_instance.context_processors + ): + errors.append( + checks.Warning( + "'django.template.context_processors.request' must be enabled " + "in DjangoTemplates (TEMPLATES) in order to use the admin " + "navigation sidebar.", + id="admin.W411", + ) + ) + + if not _contains_subclass( + "django.contrib.auth.middleware.AuthenticationMiddleware", settings.MIDDLEWARE + ): + errors.append( + checks.Error( + "'django.contrib.auth.middleware.AuthenticationMiddleware' must " + "be in MIDDLEWARE in order to use the admin application.", + id="admin.E408", + ) + ) + if not _contains_subclass( + "django.contrib.messages.middleware.MessageMiddleware", settings.MIDDLEWARE + ): + errors.append( + checks.Error( + "'django.contrib.messages.middleware.MessageMiddleware' must " + "be in MIDDLEWARE in order to use the admin application.", + id="admin.E409", + ) + ) + if not _contains_subclass( + "django.contrib.sessions.middleware.SessionMiddleware", settings.MIDDLEWARE + ): + errors.append( + checks.Error( + "'django.contrib.sessions.middleware.SessionMiddleware' must " + "be in MIDDLEWARE in order to use the admin application.", + hint=( + "Insert " + "'django.contrib.sessions.middleware.SessionMiddleware' " + "before " + "'django.contrib.auth.middleware.AuthenticationMiddleware'." + ), + id="admin.E410", + ) + ) + return errors + + +class BaseModelAdminChecks: + def check(self, admin_obj, **kwargs): + return [ + *self._check_autocomplete_fields(admin_obj), + *self._check_raw_id_fields(admin_obj), + *self._check_fields(admin_obj), + *self._check_fieldsets(admin_obj), + *self._check_exclude(admin_obj), + *self._check_form(admin_obj), + *self._check_filter_vertical(admin_obj), + *self._check_filter_horizontal(admin_obj), + *self._check_radio_fields(admin_obj), + *self._check_prepopulated_fields(admin_obj), + *self._check_view_on_site_url(admin_obj), + *self._check_ordering(admin_obj), + *self._check_readonly_fields(admin_obj), + ] + + def _check_autocomplete_fields(self, obj): + """ + Check that `autocomplete_fields` is a list or tuple of model fields. + """ + if not isinstance(obj.autocomplete_fields, (list, tuple)): + return must_be( + "a list or tuple", + option="autocomplete_fields", + obj=obj, + id="admin.E036", + ) + else: + return list( + chain.from_iterable( + [ + self._check_autocomplete_fields_item( + obj, field_name, "autocomplete_fields[%d]" % index + ) + for index, field_name in enumerate(obj.autocomplete_fields) + ] + ) + ) + + def _check_autocomplete_fields_item(self, obj, field_name, label): + """ + Check that an item in `autocomplete_fields` is a ForeignKey or a + ManyToManyField and that the item has a related ModelAdmin with + search_fields defined. + """ + try: + field = obj.model._meta.get_field(field_name) + except FieldDoesNotExist: + return refer_to_missing_field( + field=field_name, option=label, obj=obj, id="admin.E037" + ) + else: + if not field.many_to_many and not isinstance(field, models.ForeignKey): + return must_be( + "a foreign key or a many-to-many field", + option=label, + obj=obj, + id="admin.E038", + ) + related_admin = obj.admin_site._registry.get(field.remote_field.model) + if related_admin is None: + return [ + checks.Error( + 'An admin for model "%s" has to be registered ' + "to be referenced by %s.autocomplete_fields." + % ( + field.remote_field.model.__name__, + type(obj).__name__, + ), + obj=obj.__class__, + id="admin.E039", + ) + ] + elif not related_admin.search_fields: + return [ + checks.Error( + '%s must define "search_fields", because it\'s ' + "referenced by %s.autocomplete_fields." + % ( + related_admin.__class__.__name__, + type(obj).__name__, + ), + obj=obj.__class__, + id="admin.E040", + ) + ] + return [] + + def _check_raw_id_fields(self, obj): + """Check that `raw_id_fields` only contains field names that are listed + on the model.""" + + if not isinstance(obj.raw_id_fields, (list, tuple)): + return must_be( + "a list or tuple", option="raw_id_fields", obj=obj, id="admin.E001" + ) + else: + return list( + chain.from_iterable( + self._check_raw_id_fields_item( + obj, field_name, "raw_id_fields[%d]" % index + ) + for index, field_name in enumerate(obj.raw_id_fields) + ) + ) + + def _check_raw_id_fields_item(self, obj, field_name, label): + """Check an item of `raw_id_fields`, i.e. check that field named + `field_name` exists in model `model` and is a ForeignKey or a + ManyToManyField.""" + + try: + field = obj.model._meta.get_field(field_name) + except FieldDoesNotExist: + return refer_to_missing_field( + field=field_name, option=label, obj=obj, id="admin.E002" + ) + else: + # Using attname is not supported. + if field.name != field_name: + return refer_to_missing_field( + field=field_name, + option=label, + obj=obj, + id="admin.E002", + ) + if not field.many_to_many and not isinstance(field, models.ForeignKey): + return must_be( + "a foreign key or a many-to-many field", + option=label, + obj=obj, + id="admin.E003", + ) + else: + return [] + + def _check_fields(self, obj): + """Check that `fields` only refer to existing fields, doesn't contain + duplicates. Check if at most one of `fields` and `fieldsets` is defined. + """ + + if obj.fields is None: + return [] + elif not isinstance(obj.fields, (list, tuple)): + return must_be("a list or tuple", option="fields", obj=obj, id="admin.E004") + elif obj.fieldsets: + return [ + checks.Error( + "Both 'fieldsets' and 'fields' are specified.", + obj=obj.__class__, + id="admin.E005", + ) + ] + fields = flatten(obj.fields) + if len(fields) != len(set(fields)): + return [ + checks.Error( + "The value of 'fields' contains duplicate field(s).", + obj=obj.__class__, + id="admin.E006", + ) + ] + + return list( + chain.from_iterable( + self._check_field_spec(obj, field_name, "fields") + for field_name in obj.fields + ) + ) + + def _check_fieldsets(self, obj): + """Check that fieldsets is properly formatted and doesn't contain + duplicates.""" + + if obj.fieldsets is None: + return [] + elif not isinstance(obj.fieldsets, (list, tuple)): + return must_be( + "a list or tuple", option="fieldsets", obj=obj, id="admin.E007" + ) + else: + seen_fields = [] + return list( + chain.from_iterable( + self._check_fieldsets_item( + obj, fieldset, "fieldsets[%d]" % index, seen_fields + ) + for index, fieldset in enumerate(obj.fieldsets) + ) + ) + + def _check_fieldsets_item(self, obj, fieldset, label, seen_fields): + """Check an item of `fieldsets`, i.e. check that this is a pair of a + set name and a dictionary containing "fields" key.""" + + if not isinstance(fieldset, (list, tuple)): + return must_be("a list or tuple", option=label, obj=obj, id="admin.E008") + elif len(fieldset) != 2: + return must_be("of length 2", option=label, obj=obj, id="admin.E009") + elif not isinstance(fieldset[1], dict): + return must_be( + "a dictionary", option="%s[1]" % label, obj=obj, id="admin.E010" + ) + elif "fields" not in fieldset[1]: + return [ + checks.Error( + "The value of '%s[1]' must contain the key 'fields'." % label, + obj=obj.__class__, + id="admin.E011", + ) + ] + elif not isinstance(fieldset[1]["fields"], (list, tuple)): + return must_be( + "a list or tuple", + option="%s[1]['fields']" % label, + obj=obj, + id="admin.E008", + ) + + seen_fields.extend(flatten(fieldset[1]["fields"])) + if len(seen_fields) != len(set(seen_fields)): + return [ + checks.Error( + "There are duplicate field(s) in '%s[1]'." % label, + obj=obj.__class__, + id="admin.E012", + ) + ] + return list( + chain.from_iterable( + self._check_field_spec(obj, fieldset_fields, '%s[1]["fields"]' % label) + for fieldset_fields in fieldset[1]["fields"] + ) + ) + + def _check_field_spec(self, obj, fields, label): + """`fields` should be an item of `fields` or an item of + fieldset[1]['fields'] for any `fieldset` in `fieldsets`. It should be a + field name or a tuple of field names.""" + + if isinstance(fields, tuple): + return list( + chain.from_iterable( + self._check_field_spec_item( + obj, field_name, "%s[%d]" % (label, index) + ) + for index, field_name in enumerate(fields) + ) + ) + else: + return self._check_field_spec_item(obj, fields, label) + + def _check_field_spec_item(self, obj, field_name, label): + if field_name in obj.readonly_fields: + # Stuff can be put in fields that isn't actually a model field if + # it's in readonly_fields, readonly_fields will handle the + # validation of such things. + return [] + else: + try: + field = obj.model._meta.get_field(field_name) + except FieldDoesNotExist: + # If we can't find a field on the model that matches, it could + # be an extra field on the form. + return [] + else: + if ( + isinstance(field, models.ManyToManyField) + and not field.remote_field.through._meta.auto_created + ): + return [ + checks.Error( + "The value of '%s' cannot include the ManyToManyField " + "'%s', because that field manually specifies a " + "relationship model." % (label, field_name), + obj=obj.__class__, + id="admin.E013", + ) + ] + else: + return [] + + def _check_exclude(self, obj): + """Check that exclude is a sequence without duplicates.""" + + if obj.exclude is None: # default value is None + return [] + elif not isinstance(obj.exclude, (list, tuple)): + return must_be( + "a list or tuple", option="exclude", obj=obj, id="admin.E014" + ) + elif len(obj.exclude) > len(set(obj.exclude)): + return [ + checks.Error( + "The value of 'exclude' contains duplicate field(s).", + obj=obj.__class__, + id="admin.E015", + ) + ] + else: + return [] + + def _check_form(self, obj): + """Check that form subclasses BaseModelForm.""" + if not _issubclass(obj.form, BaseModelForm): + return must_inherit_from( + parent="BaseModelForm", option="form", obj=obj, id="admin.E016" + ) + else: + return [] + + def _check_filter_vertical(self, obj): + """Check that filter_vertical is a sequence of field names.""" + if not isinstance(obj.filter_vertical, (list, tuple)): + return must_be( + "a list or tuple", option="filter_vertical", obj=obj, id="admin.E017" + ) + else: + return list( + chain.from_iterable( + self._check_filter_item( + obj, field_name, "filter_vertical[%d]" % index + ) + for index, field_name in enumerate(obj.filter_vertical) + ) + ) + + def _check_filter_horizontal(self, obj): + """Check that filter_horizontal is a sequence of field names.""" + if not isinstance(obj.filter_horizontal, (list, tuple)): + return must_be( + "a list or tuple", option="filter_horizontal", obj=obj, id="admin.E018" + ) + else: + return list( + chain.from_iterable( + self._check_filter_item( + obj, field_name, "filter_horizontal[%d]" % index + ) + for index, field_name in enumerate(obj.filter_horizontal) + ) + ) + + def _check_filter_item(self, obj, field_name, label): + """Check one item of `filter_vertical` or `filter_horizontal`, i.e. + check that given field exists and is a ManyToManyField.""" + + try: + field = obj.model._meta.get_field(field_name) + except FieldDoesNotExist: + return refer_to_missing_field( + field=field_name, option=label, obj=obj, id="admin.E019" + ) + else: + if not field.many_to_many: + return must_be( + "a many-to-many field", option=label, obj=obj, id="admin.E020" + ) + else: + return [] + + def _check_radio_fields(self, obj): + """Check that `radio_fields` is a dictionary.""" + if not isinstance(obj.radio_fields, dict): + return must_be( + "a dictionary", option="radio_fields", obj=obj, id="admin.E021" + ) + else: + return list( + chain.from_iterable( + self._check_radio_fields_key(obj, field_name, "radio_fields") + + self._check_radio_fields_value( + obj, val, 'radio_fields["%s"]' % field_name + ) + for field_name, val in obj.radio_fields.items() + ) + ) + + def _check_radio_fields_key(self, obj, field_name, label): + """Check that a key of `radio_fields` dictionary is name of existing + field and that the field is a ForeignKey or has `choices` defined.""" + + try: + field = obj.model._meta.get_field(field_name) + except FieldDoesNotExist: + return refer_to_missing_field( + field=field_name, option=label, obj=obj, id="admin.E022" + ) + else: + if not (isinstance(field, models.ForeignKey) or field.choices): + return [ + checks.Error( + "The value of '%s' refers to '%s', which is not an " + "instance of ForeignKey, and does not have a 'choices' " + "definition." % (label, field_name), + obj=obj.__class__, + id="admin.E023", + ) + ] + else: + return [] + + def _check_radio_fields_value(self, obj, val, label): + """Check type of a value of `radio_fields` dictionary.""" + + from django.contrib.admin.options import HORIZONTAL, VERTICAL + + if val not in (HORIZONTAL, VERTICAL): + return [ + checks.Error( + "The value of '%s' must be either admin.HORIZONTAL or " + "admin.VERTICAL." % label, + obj=obj.__class__, + id="admin.E024", + ) + ] + else: + return [] + + def _check_view_on_site_url(self, obj): + if not callable(obj.view_on_site) and not isinstance(obj.view_on_site, bool): + return [ + checks.Error( + "The value of 'view_on_site' must be a callable or a boolean " + "value.", + obj=obj.__class__, + id="admin.E025", + ) + ] + else: + return [] + + def _check_prepopulated_fields(self, obj): + """Check that `prepopulated_fields` is a dictionary containing allowed + field types.""" + if not isinstance(obj.prepopulated_fields, dict): + return must_be( + "a dictionary", option="prepopulated_fields", obj=obj, id="admin.E026" + ) + else: + return list( + chain.from_iterable( + self._check_prepopulated_fields_key( + obj, field_name, "prepopulated_fields" + ) + + self._check_prepopulated_fields_value( + obj, val, 'prepopulated_fields["%s"]' % field_name + ) + for field_name, val in obj.prepopulated_fields.items() + ) + ) + + def _check_prepopulated_fields_key(self, obj, field_name, label): + """Check a key of `prepopulated_fields` dictionary, i.e. check that it + is a name of existing field and the field is one of the allowed types. + """ + + try: + field = obj.model._meta.get_field(field_name) + except FieldDoesNotExist: + return refer_to_missing_field( + field=field_name, option=label, obj=obj, id="admin.E027" + ) + else: + if isinstance( + field, (models.DateTimeField, models.ForeignKey, models.ManyToManyField) + ): + return [ + checks.Error( + "The value of '%s' refers to '%s', which must not be a " + "DateTimeField, a ForeignKey, a OneToOneField, or a " + "ManyToManyField." % (label, field_name), + obj=obj.__class__, + id="admin.E028", + ) + ] + else: + return [] + + def _check_prepopulated_fields_value(self, obj, val, label): + """Check a value of `prepopulated_fields` dictionary, i.e. it's an + iterable of existing fields.""" + + if not isinstance(val, (list, tuple)): + return must_be("a list or tuple", option=label, obj=obj, id="admin.E029") + else: + return list( + chain.from_iterable( + self._check_prepopulated_fields_value_item( + obj, subfield_name, "%s[%r]" % (label, index) + ) + for index, subfield_name in enumerate(val) + ) + ) + + def _check_prepopulated_fields_value_item(self, obj, field_name, label): + """For `prepopulated_fields` equal to {"slug": ("title",)}, + `field_name` is "title".""" + + try: + obj.model._meta.get_field(field_name) + except FieldDoesNotExist: + return refer_to_missing_field( + field=field_name, option=label, obj=obj, id="admin.E030" + ) + else: + return [] + + def _check_ordering(self, obj): + """Check that ordering refers to existing fields or is random.""" + + # ordering = None + if obj.ordering is None: # The default value is None + return [] + elif not isinstance(obj.ordering, (list, tuple)): + return must_be( + "a list or tuple", option="ordering", obj=obj, id="admin.E031" + ) + else: + return list( + chain.from_iterable( + self._check_ordering_item(obj, field_name, "ordering[%d]" % index) + for index, field_name in enumerate(obj.ordering) + ) + ) + + def _check_ordering_item(self, obj, field_name, label): + """Check that `ordering` refers to existing fields.""" + if isinstance(field_name, (Combinable, models.OrderBy)): + if not isinstance(field_name, models.OrderBy): + field_name = field_name.asc() + if isinstance(field_name.expression, models.F): + field_name = field_name.expression.name + else: + return [] + if field_name == "?" and len(obj.ordering) != 1: + return [ + checks.Error( + "The value of 'ordering' has the random ordering marker '?', " + "but contains other fields as well.", + hint='Either remove the "?", or remove the other fields.', + obj=obj.__class__, + id="admin.E032", + ) + ] + elif field_name == "?": + return [] + elif LOOKUP_SEP in field_name: + # Skip ordering in the format field1__field2 (FIXME: checking + # this format would be nice, but it's a little fiddly). + return [] + else: + if field_name.startswith("-"): + field_name = field_name[1:] + if field_name == "pk": + return [] + try: + obj.model._meta.get_field(field_name) + except FieldDoesNotExist: + return refer_to_missing_field( + field=field_name, option=label, obj=obj, id="admin.E033" + ) + else: + return [] + + def _check_readonly_fields(self, obj): + """Check that readonly_fields refers to proper attribute or field.""" + + if obj.readonly_fields == (): + return [] + elif not isinstance(obj.readonly_fields, (list, tuple)): + return must_be( + "a list or tuple", option="readonly_fields", obj=obj, id="admin.E034" + ) + else: + return list( + chain.from_iterable( + self._check_readonly_fields_item( + obj, field_name, "readonly_fields[%d]" % index + ) + for index, field_name in enumerate(obj.readonly_fields) + ) + ) + + def _check_readonly_fields_item(self, obj, field_name, label): + if callable(field_name): + return [] + elif hasattr(obj, field_name): + return [] + elif hasattr(obj.model, field_name): + return [] + else: + try: + obj.model._meta.get_field(field_name) + except FieldDoesNotExist: + return [ + checks.Error( + "The value of '%s' is not a callable, an attribute of " + "'%s', or an attribute of '%s'." + % ( + label, + obj.__class__.__name__, + obj.model._meta.label, + ), + obj=obj.__class__, + id="admin.E035", + ) + ] + else: + return [] + + +class ModelAdminChecks(BaseModelAdminChecks): + def check(self, admin_obj, **kwargs): + return [ + *super().check(admin_obj), + *self._check_save_as(admin_obj), + *self._check_save_on_top(admin_obj), + *self._check_inlines(admin_obj), + *self._check_list_display(admin_obj), + *self._check_list_display_links(admin_obj), + *self._check_list_filter(admin_obj), + *self._check_list_select_related(admin_obj), + *self._check_list_per_page(admin_obj), + *self._check_list_max_show_all(admin_obj), + *self._check_list_editable(admin_obj), + *self._check_search_fields(admin_obj), + *self._check_date_hierarchy(admin_obj), + *self._check_action_permission_methods(admin_obj), + *self._check_actions_uniqueness(admin_obj), + ] + + def _check_save_as(self, obj): + """Check save_as is a boolean.""" + + if not isinstance(obj.save_as, bool): + return must_be("a boolean", option="save_as", obj=obj, id="admin.E101") + else: + return [] + + def _check_save_on_top(self, obj): + """Check save_on_top is a boolean.""" + + if not isinstance(obj.save_on_top, bool): + return must_be("a boolean", option="save_on_top", obj=obj, id="admin.E102") + else: + return [] + + def _check_inlines(self, obj): + """Check all inline model admin classes.""" + + if not isinstance(obj.inlines, (list, tuple)): + return must_be( + "a list or tuple", option="inlines", obj=obj, id="admin.E103" + ) + else: + return list( + chain.from_iterable( + self._check_inlines_item(obj, item, "inlines[%d]" % index) + for index, item in enumerate(obj.inlines) + ) + ) + + def _check_inlines_item(self, obj, inline, label): + """Check one inline model admin.""" + try: + inline_label = inline.__module__ + "." + inline.__name__ + except AttributeError: + return [ + checks.Error( + "'%s' must inherit from 'InlineModelAdmin'." % obj, + obj=obj.__class__, + id="admin.E104", + ) + ] + + from django.contrib.admin.options import InlineModelAdmin + + if not _issubclass(inline, InlineModelAdmin): + return [ + checks.Error( + "'%s' must inherit from 'InlineModelAdmin'." % inline_label, + obj=obj.__class__, + id="admin.E104", + ) + ] + elif not inline.model: + return [ + checks.Error( + "'%s' must have a 'model' attribute." % inline_label, + obj=obj.__class__, + id="admin.E105", + ) + ] + elif not _issubclass(inline.model, models.Model): + return must_be( + "a Model", option="%s.model" % inline_label, obj=obj, id="admin.E106" + ) + else: + return inline(obj.model, obj.admin_site).check() + + def _check_list_display(self, obj): + """Check that list_display only contains fields or usable attributes.""" + + if not isinstance(obj.list_display, (list, tuple)): + return must_be( + "a list or tuple", option="list_display", obj=obj, id="admin.E107" + ) + else: + return list( + chain.from_iterable( + self._check_list_display_item(obj, item, "list_display[%d]" % index) + for index, item in enumerate(obj.list_display) + ) + ) + + def _check_list_display_item(self, obj, item, label): + if callable(item): + return [] + elif hasattr(obj, item): + return [] + try: + field = obj.model._meta.get_field(item) + except FieldDoesNotExist: + try: + field = getattr(obj.model, item) + except AttributeError: + return [ + checks.Error( + "The value of '%s' refers to '%s', which is not a " + "callable, an attribute of '%s', or an attribute or " + "method on '%s'." + % ( + label, + item, + obj.__class__.__name__, + obj.model._meta.label, + ), + obj=obj.__class__, + id="admin.E108", + ) + ] + if isinstance(field, models.ManyToManyField): + return [ + checks.Error( + "The value of '%s' must not be a ManyToManyField." % label, + obj=obj.__class__, + id="admin.E109", + ) + ] + return [] + + def _check_list_display_links(self, obj): + """Check that list_display_links is a unique subset of list_display.""" + from django.contrib.admin.options import ModelAdmin + + if obj.list_display_links is None: + return [] + elif not isinstance(obj.list_display_links, (list, tuple)): + return must_be( + "a list, a tuple, or None", + option="list_display_links", + obj=obj, + id="admin.E110", + ) + # Check only if ModelAdmin.get_list_display() isn't overridden. + elif obj.get_list_display.__func__ is ModelAdmin.get_list_display: + return list( + chain.from_iterable( + self._check_list_display_links_item( + obj, field_name, "list_display_links[%d]" % index + ) + for index, field_name in enumerate(obj.list_display_links) + ) + ) + return [] + + def _check_list_display_links_item(self, obj, field_name, label): + if field_name not in obj.list_display: + return [ + checks.Error( + "The value of '%s' refers to '%s', which is not defined in " + "'list_display'." % (label, field_name), + obj=obj.__class__, + id="admin.E111", + ) + ] + else: + return [] + + def _check_list_filter(self, obj): + if not isinstance(obj.list_filter, (list, tuple)): + return must_be( + "a list or tuple", option="list_filter", obj=obj, id="admin.E112" + ) + else: + return list( + chain.from_iterable( + self._check_list_filter_item(obj, item, "list_filter[%d]" % index) + for index, item in enumerate(obj.list_filter) + ) + ) + + def _check_list_filter_item(self, obj, item, label): + """ + Check one item of `list_filter`, i.e. check if it is one of three options: + 1. 'field' -- a basic field filter, possibly w/ relationships (e.g. + 'field__rel') + 2. ('field', SomeFieldListFilter) - a field-based list filter class + 3. SomeListFilter - a non-field list filter class + """ + from django.contrib.admin import FieldListFilter, ListFilter + + if callable(item) and not isinstance(item, models.Field): + # If item is option 3, it should be a ListFilter... + if not _issubclass(item, ListFilter): + return must_inherit_from( + parent="ListFilter", option=label, obj=obj, id="admin.E113" + ) + # ... but not a FieldListFilter. + elif issubclass(item, FieldListFilter): + return [ + checks.Error( + "The value of '%s' must not inherit from 'FieldListFilter'." + % label, + obj=obj.__class__, + id="admin.E114", + ) + ] + else: + return [] + elif isinstance(item, (tuple, list)): + # item is option #2 + field, list_filter_class = item + if not _issubclass(list_filter_class, FieldListFilter): + return must_inherit_from( + parent="FieldListFilter", + option="%s[1]" % label, + obj=obj, + id="admin.E115", + ) + else: + return [] + else: + # item is option #1 + field = item + + # Validate the field string + try: + get_fields_from_path(obj.model, field) + except (NotRelationField, FieldDoesNotExist): + return [ + checks.Error( + "The value of '%s' refers to '%s', which does not refer to a " + "Field." % (label, field), + obj=obj.__class__, + id="admin.E116", + ) + ] + else: + return [] + + def _check_list_select_related(self, obj): + """Check that list_select_related is a boolean, a list or a tuple.""" + + if not isinstance(obj.list_select_related, (bool, list, tuple)): + return must_be( + "a boolean, tuple or list", + option="list_select_related", + obj=obj, + id="admin.E117", + ) + else: + return [] + + def _check_list_per_page(self, obj): + """Check that list_per_page is an integer.""" + + if not isinstance(obj.list_per_page, int): + return must_be( + "an integer", option="list_per_page", obj=obj, id="admin.E118" + ) + else: + return [] + + def _check_list_max_show_all(self, obj): + """Check that list_max_show_all is an integer.""" + + if not isinstance(obj.list_max_show_all, int): + return must_be( + "an integer", option="list_max_show_all", obj=obj, id="admin.E119" + ) + else: + return [] + + def _check_list_editable(self, obj): + """Check that list_editable is a sequence of editable fields from + list_display without first element.""" + + if not isinstance(obj.list_editable, (list, tuple)): + return must_be( + "a list or tuple", option="list_editable", obj=obj, id="admin.E120" + ) + else: + return list( + chain.from_iterable( + self._check_list_editable_item( + obj, item, "list_editable[%d]" % index + ) + for index, item in enumerate(obj.list_editable) + ) + ) + + def _check_list_editable_item(self, obj, field_name, label): + try: + field = obj.model._meta.get_field(field_name) + except FieldDoesNotExist: + return refer_to_missing_field( + field=field_name, option=label, obj=obj, id="admin.E121" + ) + else: + if field_name not in obj.list_display: + return [ + checks.Error( + "The value of '%s' refers to '%s', which is not " + "contained in 'list_display'." % (label, field_name), + obj=obj.__class__, + id="admin.E122", + ) + ] + elif obj.list_display_links and field_name in obj.list_display_links: + return [ + checks.Error( + "The value of '%s' cannot be in both 'list_editable' and " + "'list_display_links'." % field_name, + obj=obj.__class__, + id="admin.E123", + ) + ] + # If list_display[0] is in list_editable, check that + # list_display_links is set. See #22792 and #26229 for use cases. + elif ( + obj.list_display[0] == field_name + and not obj.list_display_links + and obj.list_display_links is not None + ): + return [ + checks.Error( + "The value of '%s' refers to the first field in 'list_display' " + "('%s'), which cannot be used unless 'list_display_links' is " + "set." % (label, obj.list_display[0]), + obj=obj.__class__, + id="admin.E124", + ) + ] + elif not field.editable or field.primary_key: + return [ + checks.Error( + "The value of '%s' refers to '%s', which is not editable " + "through the admin." % (label, field_name), + obj=obj.__class__, + id="admin.E125", + ) + ] + else: + return [] + + def _check_search_fields(self, obj): + """Check search_fields is a sequence.""" + + if not isinstance(obj.search_fields, (list, tuple)): + return must_be( + "a list or tuple", option="search_fields", obj=obj, id="admin.E126" + ) + else: + return [] + + def _check_date_hierarchy(self, obj): + """Check that date_hierarchy refers to DateField or DateTimeField.""" + + if obj.date_hierarchy is None: + return [] + else: + try: + field = get_fields_from_path(obj.model, obj.date_hierarchy)[-1] + except (NotRelationField, FieldDoesNotExist): + return [ + checks.Error( + "The value of 'date_hierarchy' refers to '%s', which " + "does not refer to a Field." % obj.date_hierarchy, + obj=obj.__class__, + id="admin.E127", + ) + ] + else: + if not isinstance(field, (models.DateField, models.DateTimeField)): + return must_be( + "a DateField or DateTimeField", + option="date_hierarchy", + obj=obj, + id="admin.E128", + ) + else: + return [] + + def _check_action_permission_methods(self, obj): + """ + Actions with an allowed_permission attribute require the ModelAdmin to + implement a has__permission() method for each permission. + """ + actions = obj._get_base_actions() + errors = [] + for func, name, _ in actions: + if not hasattr(func, "allowed_permissions"): + continue + for permission in func.allowed_permissions: + method_name = "has_%s_permission" % permission + if not hasattr(obj, method_name): + errors.append( + checks.Error( + "%s must define a %s() method for the %s action." + % ( + obj.__class__.__name__, + method_name, + func.__name__, + ), + obj=obj.__class__, + id="admin.E129", + ) + ) + return errors + + def _check_actions_uniqueness(self, obj): + """Check that every action has a unique __name__.""" + errors = [] + names = collections.Counter(name for _, name, _ in obj._get_base_actions()) + for name, count in names.items(): + if count > 1: + errors.append( + checks.Error( + "__name__ attributes of actions defined in %s must be " + "unique. Name %r is not unique." + % ( + obj.__class__.__name__, + name, + ), + obj=obj.__class__, + id="admin.E130", + ) + ) + return errors + + +class InlineModelAdminChecks(BaseModelAdminChecks): + def check(self, inline_obj, **kwargs): + parent_model = inline_obj.parent_model + return [ + *super().check(inline_obj), + *self._check_relation(inline_obj, parent_model), + *self._check_exclude_of_parent_model(inline_obj, parent_model), + *self._check_extra(inline_obj), + *self._check_max_num(inline_obj), + *self._check_min_num(inline_obj), + *self._check_formset(inline_obj), + ] + + def _check_exclude_of_parent_model(self, obj, parent_model): + # Do not perform more specific checks if the base checks result in an + # error. + errors = super()._check_exclude(obj) + if errors: + return [] + + # Skip if `fk_name` is invalid. + if self._check_relation(obj, parent_model): + return [] + + if obj.exclude is None: + return [] + + fk = _get_foreign_key(parent_model, obj.model, fk_name=obj.fk_name) + if fk.name in obj.exclude: + return [ + checks.Error( + "Cannot exclude the field '%s', because it is the foreign key " + "to the parent model '%s'." + % ( + fk.name, + parent_model._meta.label, + ), + obj=obj.__class__, + id="admin.E201", + ) + ] + else: + return [] + + def _check_relation(self, obj, parent_model): + try: + _get_foreign_key(parent_model, obj.model, fk_name=obj.fk_name) + except ValueError as e: + return [checks.Error(e.args[0], obj=obj.__class__, id="admin.E202")] + else: + return [] + + def _check_extra(self, obj): + """Check that extra is an integer.""" + + if not isinstance(obj.extra, int): + return must_be("an integer", option="extra", obj=obj, id="admin.E203") + else: + return [] + + def _check_max_num(self, obj): + """Check that max_num is an integer.""" + + if obj.max_num is None: + return [] + elif not isinstance(obj.max_num, int): + return must_be("an integer", option="max_num", obj=obj, id="admin.E204") + else: + return [] + + def _check_min_num(self, obj): + """Check that min_num is an integer.""" + + if obj.min_num is None: + return [] + elif not isinstance(obj.min_num, int): + return must_be("an integer", option="min_num", obj=obj, id="admin.E205") + else: + return [] + + def _check_formset(self, obj): + """Check formset is a subclass of BaseModelFormSet.""" + + if not _issubclass(obj.formset, BaseModelFormSet): + return must_inherit_from( + parent="BaseModelFormSet", option="formset", obj=obj, id="admin.E206" + ) + else: + return [] + + +def must_be(type, option, obj, id): + return [ + checks.Error( + "The value of '%s' must be %s." % (option, type), + obj=obj.__class__, + id=id, + ), + ] + + +def must_inherit_from(parent, option, obj, id): + return [ + checks.Error( + "The value of '%s' must inherit from '%s'." % (option, parent), + obj=obj.__class__, + id=id, + ), + ] + + +def refer_to_missing_field(field, option, obj, id): + return [ + checks.Error( + "The value of '%s' refers to '%s', which is not a field of '%s'." + % (option, field, obj.model._meta.label), + obj=obj.__class__, + id=id, + ), + ] diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/filters 3.py b/virt/lib/python3.9/site-packages/django/contrib/admin/filters 3.py new file mode 100644 index 00000000..64cc9fad --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/filters 3.py @@ -0,0 +1,550 @@ +""" +This encapsulates the logic for displaying filters in the Django admin. +Filters are specified in models with the "list_filter" option. + +Each filter subclass knows how to display a filter for a field that passes a +certain test -- e.g. being a DateField or ForeignKey. +""" +import datetime + +from django.contrib.admin.options import IncorrectLookupParameters +from django.contrib.admin.utils import ( + get_model_from_relation, + prepare_lookup_value, + reverse_field_path, +) +from django.core.exceptions import ImproperlyConfigured, ValidationError +from django.db import models +from django.utils import timezone +from django.utils.translation import gettext_lazy as _ + + +class ListFilter: + title = None # Human-readable title to appear in the right sidebar. + template = "admin/filter.html" + + def __init__(self, request, params, model, model_admin): + # This dictionary will eventually contain the request's query string + # parameters actually used by this filter. + self.used_parameters = {} + if self.title is None: + raise ImproperlyConfigured( + "The list filter '%s' does not specify a 'title'." + % self.__class__.__name__ + ) + + def has_output(self): + """ + Return True if some choices would be output for this filter. + """ + raise NotImplementedError( + "subclasses of ListFilter must provide a has_output() method" + ) + + def choices(self, changelist): + """ + Return choices ready to be output in the template. + + `changelist` is the ChangeList to be displayed. + """ + raise NotImplementedError( + "subclasses of ListFilter must provide a choices() method" + ) + + def queryset(self, request, queryset): + """ + Return the filtered queryset. + """ + raise NotImplementedError( + "subclasses of ListFilter must provide a queryset() method" + ) + + def expected_parameters(self): + """ + Return the list of parameter names that are expected from the + request's query string and that will be used by this filter. + """ + raise NotImplementedError( + "subclasses of ListFilter must provide an expected_parameters() method" + ) + + +class SimpleListFilter(ListFilter): + # The parameter that should be used in the query string for that filter. + parameter_name = None + + def __init__(self, request, params, model, model_admin): + super().__init__(request, params, model, model_admin) + if self.parameter_name is None: + raise ImproperlyConfigured( + "The list filter '%s' does not specify a 'parameter_name'." + % self.__class__.__name__ + ) + if self.parameter_name in params: + value = params.pop(self.parameter_name) + self.used_parameters[self.parameter_name] = value + lookup_choices = self.lookups(request, model_admin) + if lookup_choices is None: + lookup_choices = () + self.lookup_choices = list(lookup_choices) + + def has_output(self): + return len(self.lookup_choices) > 0 + + def value(self): + """ + Return the value (in string format) provided in the request's + query string for this filter, if any, or None if the value wasn't + provided. + """ + return self.used_parameters.get(self.parameter_name) + + def lookups(self, request, model_admin): + """ + Must be overridden to return a list of tuples (value, verbose value) + """ + raise NotImplementedError( + "The SimpleListFilter.lookups() method must be overridden to " + "return a list of tuples (value, verbose value)." + ) + + def expected_parameters(self): + return [self.parameter_name] + + def choices(self, changelist): + yield { + "selected": self.value() is None, + "query_string": changelist.get_query_string(remove=[self.parameter_name]), + "display": _("All"), + } + for lookup, title in self.lookup_choices: + yield { + "selected": self.value() == str(lookup), + "query_string": changelist.get_query_string( + {self.parameter_name: lookup} + ), + "display": title, + } + + +class FieldListFilter(ListFilter): + _field_list_filters = [] + _take_priority_index = 0 + list_separator = "," + + def __init__(self, field, request, params, model, model_admin, field_path): + self.field = field + self.field_path = field_path + self.title = getattr(field, "verbose_name", field_path) + super().__init__(request, params, model, model_admin) + for p in self.expected_parameters(): + if p in params: + value = params.pop(p) + self.used_parameters[p] = prepare_lookup_value( + p, value, self.list_separator + ) + + def has_output(self): + return True + + def queryset(self, request, queryset): + try: + return queryset.filter(**self.used_parameters) + except (ValueError, ValidationError) as e: + # Fields may raise a ValueError or ValidationError when converting + # the parameters to the correct type. + raise IncorrectLookupParameters(e) + + @classmethod + def register(cls, test, list_filter_class, take_priority=False): + if take_priority: + # This is to allow overriding the default filters for certain types + # of fields with some custom filters. The first found in the list + # is used in priority. + cls._field_list_filters.insert( + cls._take_priority_index, (test, list_filter_class) + ) + cls._take_priority_index += 1 + else: + cls._field_list_filters.append((test, list_filter_class)) + + @classmethod + def create(cls, field, request, params, model, model_admin, field_path): + for test, list_filter_class in cls._field_list_filters: + if test(field): + return list_filter_class( + field, request, params, model, model_admin, field_path=field_path + ) + + +class RelatedFieldListFilter(FieldListFilter): + def __init__(self, field, request, params, model, model_admin, field_path): + other_model = get_model_from_relation(field) + self.lookup_kwarg = "%s__%s__exact" % (field_path, field.target_field.name) + self.lookup_kwarg_isnull = "%s__isnull" % field_path + self.lookup_val = params.get(self.lookup_kwarg) + self.lookup_val_isnull = params.get(self.lookup_kwarg_isnull) + super().__init__(field, request, params, model, model_admin, field_path) + self.lookup_choices = self.field_choices(field, request, model_admin) + if hasattr(field, "verbose_name"): + self.lookup_title = field.verbose_name + else: + self.lookup_title = other_model._meta.verbose_name + self.title = self.lookup_title + self.empty_value_display = model_admin.get_empty_value_display() + + @property + def include_empty_choice(self): + """ + Return True if a "(None)" choice should be included, which filters + out everything except empty relationships. + """ + return self.field.null or (self.field.is_relation and self.field.many_to_many) + + def has_output(self): + if self.include_empty_choice: + extra = 1 + else: + extra = 0 + return len(self.lookup_choices) + extra > 1 + + def expected_parameters(self): + return [self.lookup_kwarg, self.lookup_kwarg_isnull] + + def field_admin_ordering(self, field, request, model_admin): + """ + Return the model admin's ordering for related field, if provided. + """ + related_admin = model_admin.admin_site._registry.get(field.remote_field.model) + if related_admin is not None: + return related_admin.get_ordering(request) + return () + + def field_choices(self, field, request, model_admin): + ordering = self.field_admin_ordering(field, request, model_admin) + return field.get_choices(include_blank=False, ordering=ordering) + + def choices(self, changelist): + yield { + "selected": self.lookup_val is None and not self.lookup_val_isnull, + "query_string": changelist.get_query_string( + remove=[self.lookup_kwarg, self.lookup_kwarg_isnull] + ), + "display": _("All"), + } + for pk_val, val in self.lookup_choices: + yield { + "selected": self.lookup_val == str(pk_val), + "query_string": changelist.get_query_string( + {self.lookup_kwarg: pk_val}, [self.lookup_kwarg_isnull] + ), + "display": val, + } + if self.include_empty_choice: + yield { + "selected": bool(self.lookup_val_isnull), + "query_string": changelist.get_query_string( + {self.lookup_kwarg_isnull: "True"}, [self.lookup_kwarg] + ), + "display": self.empty_value_display, + } + + +FieldListFilter.register(lambda f: f.remote_field, RelatedFieldListFilter) + + +class BooleanFieldListFilter(FieldListFilter): + def __init__(self, field, request, params, model, model_admin, field_path): + self.lookup_kwarg = "%s__exact" % field_path + self.lookup_kwarg2 = "%s__isnull" % field_path + self.lookup_val = params.get(self.lookup_kwarg) + self.lookup_val2 = params.get(self.lookup_kwarg2) + super().__init__(field, request, params, model, model_admin, field_path) + if ( + self.used_parameters + and self.lookup_kwarg in self.used_parameters + and self.used_parameters[self.lookup_kwarg] in ("1", "0") + ): + self.used_parameters[self.lookup_kwarg] = bool( + int(self.used_parameters[self.lookup_kwarg]) + ) + + def expected_parameters(self): + return [self.lookup_kwarg, self.lookup_kwarg2] + + def choices(self, changelist): + field_choices = dict(self.field.flatchoices) + for lookup, title in ( + (None, _("All")), + ("1", field_choices.get(True, _("Yes"))), + ("0", field_choices.get(False, _("No"))), + ): + yield { + "selected": self.lookup_val == lookup and not self.lookup_val2, + "query_string": changelist.get_query_string( + {self.lookup_kwarg: lookup}, [self.lookup_kwarg2] + ), + "display": title, + } + if self.field.null: + yield { + "selected": self.lookup_val2 == "True", + "query_string": changelist.get_query_string( + {self.lookup_kwarg2: "True"}, [self.lookup_kwarg] + ), + "display": field_choices.get(None, _("Unknown")), + } + + +FieldListFilter.register( + lambda f: isinstance(f, models.BooleanField), BooleanFieldListFilter +) + + +class ChoicesFieldListFilter(FieldListFilter): + def __init__(self, field, request, params, model, model_admin, field_path): + self.lookup_kwarg = "%s__exact" % field_path + self.lookup_kwarg_isnull = "%s__isnull" % field_path + self.lookup_val = params.get(self.lookup_kwarg) + self.lookup_val_isnull = params.get(self.lookup_kwarg_isnull) + super().__init__(field, request, params, model, model_admin, field_path) + + def expected_parameters(self): + return [self.lookup_kwarg, self.lookup_kwarg_isnull] + + def choices(self, changelist): + yield { + "selected": self.lookup_val is None, + "query_string": changelist.get_query_string( + remove=[self.lookup_kwarg, self.lookup_kwarg_isnull] + ), + "display": _("All"), + } + none_title = "" + for lookup, title in self.field.flatchoices: + if lookup is None: + none_title = title + continue + yield { + "selected": str(lookup) == self.lookup_val, + "query_string": changelist.get_query_string( + {self.lookup_kwarg: lookup}, [self.lookup_kwarg_isnull] + ), + "display": title, + } + if none_title: + yield { + "selected": bool(self.lookup_val_isnull), + "query_string": changelist.get_query_string( + {self.lookup_kwarg_isnull: "True"}, [self.lookup_kwarg] + ), + "display": none_title, + } + + +FieldListFilter.register(lambda f: bool(f.choices), ChoicesFieldListFilter) + + +class DateFieldListFilter(FieldListFilter): + def __init__(self, field, request, params, model, model_admin, field_path): + self.field_generic = "%s__" % field_path + self.date_params = { + k: v for k, v in params.items() if k.startswith(self.field_generic) + } + + now = timezone.now() + # When time zone support is enabled, convert "now" to the user's time + # zone so Django's definition of "Today" matches what the user expects. + if timezone.is_aware(now): + now = timezone.localtime(now) + + if isinstance(field, models.DateTimeField): + today = now.replace(hour=0, minute=0, second=0, microsecond=0) + else: # field is a models.DateField + today = now.date() + tomorrow = today + datetime.timedelta(days=1) + if today.month == 12: + next_month = today.replace(year=today.year + 1, month=1, day=1) + else: + next_month = today.replace(month=today.month + 1, day=1) + next_year = today.replace(year=today.year + 1, month=1, day=1) + + self.lookup_kwarg_since = "%s__gte" % field_path + self.lookup_kwarg_until = "%s__lt" % field_path + self.links = ( + (_("Any date"), {}), + ( + _("Today"), + { + self.lookup_kwarg_since: str(today), + self.lookup_kwarg_until: str(tomorrow), + }, + ), + ( + _("Past 7 days"), + { + self.lookup_kwarg_since: str(today - datetime.timedelta(days=7)), + self.lookup_kwarg_until: str(tomorrow), + }, + ), + ( + _("This month"), + { + self.lookup_kwarg_since: str(today.replace(day=1)), + self.lookup_kwarg_until: str(next_month), + }, + ), + ( + _("This year"), + { + self.lookup_kwarg_since: str(today.replace(month=1, day=1)), + self.lookup_kwarg_until: str(next_year), + }, + ), + ) + if field.null: + self.lookup_kwarg_isnull = "%s__isnull" % field_path + self.links += ( + (_("No date"), {self.field_generic + "isnull": "True"}), + (_("Has date"), {self.field_generic + "isnull": "False"}), + ) + super().__init__(field, request, params, model, model_admin, field_path) + + def expected_parameters(self): + params = [self.lookup_kwarg_since, self.lookup_kwarg_until] + if self.field.null: + params.append(self.lookup_kwarg_isnull) + return params + + def choices(self, changelist): + for title, param_dict in self.links: + yield { + "selected": self.date_params == param_dict, + "query_string": changelist.get_query_string( + param_dict, [self.field_generic] + ), + "display": title, + } + + +FieldListFilter.register(lambda f: isinstance(f, models.DateField), DateFieldListFilter) + + +# This should be registered last, because it's a last resort. For example, +# if a field is eligible to use the BooleanFieldListFilter, that'd be much +# more appropriate, and the AllValuesFieldListFilter won't get used for it. +class AllValuesFieldListFilter(FieldListFilter): + def __init__(self, field, request, params, model, model_admin, field_path): + self.lookup_kwarg = field_path + self.lookup_kwarg_isnull = "%s__isnull" % field_path + self.lookup_val = params.get(self.lookup_kwarg) + self.lookup_val_isnull = params.get(self.lookup_kwarg_isnull) + self.empty_value_display = model_admin.get_empty_value_display() + parent_model, reverse_path = reverse_field_path(model, field_path) + # Obey parent ModelAdmin queryset when deciding which options to show + if model == parent_model: + queryset = model_admin.get_queryset(request) + else: + queryset = parent_model._default_manager.all() + self.lookup_choices = ( + queryset.distinct().order_by(field.name).values_list(field.name, flat=True) + ) + super().__init__(field, request, params, model, model_admin, field_path) + + def expected_parameters(self): + return [self.lookup_kwarg, self.lookup_kwarg_isnull] + + def choices(self, changelist): + yield { + "selected": self.lookup_val is None and self.lookup_val_isnull is None, + "query_string": changelist.get_query_string( + remove=[self.lookup_kwarg, self.lookup_kwarg_isnull] + ), + "display": _("All"), + } + include_none = False + for val in self.lookup_choices: + if val is None: + include_none = True + continue + val = str(val) + yield { + "selected": self.lookup_val == val, + "query_string": changelist.get_query_string( + {self.lookup_kwarg: val}, [self.lookup_kwarg_isnull] + ), + "display": val, + } + if include_none: + yield { + "selected": bool(self.lookup_val_isnull), + "query_string": changelist.get_query_string( + {self.lookup_kwarg_isnull: "True"}, [self.lookup_kwarg] + ), + "display": self.empty_value_display, + } + + +FieldListFilter.register(lambda f: True, AllValuesFieldListFilter) + + +class RelatedOnlyFieldListFilter(RelatedFieldListFilter): + def field_choices(self, field, request, model_admin): + pk_qs = ( + model_admin.get_queryset(request) + .distinct() + .values_list("%s__pk" % self.field_path, flat=True) + ) + ordering = self.field_admin_ordering(field, request, model_admin) + return field.get_choices( + include_blank=False, limit_choices_to={"pk__in": pk_qs}, ordering=ordering + ) + + +class EmptyFieldListFilter(FieldListFilter): + def __init__(self, field, request, params, model, model_admin, field_path): + if not field.empty_strings_allowed and not field.null: + raise ImproperlyConfigured( + "The list filter '%s' cannot be used with field '%s' which " + "doesn't allow empty strings and nulls." + % ( + self.__class__.__name__, + field.name, + ) + ) + self.lookup_kwarg = "%s__isempty" % field_path + self.lookup_val = params.get(self.lookup_kwarg) + super().__init__(field, request, params, model, model_admin, field_path) + + def queryset(self, request, queryset): + if self.lookup_kwarg not in self.used_parameters: + return queryset + if self.lookup_val not in ("0", "1"): + raise IncorrectLookupParameters + + lookup_conditions = [] + if self.field.empty_strings_allowed: + lookup_conditions.append((self.field_path, "")) + if self.field.null: + lookup_conditions.append((f"{self.field_path}__isnull", True)) + lookup_condition = models.Q.create(lookup_conditions, connector=models.Q.OR) + if self.lookup_val == "1": + return queryset.filter(lookup_condition) + return queryset.exclude(lookup_condition) + + def expected_parameters(self): + return [self.lookup_kwarg] + + def choices(self, changelist): + for lookup, title in ( + (None, _("All")), + ("1", _("Empty")), + ("0", _("Not empty")), + ): + yield { + "selected": self.lookup_val == lookup, + "query_string": changelist.get_query_string( + {self.lookup_kwarg: lookup} + ), + "display": title, + } diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/helpers 3.py b/virt/lib/python3.9/site-packages/django/contrib/admin/helpers 3.py new file mode 100644 index 00000000..e7e831a5 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/helpers 3.py @@ -0,0 +1,555 @@ +import json + +from django import forms +from django.contrib.admin.utils import ( + display_for_field, + flatten_fieldsets, + help_text_for_field, + label_for_field, + lookup_field, + quote, +) +from django.core.exceptions import ObjectDoesNotExist +from django.db.models.fields.related import ( + ForeignObjectRel, + ManyToManyRel, + OneToOneField, +) +from django.forms.utils import flatatt +from django.template.defaultfilters import capfirst, linebreaksbr +from django.urls import NoReverseMatch, reverse +from django.utils.html import conditional_escape, format_html +from django.utils.safestring import mark_safe +from django.utils.translation import gettext +from django.utils.translation import gettext_lazy as _ + +ACTION_CHECKBOX_NAME = "_selected_action" + + +class ActionForm(forms.Form): + action = forms.ChoiceField(label=_("Action:")) + select_across = forms.BooleanField( + label="", + required=False, + initial=0, + widget=forms.HiddenInput({"class": "select-across"}), + ) + + +checkbox = forms.CheckboxInput({"class": "action-select"}, lambda value: False) + + +class AdminForm: + def __init__( + self, + form, + fieldsets, + prepopulated_fields, + readonly_fields=None, + model_admin=None, + ): + self.form, self.fieldsets = form, fieldsets + self.prepopulated_fields = [ + {"field": form[field_name], "dependencies": [form[f] for f in dependencies]} + for field_name, dependencies in prepopulated_fields.items() + ] + self.model_admin = model_admin + if readonly_fields is None: + readonly_fields = () + self.readonly_fields = readonly_fields + + def __repr__(self): + return ( + f"<{self.__class__.__qualname__}: " + f"form={self.form.__class__.__qualname__} " + f"fieldsets={self.fieldsets!r}>" + ) + + def __iter__(self): + for name, options in self.fieldsets: + yield Fieldset( + self.form, + name, + readonly_fields=self.readonly_fields, + model_admin=self.model_admin, + **options, + ) + + @property + def errors(self): + return self.form.errors + + @property + def non_field_errors(self): + return self.form.non_field_errors + + @property + def fields(self): + return self.form.fields + + @property + def is_bound(self): + return self.form.is_bound + + @property + def media(self): + media = self.form.media + for fs in self: + media += fs.media + return media + + +class Fieldset: + def __init__( + self, + form, + name=None, + readonly_fields=(), + fields=(), + classes=(), + description=None, + model_admin=None, + ): + self.form = form + self.name, self.fields = name, fields + self.classes = " ".join(classes) + self.description = description + self.model_admin = model_admin + self.readonly_fields = readonly_fields + + @property + def media(self): + if "collapse" in self.classes: + return forms.Media(js=["admin/js/collapse.js"]) + return forms.Media() + + def __iter__(self): + for field in self.fields: + yield Fieldline( + self.form, field, self.readonly_fields, model_admin=self.model_admin + ) + + +class Fieldline: + def __init__(self, form, field, readonly_fields=None, model_admin=None): + self.form = form # A django.forms.Form instance + if not hasattr(field, "__iter__") or isinstance(field, str): + self.fields = [field] + else: + self.fields = field + self.has_visible_field = not all( + field in self.form.fields and self.form.fields[field].widget.is_hidden + for field in self.fields + ) + self.model_admin = model_admin + if readonly_fields is None: + readonly_fields = () + self.readonly_fields = readonly_fields + + def __iter__(self): + for i, field in enumerate(self.fields): + if field in self.readonly_fields: + yield AdminReadonlyField( + self.form, field, is_first=(i == 0), model_admin=self.model_admin + ) + else: + yield AdminField(self.form, field, is_first=(i == 0)) + + def errors(self): + return mark_safe( + "\n".join( + self.form[f].errors.as_ul() + for f in self.fields + if f not in self.readonly_fields + ).strip("\n") + ) + + +class AdminField: + def __init__(self, form, field, is_first): + self.field = form[field] # A django.forms.BoundField instance + self.is_first = is_first # Whether this field is first on the line + self.is_checkbox = isinstance(self.field.field.widget, forms.CheckboxInput) + self.is_readonly = False + + def label_tag(self): + classes = [] + contents = conditional_escape(self.field.label) + if self.is_checkbox: + classes.append("vCheckboxLabel") + + if self.field.field.required: + classes.append("required") + if not self.is_first: + classes.append("inline") + attrs = {"class": " ".join(classes)} if classes else {} + # checkboxes should not have a label suffix as the checkbox appears + # to the left of the label. + return self.field.label_tag( + contents=mark_safe(contents), + attrs=attrs, + label_suffix="" if self.is_checkbox else None, + ) + + def errors(self): + return mark_safe(self.field.errors.as_ul()) + + +class AdminReadonlyField: + def __init__(self, form, field, is_first, model_admin=None): + # Make self.field look a little bit like a field. This means that + # {{ field.name }} must be a useful class name to identify the field. + # For convenience, store other field-related data here too. + if callable(field): + class_name = field.__name__ if field.__name__ != "" else "" + else: + class_name = field + + if form._meta.labels and class_name in form._meta.labels: + label = form._meta.labels[class_name] + else: + label = label_for_field(field, form._meta.model, model_admin, form=form) + + if form._meta.help_texts and class_name in form._meta.help_texts: + help_text = form._meta.help_texts[class_name] + else: + help_text = help_text_for_field(class_name, form._meta.model) + + if field in form.fields: + is_hidden = form.fields[field].widget.is_hidden + else: + is_hidden = False + + self.field = { + "name": class_name, + "label": label, + "help_text": help_text, + "field": field, + "is_hidden": is_hidden, + } + self.form = form + self.model_admin = model_admin + self.is_first = is_first + self.is_checkbox = False + self.is_readonly = True + self.empty_value_display = model_admin.get_empty_value_display() + + def label_tag(self): + attrs = {} + if not self.is_first: + attrs["class"] = "inline" + label = self.field["label"] + return format_html( + "{}{}", + flatatt(attrs), + capfirst(label), + self.form.label_suffix, + ) + + def get_admin_url(self, remote_field, remote_obj): + url_name = "admin:%s_%s_change" % ( + remote_field.model._meta.app_label, + remote_field.model._meta.model_name, + ) + try: + url = reverse( + url_name, + args=[quote(remote_obj.pk)], + current_app=self.model_admin.admin_site.name, + ) + return format_html('{}', url, remote_obj) + except NoReverseMatch: + return str(remote_obj) + + def contents(self): + from django.contrib.admin.templatetags.admin_list import _boolean_icon + + field, obj, model_admin = ( + self.field["field"], + self.form.instance, + self.model_admin, + ) + try: + f, attr, value = lookup_field(field, obj, model_admin) + except (AttributeError, ValueError, ObjectDoesNotExist): + result_repr = self.empty_value_display + else: + if field in self.form.fields: + widget = self.form[field].field.widget + # This isn't elegant but suffices for contrib.auth's + # ReadOnlyPasswordHashWidget. + if getattr(widget, "read_only", False): + return widget.render(field, value) + if f is None: + if getattr(attr, "boolean", False): + result_repr = _boolean_icon(value) + else: + if hasattr(value, "__html__"): + result_repr = value + else: + result_repr = linebreaksbr(value) + else: + if isinstance(f.remote_field, ManyToManyRel) and value is not None: + result_repr = ", ".join(map(str, value.all())) + elif ( + isinstance(f.remote_field, (ForeignObjectRel, OneToOneField)) + and value is not None + ): + result_repr = self.get_admin_url(f.remote_field, value) + else: + result_repr = display_for_field(value, f, self.empty_value_display) + result_repr = linebreaksbr(result_repr) + return conditional_escape(result_repr) + + +class InlineAdminFormSet: + """ + A wrapper around an inline formset for use in the admin system. + """ + + def __init__( + self, + inline, + formset, + fieldsets, + prepopulated_fields=None, + readonly_fields=None, + model_admin=None, + has_add_permission=True, + has_change_permission=True, + has_delete_permission=True, + has_view_permission=True, + ): + self.opts = inline + self.formset = formset + self.fieldsets = fieldsets + self.model_admin = model_admin + if readonly_fields is None: + readonly_fields = () + self.readonly_fields = readonly_fields + if prepopulated_fields is None: + prepopulated_fields = {} + self.prepopulated_fields = prepopulated_fields + self.classes = " ".join(inline.classes) if inline.classes else "" + self.has_add_permission = has_add_permission + self.has_change_permission = has_change_permission + self.has_delete_permission = has_delete_permission + self.has_view_permission = has_view_permission + + def __iter__(self): + if self.has_change_permission: + readonly_fields_for_editing = self.readonly_fields + else: + readonly_fields_for_editing = self.readonly_fields + flatten_fieldsets( + self.fieldsets + ) + + for form, original in zip( + self.formset.initial_forms, self.formset.get_queryset() + ): + view_on_site_url = self.opts.get_view_on_site_url(original) + yield InlineAdminForm( + self.formset, + form, + self.fieldsets, + self.prepopulated_fields, + original, + readonly_fields_for_editing, + model_admin=self.opts, + view_on_site_url=view_on_site_url, + ) + for form in self.formset.extra_forms: + yield InlineAdminForm( + self.formset, + form, + self.fieldsets, + self.prepopulated_fields, + None, + self.readonly_fields, + model_admin=self.opts, + ) + if self.has_add_permission: + yield InlineAdminForm( + self.formset, + self.formset.empty_form, + self.fieldsets, + self.prepopulated_fields, + None, + self.readonly_fields, + model_admin=self.opts, + ) + + def fields(self): + fk = getattr(self.formset, "fk", None) + empty_form = self.formset.empty_form + meta_labels = empty_form._meta.labels or {} + meta_help_texts = empty_form._meta.help_texts or {} + for i, field_name in enumerate(flatten_fieldsets(self.fieldsets)): + if fk and fk.name == field_name: + continue + if not self.has_change_permission or field_name in self.readonly_fields: + form_field = empty_form.fields.get(field_name) + widget_is_hidden = False + if form_field is not None: + widget_is_hidden = form_field.widget.is_hidden + yield { + "name": field_name, + "label": meta_labels.get(field_name) + or label_for_field( + field_name, + self.opts.model, + self.opts, + form=empty_form, + ), + "widget": {"is_hidden": widget_is_hidden}, + "required": False, + "help_text": meta_help_texts.get(field_name) + or help_text_for_field(field_name, self.opts.model), + } + else: + form_field = empty_form.fields[field_name] + label = form_field.label + if label is None: + label = label_for_field( + field_name, self.opts.model, self.opts, form=empty_form + ) + yield { + "name": field_name, + "label": label, + "widget": form_field.widget, + "required": form_field.required, + "help_text": form_field.help_text, + } + + def inline_formset_data(self): + verbose_name = self.opts.verbose_name + return json.dumps( + { + "name": "#%s" % self.formset.prefix, + "options": { + "prefix": self.formset.prefix, + "addText": gettext("Add another %(verbose_name)s") + % { + "verbose_name": capfirst(verbose_name), + }, + "deleteText": gettext("Remove"), + }, + } + ) + + @property + def forms(self): + return self.formset.forms + + def non_form_errors(self): + return self.formset.non_form_errors() + + @property + def is_bound(self): + return self.formset.is_bound + + @property + def total_form_count(self): + return self.formset.total_form_count + + @property + def media(self): + media = self.opts.media + self.formset.media + for fs in self: + media += fs.media + return media + + +class InlineAdminForm(AdminForm): + """ + A wrapper around an inline form for use in the admin system. + """ + + def __init__( + self, + formset, + form, + fieldsets, + prepopulated_fields, + original, + readonly_fields=None, + model_admin=None, + view_on_site_url=None, + ): + self.formset = formset + self.model_admin = model_admin + self.original = original + self.show_url = original and view_on_site_url is not None + self.absolute_url = view_on_site_url + super().__init__( + form, fieldsets, prepopulated_fields, readonly_fields, model_admin + ) + + def __iter__(self): + for name, options in self.fieldsets: + yield InlineFieldset( + self.formset, + self.form, + name, + self.readonly_fields, + model_admin=self.model_admin, + **options, + ) + + def needs_explicit_pk_field(self): + return ( + # Auto fields are editable, so check for auto or non-editable pk. + self.form._meta.model._meta.auto_field + or not self.form._meta.model._meta.pk.editable + or + # Also search any parents for an auto field. (The pk info is + # propagated to child models so that does not need to be checked + # in parents.) + any( + parent._meta.auto_field or not parent._meta.model._meta.pk.editable + for parent in self.form._meta.model._meta.get_parent_list() + ) + ) + + def pk_field(self): + return AdminField(self.form, self.formset._pk_field.name, False) + + def fk_field(self): + fk = getattr(self.formset, "fk", None) + if fk: + return AdminField(self.form, fk.name, False) + else: + return "" + + def deletion_field(self): + from django.forms.formsets import DELETION_FIELD_NAME + + return AdminField(self.form, DELETION_FIELD_NAME, False) + + +class InlineFieldset(Fieldset): + def __init__(self, formset, *args, **kwargs): + self.formset = formset + super().__init__(*args, **kwargs) + + def __iter__(self): + fk = getattr(self.formset, "fk", None) + for field in self.fields: + if not fk or fk.name != field: + yield Fieldline( + self.form, field, self.readonly_fields, model_admin=self.model_admin + ) + + +class AdminErrorList(forms.utils.ErrorList): + """Store errors for the form/formsets in an add/change view.""" + + def __init__(self, form, inline_formsets): + super().__init__() + + if form.is_bound: + self.extend(form.errors.values()) + for inline_formset in inline_formsets: + self.extend(inline_formset.non_form_errors()) + for errors_in_inline_form in inline_formset.errors: + self.extend(errors_in_inline_form.values()) diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/af/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/af/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..410d3cdd --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/af/LC_MESSAGES/django 3.po @@ -0,0 +1,720 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Christopher Penkin, 2012 +# Christopher Penkin, 2012 +# F Wolff , 2019-2020 +# Pi Delport , 2012 +# Pi Delport , 2012 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-07-14 19:53+0200\n" +"PO-Revision-Date: 2020-07-20 17:06+0000\n" +"Last-Translator: F Wolff \n" +"Language-Team: Afrikaans (http://www.transifex.com/django/django/language/" +"af/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: af\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Het %(count)d %(items)s suksesvol geskrap." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Kan %(name)s nie skrap nie" + +msgid "Are you sure?" +msgstr "Is u seker?" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Skrap gekose %(verbose_name_plural)s" + +msgid "Administration" +msgstr "Administrasie" + +msgid "All" +msgstr "Almal" + +msgid "Yes" +msgstr "Ja" + +msgid "No" +msgstr "Nee" + +msgid "Unknown" +msgstr "Onbekend" + +msgid "Any date" +msgstr "Enige datum" + +msgid "Today" +msgstr "Vandag" + +msgid "Past 7 days" +msgstr "Vorige 7 dae" + +msgid "This month" +msgstr "Hierdie maand" + +msgid "This year" +msgstr "Hierdie jaar" + +msgid "No date" +msgstr "Geen datum" + +msgid "Has date" +msgstr "Het datum" + +msgid "Empty" +msgstr "Leeg" + +msgid "Not empty" +msgstr "Nie leeg nie" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Gee die korrekte %(username)s en wagwoord vir ’n personeelrekening. Let op " +"dat altwee velde dalk hooflettersensitief is." + +msgid "Action:" +msgstr "Aksie:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Voeg nog ’n %(verbose_name)s by" + +msgid "Remove" +msgstr "Verwyder" + +msgid "Addition" +msgstr "Byvoeging" + +msgid "Change" +msgstr "" + +msgid "Deletion" +msgstr "Verwydering" + +msgid "action time" +msgstr "aksietyd" + +msgid "user" +msgstr "gebruiker" + +msgid "content type" +msgstr "inhoudtipe" + +msgid "object id" +msgstr "objek-ID" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "objek-repr" + +msgid "action flag" +msgstr "aksievlag" + +msgid "change message" +msgstr "veranderingboodskap" + +msgid "log entry" +msgstr "log-inskrywing" + +msgid "log entries" +msgstr "log-inskrywingings" + +#, python-format +msgid "Added “%(object)s”." +msgstr "Het “%(object)s” bygevoeg." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "Het “%(object)s” gewysig — %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "Het “%(object)s” geskrap." + +msgid "LogEntry Object" +msgstr "LogEntry-objek" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "Het {name} “{object}” bygevoeg." + +msgid "Added." +msgstr "Bygevoeg." + +msgid "and" +msgstr "en" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "Het {fields} vir {name} “{object}” bygevoeg." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "Het {fields} verander." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "Het {name} “{object}” geskrap." + +msgid "No fields changed." +msgstr "Geen velde het verander nie." + +msgid "None" +msgstr "Geen" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "Hou “Control” in (of “Command” op ’n Mac) om meer as een te kies." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "Die {name} “{obj}” is suksesvol bygevoeg." + +msgid "You may edit it again below." +msgstr "Dit kan weer hieronder gewysig word." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"Die {name} “{obj}” is suksesvol bygevoeg. Voeg gerus nog ’n {name} onder by." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" +"Die {name} “{obj}” is suksesvol gewysig. Redigeer dit gerus weer onder." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" +"Die {name} “{obj}” is suksesvol bygevoeg. Redigeer dit gerus weer onder." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" +"Die {name} “{obj}” is suksesvol bygevoeg. Voeg gerus nog ’n {name} onder by." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "Die {name} “{obj}” is suksesvol gewysig." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Items moet gekies word om aksies op hulle uit te voer. Geen items is " +"verander nie." + +msgid "No action selected." +msgstr "Geen aksie gekies nie." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "Die %(name)s “%(obj)s” is suksesvol geskrap." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "%(name)s met ID “%(key)s” bestaan nie. Is dit dalk geskrap?" + +#, python-format +msgid "Add %s" +msgstr "Voeg %s by" + +#, python-format +msgid "Change %s" +msgstr "Wysig %s" + +#, python-format +msgid "View %s" +msgstr "Beskou %s" + +msgid "Database error" +msgstr "Databasisfout" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s is suksesvol verander." +msgstr[1] "%(count)s %(name)s is suksesvol verander." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s gekies" +msgstr[1] "Al %(total_count)s gekies" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 uit %(cnt)s gekies" + +#, python-format +msgid "Change history: %s" +msgstr "Verander geskiedenis: %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Om %(class_name)s %(instance)s te skrap sal vereis dat die volgende " +"beskermde verwante objekte geskrap word: %(related_objects)s" + +msgid "Django site admin" +msgstr "Django-werfadmin" + +msgid "Django administration" +msgstr "Django-administrasie" + +msgid "Site administration" +msgstr "Werfadministrasie" + +msgid "Log in" +msgstr "Meld aan" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s-administrasie" + +msgid "Page not found" +msgstr "Bladsy nie gevind nie" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Jammer! Die aangevraagde bladsy kon nie gevind word nie." + +msgid "Home" +msgstr "Tuis" + +msgid "Server error" +msgstr "Bedienerfout" + +msgid "Server error (500)" +msgstr "Bedienerfout (500)" + +msgid "Server Error (500)" +msgstr "Bedienerfout (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"’n Fout het voorgekom Dit is via e-pos aan die werfadministrateurs " +"gerapporteer en behoort binnekort reggestel te word. Dankie vir u geduld." + +msgid "Run the selected action" +msgstr "Voer die gekose aksie uit" + +msgid "Go" +msgstr "Gaan" + +msgid "Click here to select the objects across all pages" +msgstr "Kliek hier om die objekte oor alle bladsye te kies." + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Kies al %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "Verwyder keuses" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Modelle in die %(name)s-toepassing" + +msgid "Add" +msgstr "Voeg by" + +msgid "View" +msgstr "Bekyk" + +msgid "You don’t have permission to view or edit anything." +msgstr "" + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Gee eerstens ’n gebruikernaam en wagwoord. Daarna kan meer gebruikervelde " +"geredigeer word." + +msgid "Enter a username and password." +msgstr "Vul ’n gebruikersnaam en wagwoord in." + +msgid "Change password" +msgstr "Verander wagwoord" + +msgid "Please correct the error below." +msgstr "Maak die onderstaande fout asb. reg." + +msgid "Please correct the errors below." +msgstr "Maak die onderstaande foute asb. reg." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "Vul ’n nuwe wagwoord vir gebruiker %(username)s in." + +msgid "Welcome," +msgstr "Welkom," + +msgid "View site" +msgstr "Besoek werf" + +msgid "Documentation" +msgstr "Dokumentasie" + +msgid "Log out" +msgstr "Meld af" + +#, python-format +msgid "Add %(name)s" +msgstr "Voeg %(name)s by" + +msgid "History" +msgstr "Geskiedenis" + +msgid "View on site" +msgstr "Bekyk op werf" + +msgid "Filter" +msgstr "Filtreer" + +msgid "Clear all filters" +msgstr "" + +msgid "Remove from sorting" +msgstr "Verwyder uit sortering" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Sorteerprioriteit: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Wissel sortering" + +msgid "Delete" +msgstr "Skrap" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Om die %(object_name)s %(escaped_object)s te skrap sou verwante objekte " +"skrap, maar jou rekening het nie toestemming om die volgende tipes objekte " +"te skrap nie:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Om die %(object_name)s “%(escaped_object)s” te skrap vereis dat die volgende " +"beskermde verwante objekte geskrap word:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Wil u definitief die %(object_name)s “%(escaped_object)s” skrap? Al die " +"volgende verwante items sal geskrap word:" + +msgid "Objects" +msgstr "Objekte" + +msgid "Yes, I’m sure" +msgstr "Ja, ek is seker" + +msgid "No, take me back" +msgstr "Nee, ek wil teruggaan" + +msgid "Delete multiple objects" +msgstr "Skrap meerdere objekte" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Om die gekose %(objects_name)s te skrap sou verwante objekte skrap, maar u " +"rekening het nie toestemming om die volgende tipes objekte te skrap nie:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Om die gekose %(objects_name)s te skrap vereis dat die volgende beskermde " +"verwante objekte geskrap word:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Wil u definitief die gekose %(objects_name)s skrap? Al die volgende objekte " +"en hul verwante items sal geskrap word:" + +msgid "Delete?" +msgstr "Skrap?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " Volgens %(filter_title)s " + +msgid "Summary" +msgstr "Opsomming" + +msgid "Recent actions" +msgstr "Onlangse aksies" + +msgid "My actions" +msgstr "My aksies" + +msgid "None available" +msgstr "Niks beskikbaar nie" + +msgid "Unknown content" +msgstr "Onbekende inhoud" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Iets is fout met die databasisinstallasie. Maak seker die gepaste " +"databasistabelle is geskep en maak seker die databasis is leesbaar deur die " +"gepaste gebruiker." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"U is aangemeld as %(username)s, maar het nie toegang tot hierdie bladsy nie. " +"Wil u met ’n ander rekening aanmeld?" + +msgid "Forgotten your password or username?" +msgstr "Wagwoord of gebruikersnaam vergeet?" + +msgid "Toggle navigation" +msgstr "" + +msgid "Date/time" +msgstr "Datum/tyd" + +msgid "User" +msgstr "Gebruiker" + +msgid "Action" +msgstr "Aksie" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"Dié objek het nie 'n wysigingsgeskiedenis. Dit is waarskynlik nie deur dié " +"adminwerf bygevoeg nie." + +msgid "Show all" +msgstr "Wys almal" + +msgid "Save" +msgstr "Stoor" + +msgid "Popup closing…" +msgstr "Opspringer sluit tans…" + +msgid "Search" +msgstr "Soek" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s resultaat" +msgstr[1] "%(counter)s resultate" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s in totaal" + +msgid "Save as new" +msgstr "Stoor as nuwe" + +msgid "Save and add another" +msgstr "Stoor en voeg ’n ander by" + +msgid "Save and continue editing" +msgstr "Stoor en wysig verder" + +msgid "Save and view" +msgstr "Stoor en bekyk" + +msgid "Close" +msgstr "Sluit" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Wysig gekose %(model)s" + +#, python-format +msgid "Add another %(model)s" +msgstr "Voeg nog ’n %(model)s by" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Skrap gekose %(model)s" + +msgid "Thanks for spending some quality time with the Web site today." +msgstr "" +"Dankie vir die kwaliteittyd wat u met die webwerf deurgebring het vandag." + +msgid "Log in again" +msgstr "Meld weer aan" + +msgid "Password change" +msgstr "Wagwoordverandering" + +msgid "Your password was changed." +msgstr "Die wagwoord is verander." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Gee asb. die ou wagwoord t.w.v. sekuriteit, en gee dan die nuwe wagwoord " +"twee keer sodat ons kan verifieer dat dit korrek getik is." + +msgid "Change my password" +msgstr "Verander my wagwoord" + +msgid "Password reset" +msgstr "Wagwoordherstel" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Jou wagwoord is gestel. Jy kan nou voortgaan en aanmeld." + +msgid "Password reset confirmation" +msgstr "Bevestig wagwoordherstel" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Tik die nuwe wagwoord twee keer in so ons kan seker wees dat dit korrek " +"ingetik is." + +msgid "New password:" +msgstr "Nuwe wagwoord:" + +msgid "Confirm password:" +msgstr "Bevestig wagwoord:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Die skakel vir wagwoordherstel was ongeldig, dalk omdat dit reeds gebruik " +"is. Vra gerus ’n nuwe een aan." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Ons het instruksies gestuur om ’n wagwoord in te stel as ’n rekening bestaan " +"met die gegewe e-posadres. Dit behoort binnekort afgelewer te word." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"As u geen e-pos ontvang nie, kontroleer dat die e-posadres waarmee " +"geregistreer is, gegee is, en kontroleer die gemorspos." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"U ontvang hierdie e-pos omdat u ’n wagwoordherstel vir u rekening by " +"%(site_name)s aangevra het." + +msgid "Please go to the following page and choose a new password:" +msgstr "Gaan asseblief na die volgende bladsy en kies ’n nuwe wagwoord:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "U gebruikernaam vir ingeval u vergeet het:" + +msgid "Thanks for using our site!" +msgstr "Dankie vir die gebruik van ons webwerf!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "Die %(site_name)s span" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Die wagwoord vergeet? Tik u e-posadres hieronder en ons sal instruksies vir " +"die instel van ’n nuwe wagwoord stuur." + +msgid "Email address:" +msgstr "E-posadres:" + +msgid "Reset my password" +msgstr "Herstel my wagwoord" + +msgid "All dates" +msgstr "Alle datums" + +#, python-format +msgid "Select %s" +msgstr "Kies %s" + +#, python-format +msgid "Select %s to change" +msgstr "Kies %s om te verander" + +#, python-format +msgid "Select %s to view" +msgstr "Kies %s om te bekyk" + +msgid "Date:" +msgstr "Datum:" + +msgid "Time:" +msgstr "Tyd:" + +msgid "Lookup" +msgstr "Soek" + +msgid "Currently:" +msgstr "Tans:" + +msgid "Change:" +msgstr "Wysig:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ar_DZ/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ar_DZ/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..2fb9d470 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ar_DZ/LC_MESSAGES/django 3.po @@ -0,0 +1,738 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Jihad Bahmaid Al-Halki, 2022 +# Riterix , 2019-2020 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:10-0500\n" +"PO-Revision-Date: 2022-07-25 07:05+0000\n" +"Last-Translator: Jihad Bahmaid Al-Halki\n" +"Language-Team: Arabic (Algeria) (http://www.transifex.com/django/django/" +"language/ar_DZ/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ar_DZ\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " +"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "حذف سجلات %(verbose_name_plural)s المحددة" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "تم حذف %(count)d %(items)s بنجاح." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "لا يمكن حذف %(name)s" + +msgid "Are you sure?" +msgstr "هل أنت متأكد؟" + +msgid "Administration" +msgstr "الإدارة" + +msgid "All" +msgstr "الكل" + +msgid "Yes" +msgstr "نعم" + +msgid "No" +msgstr "لا" + +msgid "Unknown" +msgstr "مجهول" + +msgid "Any date" +msgstr "أي تاريخ" + +msgid "Today" +msgstr "اليوم" + +msgid "Past 7 days" +msgstr "الأيام السبعة الماضية" + +msgid "This month" +msgstr "هذا الشهر" + +msgid "This year" +msgstr "هذه السنة" + +msgid "No date" +msgstr "لا يوجد أي تاريخ" + +msgid "Has date" +msgstr "به تاريخ" + +msgid "Empty" +msgstr "فارغة" + +msgid "Not empty" +msgstr "ليست فارغة" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"الرجاء إدخال ال%(username)s و كلمة المرور الصحيحين لحساب الطاقم. الحقلين " +"حساسين وضعية الاحرف." + +msgid "Action:" +msgstr "إجراء:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "إضافة سجل %(verbose_name)s آخر" + +msgid "Remove" +msgstr "أزل" + +msgid "Addition" +msgstr "إضافة" + +msgid "Change" +msgstr "عدّل" + +msgid "Deletion" +msgstr "حذف" + +msgid "action time" +msgstr "وقت الإجراء" + +msgid "user" +msgstr "المستخدم" + +msgid "content type" +msgstr "نوع المحتوى" + +msgid "object id" +msgstr "معرف العنصر" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "ممثل العنصر" + +msgid "action flag" +msgstr "علامة الإجراء" + +msgid "change message" +msgstr "غيّر الرسالة" + +msgid "log entry" +msgstr "مُدخل السجل" + +msgid "log entries" +msgstr "مُدخلات السجل" + +#, python-format +msgid "Added “%(object)s”." +msgstr "تم إضافة العناصر \\\"%(object)s\\\"." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "تم تعديل العناصر \\\"%(object)s\\\" - %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "تم حذف العناصر \\\"%(object)s.\\\"" + +msgid "LogEntry Object" +msgstr "كائن LogEntry" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "تم إضافة {name} \\\"{object}\\\"." + +msgid "Added." +msgstr "تمت الإضافة." + +msgid "and" +msgstr "و" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "تم تغيير {fields} لـ {name} \\\"{object}\\\"." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "تم تغيير {fields}." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "تم حذف {name} \\\"{object}\\\"." + +msgid "No fields changed." +msgstr "لم يتم تغيير أية حقول." + +msgid "None" +msgstr "لاشيء" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" +"استمر بالضغط على مفتاح \\\"Control\\\", او \\\"Command\\\" على أجهزة الماك, " +"لإختيار أكثر من أختيار واحد." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "تمت إضافة {name} \\\"{obj}\\\" بنجاح." + +msgid "You may edit it again below." +msgstr "يمكن تعديله مرة أخرى أدناه." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "تمت إضافة {name} \\\"{obj}\\\" بنجاح. يمكنك إضافة {name} آخر أدناه." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "تم تغيير {name} \\\"{obj}\\\" بنجاح. يمكنك تعديله مرة أخرى أدناه." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "تمت إضافة {name} \\\"{obj}\\\" بنجاح. يمكنك تعديله مرة أخرى أدناه." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "تم تغيير {name} \\\"{obj}\\\" بنجاح. يمكنك إضافة {name} آخر أدناه." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "تم تغيير {name} \\\"{obj}\\\" بنجاح." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "يجب تحديد العناصر لتطبيق الإجراءات عليها. لم يتم تغيير أية عناصر." + +msgid "No action selected." +msgstr "لم يحدد أي إجراء." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "تم حذف %(name)s \\\"%(obj)s\\\" بنجاح." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "%(name)s ب ID \\\"%(key)s\\\" غير موجود. ربما تم حذفه؟" + +#, python-format +msgid "Add %s" +msgstr "أضف %s" + +#, python-format +msgid "Change %s" +msgstr "عدّل %s" + +#, python-format +msgid "View %s" +msgstr "عرض %s" + +msgid "Database error" +msgstr "خطـأ في قاعدة البيانات" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "تم تغيير %(count)s %(name)s بنجاح." +msgstr[1] "تم تغيير %(count)s %(name)s بنجاح." +msgstr[2] "تم تغيير %(count)s %(name)s بنجاح." +msgstr[3] "تم تغيير %(count)s %(name)s بنجاح." +msgstr[4] "تم تغيير %(count)s %(name)s بنجاح." +msgstr[5] "تم تغيير %(count)s %(name)s بنجاح." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "تم تحديد %(total_count)s" +msgstr[1] "تم تحديد %(total_count)s" +msgstr[2] "تم تحديد %(total_count)s" +msgstr[3] "تم تحديد %(total_count)s" +msgstr[4] "تم تحديد %(total_count)s" +msgstr[5] "تم تحديد %(total_count)s" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "لا شيء محدد من %(cnt)s" + +#, python-format +msgid "Change history: %s" +msgstr "تاريخ التغيير: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"حذف %(class_name)s %(instance)s سيتسبب أيضاً بحذف العناصر المرتبطة التالية: " +"%(related_objects)s" + +msgid "Django site admin" +msgstr "إدارة موقع جانغو" + +msgid "Django administration" +msgstr "إدارة جانغو" + +msgid "Site administration" +msgstr "إدارة الموقع" + +msgid "Log in" +msgstr "ادخل" + +#, python-format +msgid "%(app)s administration" +msgstr "إدارة %(app)s " + +msgid "Page not found" +msgstr "تعذر العثور على الصفحة" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "نحن آسفون، لكننا لم نعثر على الصفحة المطلوبة.\"" + +msgid "Home" +msgstr "الرئيسية" + +msgid "Server error" +msgstr "خطأ في المزود" + +msgid "Server error (500)" +msgstr "خطأ في المزود (500)" + +msgid "Server Error (500)" +msgstr "خطأ في المزود (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"كان هناك خطأ. تم إعلام المسؤولين عن الموقع عبر البريد الإلكتروني وسوف يتم " +"إصلاح الخطأ قريباً. شكراً على صبركم." + +msgid "Run the selected action" +msgstr "نفذ الإجراء المحدّد" + +msgid "Go" +msgstr "نفّذ" + +msgid "Click here to select the objects across all pages" +msgstr "اضغط هنا لتحديد جميع العناصر في جميع الصفحات" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "اختيار %(total_count)s %(module_name)s جميعها" + +msgid "Clear selection" +msgstr "إزالة الاختيار" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "النماذج في تطبيق %(name)s" + +msgid "Add" +msgstr "أضف" + +msgid "View" +msgstr "عرض" + +msgid "You don’t have permission to view or edit anything." +msgstr "ليس لديك الصلاحية لعرض أو تعديل أي شيء." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"أولاً، أدخل اسم مستخدم وكلمة مرور. ومن ثم تستطيع تعديل المزيد من خيارات " +"المستخدم." + +msgid "Enter a username and password." +msgstr "أدخل اسم مستخدم وكلمة مرور." + +msgid "Change password" +msgstr "غيّر كلمة المرور" + +msgid "Please correct the error below." +msgstr "يرجى تصحيح الخطأ أدناه." + +msgid "Please correct the errors below." +msgstr "الرجاء تصحيح الأخطاء أدناه." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "أدخل كلمة مرور جديدة للمستخدم %(username)s." + +msgid "Welcome," +msgstr "أهلا، " + +msgid "View site" +msgstr "عرض الموقع" + +msgid "Documentation" +msgstr "الوثائق" + +msgid "Log out" +msgstr "اخرج" + +#, python-format +msgid "Add %(name)s" +msgstr "أضف %(name)s" + +msgid "History" +msgstr "تاريخ" + +msgid "View on site" +msgstr "مشاهدة على الموقع" + +msgid "Filter" +msgstr "مرشّح" + +msgid "Clear all filters" +msgstr "مسح جميع المرشحات" + +msgid "Remove from sorting" +msgstr "إزالة من الترتيب" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "أولوية الترتيب: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "عكس الترتيب" + +msgid "Delete" +msgstr "احذف" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"حذف العنصر %(object_name)s '%(escaped_object)s' سيتسبب بحذف العناصر المرتبطة " +"به، إلا أنك لا تملك صلاحية حذف العناصر التالية:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"حذف %(object_name)s '%(escaped_object)s' سيتسبب أيضاً بحذف العناصر المرتبطة، " +"إلا أن حسابك ليس لديه صلاحية حذف أنواع العناصر التالية:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"متأكد أنك تريد حذف العنصر %(object_name)s \\\"%(escaped_object)s\\\"؟ سيتم " +"حذف جميع العناصر التالية المرتبطة به:" + +msgid "Objects" +msgstr "عناصر" + +msgid "Yes, I’m sure" +msgstr "نعم، أنا متأكد" + +msgid "No, take me back" +msgstr "لا, تراجع للخلف" + +msgid "Delete multiple objects" +msgstr "حذف عدّة عناصر" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"حذف عناصر %(objects_name)s المُحدّدة سيتسبب بحذف العناصر المرتبطة، إلا أن " +"حسابك ليس له صلاحية حذف أنواع العناصر التالية:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"حذف عناصر %(objects_name)s المحدّدة قد يتطلب حذف العناصر المحميّة المرتبطة " +"التالية:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"أأنت متأكد أنك تريد حذف عناصر %(objects_name)s المحددة؟ جميع العناصر التالية " +"والعناصر المرتبطة بها سيتم حذفها:" + +msgid "Delete?" +msgstr "احذفه؟" + +#, python-format +msgid " By %(filter_title)s " +msgstr " حسب %(filter_title)s " + +msgid "Summary" +msgstr "ملخص" + +msgid "Recent actions" +msgstr "آخر الإجراءات" + +msgid "My actions" +msgstr "إجراءاتي" + +msgid "None available" +msgstr "لا يوجد" + +msgid "Unknown content" +msgstr "مُحتوى مجهول" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"هنالك أمر خاطئ في تركيب قاعدة بياناتك، تأكد من أنه تم انشاء جداول قاعدة " +"البيانات الملائمة، وأن قاعدة البيانات قابلة للقراءة من قبل المستخدم الملائم." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"أنت مسجل الدخول بإسم المستخدم %(username)s, ولكنك غير مخول للوصول لهذه " +"الصفحة. هل ترغب بتسجيل الدخول بحساب آخر؟" + +msgid "Forgotten your password or username?" +msgstr "نسيت كلمة المرور أو اسم المستخدم الخاص بك؟" + +msgid "Toggle navigation" +msgstr "تغيير التنقل" + +msgid "Start typing to filter…" +msgstr "ابدأ بالكتابة لبدء التصفية(الفلترة)..." + +msgid "Filter navigation items" +msgstr "تصفية عناصر التنقل" + +msgid "Date/time" +msgstr "التاريخ/الوقت" + +msgid "User" +msgstr "المستخدم" + +msgid "Action" +msgstr "إجراء" + +msgid "entry" +msgstr "" + +msgid "entries" +msgstr "" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"ليس لهذا العنصر سجلّ تغييرات، على الأغلب أنه لم يُنشأ من خلال نظام إدارة " +"الموقع." + +msgid "Show all" +msgstr "أظهر الكل" + +msgid "Save" +msgstr "احفظ" + +msgid "Popup closing…" +msgstr "إغلاق المنبثقة ..." + +msgid "Search" +msgstr "ابحث" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s نتيجة" +msgstr[1] "%(counter)s نتيجة" +msgstr[2] "%(counter)s نتيجة" +msgstr[3] "%(counter)s نتائج" +msgstr[4] "%(counter)s نتيجة" +msgstr[5] "%(counter)s نتيجة" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "المجموع %(full_result_count)s" + +msgid "Save as new" +msgstr "احفظ كجديد" + +msgid "Save and add another" +msgstr "احفظ وأضف آخر" + +msgid "Save and continue editing" +msgstr "احفظ واستمر بالتعديل" + +msgid "Save and view" +msgstr "احفظ ثم اعرض" + +msgid "Close" +msgstr "أغلق" + +#, python-format +msgid "Change selected %(model)s" +msgstr "تغيير %(model)s المختارة" + +#, python-format +msgid "Add another %(model)s" +msgstr "أضف %(model)s آخر" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "حذف %(model)s المختارة" + +#, python-format +msgid "View selected %(model)s" +msgstr "" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "شكرا لأخذك بعض الوقت في الموقع اليوم." + +msgid "Log in again" +msgstr "ادخل مجدداً" + +msgid "Password change" +msgstr "غيّر كلمة مرورك" + +msgid "Your password was changed." +msgstr "تمّ تغيير كلمة مرورك." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"رجاءً أدخل كلمة مرورك القديمة، للأمان، ثم أدخل كلمة مرور الجديدة مرتين كي " +"تتأكّد من كتابتها بشكل صحيح." + +msgid "Change my password" +msgstr "غيّر كلمة مروري" + +msgid "Password reset" +msgstr "استعادة كلمة المرور" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "تم تعيين كلمة مرورك. يمكن الاستمرار وتسجيل دخولك الآن." + +msgid "Password reset confirmation" +msgstr "تأكيد استعادة كلمة المرور" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "رجاءً أدخل كلمة مرورك الجديدة مرتين كي تتأكّد من كتابتها بشكل صحيح." + +msgid "New password:" +msgstr "كلمة المرور الجديدة:" + +msgid "Confirm password:" +msgstr "أكّد كلمة المرور:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"رابط استعادة كلمة المرور غير صحيح، ربما لأنه استُخدم من قبل. رجاءً اطلب " +"استعادة كلمة المرور مرة أخرى." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"تم إرسال بريد إلكتروني بالتعليمات لضبط كلمة المرور الخاصة بك, في حال تواجد " +"حساب بنفس البريد الإلكتروني الذي ادخلته. سوف تستقبل البريد الإلكتروني قريباً" + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"في حال عدم إستقبال البريد الإلكتروني، الرجاء التأكد من إدخال عنوان بريدك " +"الإلكتروني بشكل صحيح ومراجعة مجلد الرسائل غير المرغوب فيها." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"لقد قمت بتلقى هذه الرسالة لطلبك بإعادة تعين كلمة المرور لحسابك الشخصي على " +"%(site_name)s." + +msgid "Please go to the following page and choose a new password:" +msgstr "رجاءً اذهب إلى الصفحة التالية واختر كلمة مرور جديدة:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "اسم المستخدم الخاص بك، في حال كنت قد نسيته:" + +msgid "Thanks for using our site!" +msgstr "شكراً لاستخدامك موقعنا!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "فريق %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"هل فقدت كلمة المرور؟ أدخل عنوان بريدك الإلكتروني أدناه وسوف نقوم بإرسال " +"تعليمات للحصول على كلمة مرور جديدة." + +msgid "Email address:" +msgstr "عنوان البريد الإلكتروني:" + +msgid "Reset my password" +msgstr "استعد كلمة مروري" + +msgid "All dates" +msgstr "كافة التواريخ" + +#, python-format +msgid "Select %s" +msgstr "اختر %s" + +#, python-format +msgid "Select %s to change" +msgstr "اختر %s لتغييره" + +#, python-format +msgid "Select %s to view" +msgstr "حدد %s للعرض" + +msgid "Date:" +msgstr "التاريخ:" + +msgid "Time:" +msgstr "الوقت:" + +msgid "Lookup" +msgstr "ابحث" + +msgid "Currently:" +msgstr "حالياً:" + +msgid "Change:" +msgstr "تغيير:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/az/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/az/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..9f505c16 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/az/LC_MESSAGES/django 3.po @@ -0,0 +1,732 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Emin Mastizada , 2018,2020 +# Emin Mastizada , 2016 +# Konul Allahverdiyeva , 2016 +# Nicat Məmmədov , 2022 +# Zulfugar Ismayilzadeh , 2017 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:10-0500\n" +"PO-Revision-Date: 2022-07-25 07:05+0000\n" +"Last-Translator: Nicat Məmmədov \n" +"Language-Team: Azerbaijani (http://www.transifex.com/django/django/language/" +"az/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: az\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Seçilmiş %(verbose_name_plural)s-ləri sil" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d %(items)s uğurla silindi." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "%(name)s silinmir" + +msgid "Are you sure?" +msgstr "Əminsiniz?" + +msgid "Administration" +msgstr "Administrasiya" + +msgid "All" +msgstr "Hamısı" + +msgid "Yes" +msgstr "Hə" + +msgid "No" +msgstr "Yox" + +msgid "Unknown" +msgstr "Bilinmir" + +msgid "Any date" +msgstr "İstənilən tarix" + +msgid "Today" +msgstr "Bu gün" + +msgid "Past 7 days" +msgstr "Son 7 gündə" + +msgid "This month" +msgstr "Bu ay" + +msgid "This year" +msgstr "Bu il" + +msgid "No date" +msgstr "Tarixi yoxdur" + +msgid "Has date" +msgstr "Tarixi mövcuddur" + +msgid "Empty" +msgstr "Boş" + +msgid "Not empty" +msgstr "Boş deyil" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Lütfən, istifadəçi hesabı üçün doğru %(username)s və parol daxil olun. " +"Nəzərə alın ki, hər iki sahə böyük/kiçik hərflərə həssasdırlar." + +msgid "Action:" +msgstr "Əməliyyat:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Daha bir %(verbose_name)s əlavə et" + +msgid "Remove" +msgstr "Yığışdır" + +msgid "Addition" +msgstr "Əlavə" + +msgid "Change" +msgstr "Dəyiş" + +msgid "Deletion" +msgstr "Silmə" + +msgid "action time" +msgstr "əməliyyat vaxtı" + +msgid "user" +msgstr "istifadəçi" + +msgid "content type" +msgstr "məzmun növü" + +msgid "object id" +msgstr "obyekt id" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "obyekt repr" + +msgid "action flag" +msgstr "bayraq" + +msgid "change message" +msgstr "dəyişmə mesajı" + +msgid "log entry" +msgstr "loq yazısı" + +msgid "log entries" +msgstr "loq yazıları" + +#, python-format +msgid "Added “%(object)s”." +msgstr "“%(object)s” əlavə edildi." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "“%(object)s” dəyişdirildi — %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "“%(object)s” silindi." + +msgid "LogEntry Object" +msgstr "LogEntry obyekti" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "{name} “{object}” əlavə edildi." + +msgid "Added." +msgstr "Əlavə edildi." + +msgid "and" +msgstr "və" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "{name} “{object}” üçün {fields} dəyişdirildi." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "{fields} dəyişdirildi." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "{name} “{object}” silindi." + +msgid "No fields changed." +msgstr "Heç bir sahə dəyişmədi." + +msgid "None" +msgstr "Heç nə" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" +"Birdən çox seçmək üçün “Control” və ya Mac üçün “Command” düyməsini basılı " +"tutun." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "{name} “{obj}” uğurla əlavə edildi." + +msgid "You may edit it again below." +msgstr "Bunu aşağıda təkrar redaktə edə bilərsiz." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"{name} “{obj}” uğurla əlavə edildi. Aşağıdan başqa bir {name} əlavə edə " +"bilərsiz." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" +"{name} “{obj}” uğurla dəyişdirildi. Təkrar aşağıdan dəyişdirə bilərsiz." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" +"{name} “{obj}” uğurla əlavə edildi. Bunu təkrar aşağıdan dəyişdirə bilərsiz." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" +"{name} “{obj}” uğurla dəyişdirildi. Aşağıdan başqa bir {name} əlavə edə " +"bilərsiz." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "{name} “{obj}” uğurla dəyişdirildi." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Biz elementlər üzərində nəsə əməliyyat aparmaq üçün siz onları seçməlisiniz. " +"Heç bir element dəyişmədi." + +msgid "No action selected." +msgstr "Heç bir əməliyyat seçilmədi." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "%(name)s “%(obj)s” uğurla silindi." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "“%(key)s” ID nömrəli %(name)s mövcud deyil. Silinmiş ola bilər?" + +#, python-format +msgid "Add %s" +msgstr "%s əlavə et" + +#, python-format +msgid "Change %s" +msgstr "%s dəyiş" + +#, python-format +msgid "View %s" +msgstr "%s gör" + +msgid "Database error" +msgstr "Bazada xəta" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s uğurlu dəyişdirildi." +msgstr[1] "%(count)s %(name)s uğurlu dəyişdirildi." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s seçili" +msgstr[1] "Bütün %(total_count)s seçili" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "%(cnt)s-dan 0 seçilib" + +#, python-format +msgid "Change history: %s" +msgstr "Dəyişmə tarixi: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"%(class_name)s %(instance)s silmə əlaqəli qorunmalı obyektləri silməyi tələb " +"edir: %(related_objects)s" + +msgid "Django site admin" +msgstr "Django sayt administratoru" + +msgid "Django administration" +msgstr "Django administrasiya" + +msgid "Site administration" +msgstr "Sayt administrasiyası" + +msgid "Log in" +msgstr "Daxil ol" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s administrasiyası" + +msgid "Page not found" +msgstr "Səhifə tapılmadı" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Üzr istəyirik, amma sorğulanan səhifə tapılmadı." + +msgid "Home" +msgstr "Ev" + +msgid "Server error" +msgstr "Serverdə xəta" + +msgid "Server error (500)" +msgstr "Serverdə xəta (500)" + +msgid "Server Error (500)" +msgstr "Serverdə xəta (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Xəta baş verdi. Problem sayt administratorlarına epoçt vasitəsi ilə " +"bildirildi və qısa bir zamanda həll olunacaq. Anlayışınız üçün təşəkkür " +"edirik." + +msgid "Run the selected action" +msgstr "Seçdiyim əməliyyatı yerinə yetir" + +msgid "Go" +msgstr "Getdik" + +msgid "Click here to select the objects across all pages" +msgstr "Bütün səhifələr üzrə obyektləri seçmək üçün bura tıqlayın" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Bütün %(total_count)s sayda %(module_name)s seç" + +msgid "Clear selection" +msgstr "Seçimi təmizlə" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "%(name)s proqramındakı modellər" + +msgid "Add" +msgstr "Əlavə et" + +msgid "View" +msgstr "Gör" + +msgid "You don’t have permission to view or edit anything." +msgstr "Nəyi isə görmək və ya redaktə etmək icazəniz yoxdur." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Əvvəlcə istifacəçi adı və şifrəni daxil edin. Daha sonra siz daha çox " +"istifadəçi seçimlərinə düzəliş edə biləcəksiniz." + +msgid "Enter a username and password." +msgstr "İstifadəçi adını və şifrəni daxil edin." + +msgid "Change password" +msgstr "Şifrəni dəyiş" + +msgid "Please correct the error below." +msgstr "Lütfən aşağıdakı xətanı düzəldin." + +msgid "Please correct the errors below." +msgstr "Lütfən aşağıdakı səhvləri düzəldin." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "%(username)s üçün yeni şifrə daxil edin." + +msgid "Welcome," +msgstr "Xoş gördük," + +msgid "View site" +msgstr "Saytı ziyarət et" + +msgid "Documentation" +msgstr "Sənədləşdirmə" + +msgid "Log out" +msgstr "Çıx" + +#, python-format +msgid "Add %(name)s" +msgstr "%(name)s əlavə et" + +msgid "History" +msgstr "Tarix" + +msgid "View on site" +msgstr "Saytda göstər" + +msgid "Filter" +msgstr "Süzgəc" + +msgid "Clear all filters" +msgstr "Bütün filterləri təmizlə" + +msgid "Remove from sorting" +msgstr "Sıralamadan çıxar" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Sıralama prioriteti: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Sıralamanı çevir" + +msgid "Delete" +msgstr "Sil" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"%(object_name)s \"%(escaped_object)s\" obyektini sildikdə onun bağlı olduğu " +"obyektlər də silinməlidir. Ancaq sizin hesabın aşağıdakı tip obyektləri " +"silməyə səlahiyyəti çatmır:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"%(object_name)s \"%(escaped_object)s\" obyektini silmək üçün aşağıdakı " +"qorunan obyektlər də silinməlidir:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"%(object_name)s \"%(escaped_object)s\" obyektini silməkdə əminsiniz? Ona " +"bağlı olan aşağıdakı obyektlər də silinəcək:" + +msgid "Objects" +msgstr "Obyektlər" + +msgid "Yes, I’m sure" +msgstr "Bəli, əminəm" + +msgid "No, take me back" +msgstr "Xeyr, məni geri götür" + +msgid "Delete multiple objects" +msgstr "Bir neçə obyekt sil" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"%(objects_name)s obyektini silmək üçün ona bağlı obyektlər də silinməlidir. " +"Ancaq sizin hesabınızın aşağıdakı tip obyektləri silmək səlahiyyətinə malik " +"deyil:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"%(objects_name)s obyektini silmək üçün aşağıdakı qorunan obyektlər də " +"silinməlidir:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Seçdiyiniz %(objects_name)s obyektini silməkdə əminsiniz? Aşağıdakı bütün " +"obyektlər və ona bağlı digər obyektlər də silinəcək:" + +msgid "Delete?" +msgstr "Silək?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " %(filter_title)s görə " + +msgid "Summary" +msgstr "İcmal" + +msgid "Recent actions" +msgstr "Son əməliyyatlar" + +msgid "My actions" +msgstr "Mənim əməliyyatlarım" + +msgid "None available" +msgstr "Heç nə yoxdur" + +msgid "Unknown content" +msgstr "Naməlum" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"%(username)s olaraq daxil olmusunuz, amma bu səhifəyə icazəniz yoxdur. Başqa " +"bir hesaba daxil olmaq istərdiniz?" + +msgid "Forgotten your password or username?" +msgstr "Şifrə və ya istifadəçi adını unutmusuz?" + +msgid "Toggle navigation" +msgstr "" + +msgid "Start typing to filter…" +msgstr "Filterləmək üçün yazın..." + +msgid "Filter navigation items" +msgstr "" + +msgid "Date/time" +msgstr "Tarix/vaxt" + +msgid "User" +msgstr "İstifadəçi" + +msgid "Action" +msgstr "Əməliyyat" + +msgid "entry" +msgstr "" + +msgid "entries" +msgstr "" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" + +msgid "Show all" +msgstr "Hamısını göstər" + +msgid "Save" +msgstr "Yadda saxla" + +msgid "Popup closing…" +msgstr "Qəfil pəncərə qapatılır…" + +msgid "Search" +msgstr "Axtar" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s nəticə" +msgstr[1] "%(counter)s nəticə" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "Hamısı birlikdə %(full_result_count)s" + +msgid "Save as new" +msgstr "Yenisi kimi yadda saxla" + +msgid "Save and add another" +msgstr "Yadda saxla və yenisini əlavə et" + +msgid "Save and continue editing" +msgstr "Yadda saxla və redaktəyə davam et" + +msgid "Save and view" +msgstr "Saxla və gör" + +msgid "Close" +msgstr "Qapat" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Seçilmiş %(model)s dəyişdir" + +#, python-format +msgid "Add another %(model)s" +msgstr "Başqa %(model)s əlavə et" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Seçilmiş %(model)s sil" + +#, python-format +msgid "View selected %(model)s" +msgstr "" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "" + +msgid "Log in again" +msgstr "Yenidən daxil ol" + +msgid "Password change" +msgstr "Şifrəni dəyişmək" + +msgid "Your password was changed." +msgstr "Sizin şifrəniz dəyişdirildi." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" + +msgid "Change my password" +msgstr "Şifrəmi dəyiş" + +msgid "Password reset" +msgstr "Şifrənin sıfırlanması" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Yeni şifrə artıq qüvvədədir. Yenidən daxil ola bilərsiniz." + +msgid "Password reset confirmation" +msgstr "Şifrə sıfırlanmasının təsdiqi" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "Yeni şifrəni iki dəfə daxil edin ki, səhv etmədiyinizə əmin olaq." + +msgid "New password:" +msgstr "Yeni şifrə:" + +msgid "Confirm password:" +msgstr "Yeni şifrə (bir daha):" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Şifrənin sıfırlanması üçün olan keçid, yəqin ki, artıq istifadə olunub. " +"Şifrəni sıfırlamaq üçün yenə müraciət edin." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Şifrəni təyin etmək üçün lazım olan addımlar sizə göndərildi (əgər bu epoçt " +"ünvanı ilə hesab varsa təbii ki). Elektron məktub qısa bir müddət ərzində " +"sizə çatacaq." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"%(site_name)s saytında şifrəni yeniləmək istədiyinizə görə bu məktubu " +"göndərdik." + +msgid "Please go to the following page and choose a new password:" +msgstr "Növbəti səhifəyə keçid alın və yeni şifrəni seçin:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "İstifadəçi adınız, əgər unutmusunuzsa:" + +msgid "Thanks for using our site!" +msgstr "Bizim saytdan istifadə etdiyiniz üçün təşəkkür edirik!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "%(site_name)s komandası" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Şifrəni unutmusuz? Epoçt ünvanınızı daxil edin və biz sizə yeni şifrə təyin " +"etmək üçün nə etmək lazım olduğunu göndərəcəyik." + +msgid "Email address:" +msgstr "E-poçt:" + +msgid "Reset my password" +msgstr "Şifrəmi sıfırla" + +msgid "All dates" +msgstr "Bütün tarixlərdə" + +#, python-format +msgid "Select %s" +msgstr "%s seç" + +#, python-format +msgid "Select %s to change" +msgstr "%s dəyişmək üçün seç" + +#, python-format +msgid "Select %s to view" +msgstr "Görmək üçün %s seçin" + +msgid "Date:" +msgstr "Tarix:" + +msgid "Time:" +msgstr "Vaxt:" + +msgid "Lookup" +msgstr "Sorğu" + +msgid "Currently:" +msgstr "Hazırda:" + +msgid "Change:" +msgstr "Dəyişdir:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..06dfc248 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/bg/LC_MESSAGES/django 3.po @@ -0,0 +1,744 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# arneatec , 2022 +# Boris Chervenkov , 2012 +# Claude Paroz , 2014 +# Jannis Leidel , 2011 +# Lyuboslav Petrov , 2014 +# Todor Lubenov , 2020 +# Todor Lubenov , 2014-2015 +# Venelin Stoykov , 2015-2017 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:10-0500\n" +"PO-Revision-Date: 2022-05-25 07:05+0000\n" +"Last-Translator: arneatec , 2022\n" +"Language-Team: Bulgarian (http://www.transifex.com/django/django/language/" +"bg/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: bg\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Изтриване на избраните %(verbose_name_plural)s" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Успешно изтрити %(count)d %(items)s ." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Не можете да изтриете %(name)s" + +msgid "Are you sure?" +msgstr "Сигурни ли сте?" + +msgid "Administration" +msgstr "Администрация" + +msgid "All" +msgstr "Всички" + +msgid "Yes" +msgstr "Да" + +msgid "No" +msgstr "Не" + +msgid "Unknown" +msgstr "Неизвестно" + +msgid "Any date" +msgstr "Коя-да-е дата" + +msgid "Today" +msgstr "Днес" + +msgid "Past 7 days" +msgstr "Последните 7 дни" + +msgid "This month" +msgstr "Този месец" + +msgid "This year" +msgstr "Тази година" + +msgid "No date" +msgstr "Няма дата" + +msgid "Has date" +msgstr "Има дата" + +msgid "Empty" +msgstr "Празно" + +msgid "Not empty" +msgstr "Не е празно" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Моля въведете правилния %(username)s и парола за администраторски акаунт. " +"Моля забележете, че и двете полета могат да са с главни и малки букви." + +msgid "Action:" +msgstr "Действие:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Добави друг %(verbose_name)s" + +msgid "Remove" +msgstr "Премахване" + +msgid "Addition" +msgstr "Добавка" + +msgid "Change" +msgstr "Промени" + +msgid "Deletion" +msgstr "Изтриване" + +msgid "action time" +msgstr "време на действие" + +msgid "user" +msgstr "потребител" + +msgid "content type" +msgstr "тип на съдържанието" + +msgid "object id" +msgstr "id на обекта" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "repr на обекта" + +msgid "action flag" +msgstr "флаг за действие" + +msgid "change message" +msgstr "промени съобщение" + +msgid "log entry" +msgstr "записка в журнала" + +msgid "log entries" +msgstr "записки в журнала" + +#, python-format +msgid "Added “%(object)s”." +msgstr "Добавен “%(object)s”." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "Променени “%(object)s” — %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "Изтрити “%(object)s.”" + +msgid "LogEntry Object" +msgstr "LogEntry обект" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "Добавен {name} “{object}”." + +msgid "Added." +msgstr "Добавено." + +msgid "and" +msgstr "и" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "Променени {fields} за {name} “{object}”." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "Променени {fields}." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "Изтрит {name} “{object}”." + +msgid "No fields changed." +msgstr "Няма променени полета." + +msgid "None" +msgstr "Празно" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" +"Задръжте “Control”, или “Command” на Mac, за да изберете повече от едно." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "Обектът {name} “{obj}” бе успешно добавен." + +msgid "You may edit it again below." +msgstr "Можете отново да го промените по-долу." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"Обектът {name} “{obj}” бе успешно добавен. Можете да добавите друг {name} по-" +"долу." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" +"Обектът {name} “{obj}” бе успешно променен. Можете да го промените отново по-" +"долу." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" +"Обектът {name} “{obj}” бе успешно добавен. Можете да го промените отново по-" +"долу." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" +"Обектът {name} “{obj}” бе успешно променен. Можете да добавите друг {name} " +"по-долу." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "Обектът {name} “{obj}” бе успешно променен." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Елементите трябва да бъдат избрани, за да се извършат действия по тях. Няма " +"променени елементи." + +msgid "No action selected." +msgstr "Няма избрано действие." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "%(name)s “%(obj)s” беше успешно изтрит." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "%(name)s с ID “%(key)s” не съществува. Може би е изтрит?" + +#, python-format +msgid "Add %s" +msgstr "Добави %s" + +#, python-format +msgid "Change %s" +msgstr "Промени %s" + +#, python-format +msgid "View %s" +msgstr "Изглед %s" + +msgid "Database error" +msgstr "Грешка в базата данни" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s беше променено успешно." +msgstr[1] "%(count)s %(name)s бяха успешно променени." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s е избран" +msgstr[1] "Избрани са всички %(total_count)s" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "Избрани са 0 от %(cnt)s" + +#, python-format +msgid "Change history: %s" +msgstr "История на промените: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Изтриването на избраните %(class_name)s %(instance)s ще наложи изтриването " +"на следните защитени и свързани обекти: %(related_objects)s" + +msgid "Django site admin" +msgstr "Django административен сайт" + +msgid "Django administration" +msgstr "Django администрация" + +msgid "Site administration" +msgstr "Администрация на сайта" + +msgid "Log in" +msgstr "Вход" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s администрация" + +msgid "Page not found" +msgstr "Страница не е намерена" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Съжаляваме, но поисканата страница не може да бъде намерена." + +msgid "Home" +msgstr "Начало" + +msgid "Server error" +msgstr "Сървърна грешка" + +msgid "Server error (500)" +msgstr "Сървърна грешка (500)" + +msgid "Server Error (500)" +msgstr "Сървърна грешка (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Получи се грешка. Администраторите на сайта са уведомени за това чрез " +"електронна поща и грешката трябва да бъде поправена скоро. Благодарим ви за " +"търпението." + +msgid "Run the selected action" +msgstr "Изпълни избраното действие" + +msgid "Go" +msgstr "Напред" + +msgid "Click here to select the objects across all pages" +msgstr "Щракнете тук, за да изберете обектите във всички страници" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Избери всички %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "Изчисти избраното" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Модели в приложението %(name)s " + +msgid "Add" +msgstr "Добави" + +msgid "View" +msgstr "Изглед" + +msgid "You don’t have permission to view or edit anything." +msgstr "Нямате права да разглеждате или редактирате каквото и да е." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Първо въведете потребител и парола. След това ще можете да редактирате " +"повече детайли. " + +msgid "Enter a username and password." +msgstr "Въведете потребителско име и парола." + +msgid "Change password" +msgstr "Промени парола" + +msgid "Please correct the error below." +msgstr "Моля, поправете грешката по-долу" + +msgid "Please correct the errors below." +msgstr "Моля поправете грешките по-долу." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "Въведете нова парола за потребител %(username)s." + +msgid "Welcome," +msgstr "Добре дошли," + +msgid "View site" +msgstr "Виж сайта" + +msgid "Documentation" +msgstr "Документация" + +msgid "Log out" +msgstr "Изход" + +#, python-format +msgid "Add %(name)s" +msgstr "Добави %(name)s" + +msgid "History" +msgstr "История" + +msgid "View on site" +msgstr "Разгледай в сайта" + +msgid "Filter" +msgstr "Филтър" + +msgid "Clear all filters" +msgstr "Изчисти всички филтри" + +msgid "Remove from sorting" +msgstr "Премахни от подреждането" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Ред на подреждане: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Превключи подреждането" + +msgid "Delete" +msgstr "Изтрий" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Изтриването на %(object_name)s '%(escaped_object)s' би причинило изтриване " +"на свързани обекти, но вашият потребител няма право да изтрива следните " +"видове обекти:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Изтриването на %(object_name)s '%(escaped_object)s' изисква изтриването на " +"следните защитени свързани обекти:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Наистина ли искате да изтриете %(object_name)s \"%(escaped_object)s\"? " +"Следните свързани елементи също ще бъдат изтрити:" + +msgid "Objects" +msgstr "Обекти" + +msgid "Yes, I’m sure" +msgstr "Да, сигурен съм" + +msgid "No, take me back" +msgstr "Не, върни ме обратно" + +msgid "Delete multiple objects" +msgstr "Изтриване на множество обекти" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Изтриването на избраните %(objects_name)s ще доведе до изтриване на свързани " +"обекти, но вашият потребител няма право да изтрива следните типове обекти:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Изтриването на избраните %(objects_name)s изисква изтриването на следните " +"защитени свързани обекти:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Наистина ли искате да изтриете избраните %(objects_name)s? Всички изброени " +"обекти и свързаните с тях ще бъдат изтрити:" + +msgid "Delete?" +msgstr "Изтриване?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " По %(filter_title)s " + +msgid "Summary" +msgstr "Резюме" + +msgid "Recent actions" +msgstr "Последни действия" + +msgid "My actions" +msgstr "Моите действия" + +msgid "None available" +msgstr "Няма налични" + +msgid "Unknown content" +msgstr "Неизвестно съдържание" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Проблем с вашата база данни. Убедете се, че необходимите таблици в базата са " +"създадени и че съответния потребител има необходимите права за достъп. " + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Вие сте се удостоверен като %(username)s, но не сте оторизиран да достъпите " +"тази страница. Бихте ли желали да влезе с друг профил?" + +msgid "Forgotten your password or username?" +msgstr "Забравена парола или потребителско име?" + +msgid "Toggle navigation" +msgstr "Превключи навигацията" + +msgid "Start typing to filter…" +msgstr "Започнете да пишете за филтър..." + +msgid "Filter navigation items" +msgstr "Филтриране на навигационните елементи" + +msgid "Date/time" +msgstr "Дата/час" + +msgid "User" +msgstr "Потребител" + +msgid "Action" +msgstr "Действие" + +msgid "entry" +msgstr "запис" + +msgid "entries" +msgstr "записа" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"Този обект няма история на промените. Вероятно не е бил добавен чрез този " +"административен сайт." + +msgid "Show all" +msgstr "Покажи всички" + +msgid "Save" +msgstr "Запис" + +msgid "Popup closing…" +msgstr "Изскачащият прозорец се затваря..." + +msgid "Search" +msgstr "Търсене" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s резултат" +msgstr[1] "%(counter)s резултати" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s общо" + +msgid "Save as new" +msgstr "Запиши като нов" + +msgid "Save and add another" +msgstr "Запиши и добави нов" + +msgid "Save and continue editing" +msgstr "Запиши и продължи" + +msgid "Save and view" +msgstr "Запиши и прегледай" + +msgid "Close" +msgstr "Затвори" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Променете избрания %(model)s" + +#, python-format +msgid "Add another %(model)s" +msgstr "Добавяне на друг %(model)s" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Изтриване на избрания %(model)s" + +#, python-format +msgid "View selected %(model)s" +msgstr "Виж избраните %(model)s" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "Благодарим ви за добре прекараното време с този сайт днес." + +msgid "Log in again" +msgstr "Влез пак" + +msgid "Password change" +msgstr "Промяна на парола" + +msgid "Your password was changed." +msgstr "Паролата ви е променена." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Въведете старата си парола /от съображения за сигурност/. След това въведете " +"желаната нова парола два пъти, за да сверим дали е написана правилно." + +msgid "Change my password" +msgstr "Промяна на паролата ми" + +msgid "Password reset" +msgstr "Нова парола" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Паролата е променена. Вече можете да се впишете." + +msgid "Password reset confirmation" +msgstr "Потвърждение за смяна на паролата" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Моля, въведете новата парола два пъти, за да се уверим, че сте я написали " +"правилно." + +msgid "New password:" +msgstr "Нова парола:" + +msgid "Confirm password:" +msgstr "Потвърдете паролата:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Връзката за възстановяване на паролата е невалидна, може би защото вече е " +"използвана. Моля, поискайте нова промяна на паролата." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"По имейл изпратихме инструкции за смяна на паролата, ако съществува профил с " +"въведения от вас адрес. Би трябвало скоро да ги получите. " + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Ако не получите имейл, моля уверете се, че сте попълнили правилно адреса, с " +"който сте се регистрирали, също проверете спам папката във вашата поща." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Вие получавати този имейл, защото сте поискали да промените паролата за " +"вашия потребителски акаунт в %(site_name)s." + +msgid "Please go to the following page and choose a new password:" +msgstr "Моля, отидете на следната страница и изберете нова парола:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "Вашето потребителско име, в случай че сте го забравили:" + +msgid "Thanks for using our site!" +msgstr "Благодарим, че ползвате сайта ни!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "Екипът на %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Забравили сте си паролата? Въведете своя имейл адрес по-долу, и ние ще ви " +"изпратим инструкции как да я смените с нова." + +msgid "Email address:" +msgstr "Имейл адреси:" + +msgid "Reset my password" +msgstr "Задай новата ми парола" + +msgid "All dates" +msgstr "Всички дати" + +#, python-format +msgid "Select %s" +msgstr "Изберете %s" + +#, python-format +msgid "Select %s to change" +msgstr "Изберете %s за промяна" + +#, python-format +msgid "Select %s to view" +msgstr "Избери %s за преглед" + +msgid "Date:" +msgstr "Дата:" + +msgid "Time:" +msgstr "Час:" + +msgid "Lookup" +msgstr "Търсене" + +msgid "Currently:" +msgstr "Сега:" + +msgid "Change:" +msgstr "Промяна:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/bn/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/bn/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..6437d1d1 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/bn/LC_MESSAGES/django 3.po @@ -0,0 +1,713 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Anubhab Baksi, 2013 +# Jannis Leidel , 2011 +# Md Arshad Hussain, 2022 +# Tahmid Rafi , 2012-2013 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:10-0500\n" +"PO-Revision-Date: 2022-07-25 07:05+0000\n" +"Last-Translator: Md Arshad Hussain\n" +"Language-Team: Bengali (http://www.transifex.com/django/django/language/" +"bn/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: bn\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "চিহ্নিত অংশটি %(verbose_name_plural)s মুছে ফেলুন" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d টি %(items)s সফলভাবে মুছে ফেলা হয়েছে" + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "%(name)s ডিলিট করা সম্ভব নয়" + +msgid "Are you sure?" +msgstr "আপনি কি নিশ্চিত?" + +msgid "Administration" +msgstr "প্রয়োগ" + +msgid "All" +msgstr "সকল" + +msgid "Yes" +msgstr "হ্যাঁ" + +msgid "No" +msgstr "না" + +msgid "Unknown" +msgstr "অজানা" + +msgid "Any date" +msgstr "যে কোন তারিখ" + +msgid "Today" +msgstr "‍আজ" + +msgid "Past 7 days" +msgstr "শেষ ৭ দিন" + +msgid "This month" +msgstr "এ মাসে" + +msgid "This year" +msgstr "এ বছরে" + +msgid "No date" +msgstr "কোন তারিখ নেই" + +msgid "Has date" +msgstr "তারিখ আছে" + +msgid "Empty" +msgstr "খালি" + +msgid "Not empty" +msgstr "খালি নেই" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" + +msgid "Action:" +msgstr "কাজ:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "আরো একটি %(verbose_name)s যোগ করুন" + +msgid "Remove" +msgstr "মুছে ফেলুন" + +msgid "Addition" +msgstr "" + +msgid "Change" +msgstr "পরিবর্তন" + +msgid "Deletion" +msgstr "" + +msgid "action time" +msgstr "কার্য সময়" + +msgid "user" +msgstr "ব্যবহারকারী" + +msgid "content type" +msgstr "" + +msgid "object id" +msgstr "অবজেক্ট আইডি" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "অবজেক্ট উপস্থাপক" + +msgid "action flag" +msgstr "কার্যচিহ্ন" + +msgid "change message" +msgstr "বার্তা পরিবর্তন করুন" + +msgid "log entry" +msgstr "লগ এন্ট্রি" + +msgid "log entries" +msgstr "লগ এন্ট্রিসমূহ" + +#, python-format +msgid "Added “%(object)s”." +msgstr "“%(object)s” যোগ করা হয়েছে। " + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "“%(object)s” — %(changes)s পরিবর্তন করা হয়েছে" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "“%(object)s.” মুছে ফেলা হয়েছে" + +msgid "LogEntry Object" +msgstr "লগ-এন্ট্রি দ্রব্য" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "" + +msgid "Added." +msgstr "যুক্ত করা হয়েছে" + +msgid "and" +msgstr "এবং" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "" + +#, python-brace-format +msgid "Changed {fields}." +msgstr "" + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "" + +msgid "No fields changed." +msgstr "কোন ফিল্ড পরিবর্তন হয়নি।" + +msgid "None" +msgstr "কিছু না" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "" + +msgid "You may edit it again below." +msgstr "নিম্নে আপনি আবার তা সম্পাদনা/পরিবর্তন করতে পারেন।" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"{name} \"{obj}\" সফলভাবে যুক্ত হয়েছে৷ নিম্নে আপনি আরেকটি {name} যুক্ত করতে পারেন।" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "কাজ করার আগে বস্তুগুলিকে অবশ্যই চিহ্নিত করতে হবে। কোনো বস্তু পরিবর্তিত হয়নি।" + +msgid "No action selected." +msgstr "কোনো কাজ " + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "" + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "" + +#, python-format +msgid "Add %s" +msgstr "%s যোগ করুন" + +#, python-format +msgid "Change %s" +msgstr "%s পরিবর্তন করুন" + +#, python-format +msgid "View %s" +msgstr "" + +msgid "Database error" +msgstr "ডাটাবেস সমস্যা" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "%(cnt)s টি থেকে ০ টি সিলেক্ট করা হয়েছে" + +#, python-format +msgid "Change history: %s" +msgstr "ইতিহাস পরিবর্তনঃ %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" + +msgid "Django site admin" +msgstr "জ্যাঙ্গো সাইট প্রশাসক" + +msgid "Django administration" +msgstr "জ্যাঙ্গো প্রশাসন" + +msgid "Site administration" +msgstr "সাইট প্রশাসন" + +msgid "Log in" +msgstr "প্রবেশ করুন" + +#, python-format +msgid "%(app)s administration" +msgstr "" + +msgid "Page not found" +msgstr "পৃষ্ঠা পাওয়া যায়নি" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "আমরা দুঃখিত, কিন্তু অনুরোধকৃত পাতা খুঁজে পাওয়া যায়নি।" + +msgid "Home" +msgstr "নীড়পাতা" + +msgid "Server error" +msgstr "সার্ভার সমস্যা" + +msgid "Server error (500)" +msgstr "সার্ভার সমস্যা (৫০০)" + +msgid "Server Error (500)" +msgstr "সার্ভার সমস্যা (৫০০)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" + +msgid "Run the selected action" +msgstr "চিহ্নিত কাজটি শুরু করুন" + +msgid "Go" +msgstr "যান" + +msgid "Click here to select the objects across all pages" +msgstr "সকল পৃষ্ঠার দ্রব্য পছন্দ করতে এখানে ক্লিক করুন" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "%(total_count)s টি %(module_name)s এর সবগুলোই সিলেক্ট করুন" + +msgid "Clear selection" +msgstr "চিহ্নিত অংশের চিহ্ন মুছে ফেলুন" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "%(name)s এপ্লিকেশন এর মডেল গুলো" + +msgid "Add" +msgstr "যোগ করুন" + +msgid "View" +msgstr "দেখুন" + +msgid "You don’t have permission to view or edit anything." +msgstr "কোন কিছু দেখার বা সম্পাদনা/পরিবর্তন করার আপনার কোন অনুমতি নেই।" + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"প্রথমে, একজন ব্যবহারকারীর নাম এবং পাসওয়ার্ড লিখুন। তাহলে, আপনি ব্যবহারকারীর " +"অন্যান্য অনেক অপশনগুলো পরিবর্তন করতে সক্ষম হবেন। " + +msgid "Enter a username and password." +msgstr "ইউজার নেইম এবং পাসওয়ার্ড টাইপ করুন।" + +msgid "Change password" +msgstr "পাসওয়ার্ড বদলান" + +msgid "Please correct the error below." +msgstr "দয়া করে নিম্নবর্ণিত ভুলটি সংশোধন করুন।" + +msgid "Please correct the errors below." +msgstr "দয়া করে নিম্নবর্ণিত ভুলগুলো সংশোধন করুন।" + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "%(username)s সদস্যের জন্য নতুন পাসওয়ার্ড দিন।" + +msgid "Welcome," +msgstr "স্বাগতম," + +msgid "View site" +msgstr "" + +msgid "Documentation" +msgstr "সহায়িকা" + +msgid "Log out" +msgstr "প্রস্থান" + +#, python-format +msgid "Add %(name)s" +msgstr "%(name)s যোগ করুন" + +msgid "History" +msgstr "ইতিহাস" + +msgid "View on site" +msgstr "সাইটে দেখুন" + +msgid "Filter" +msgstr "ফিল্টার" + +msgid "Clear all filters" +msgstr "" + +msgid "Remove from sorting" +msgstr "ক্রমানুসারে সাজানো থেকে বিরত হোন" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "সাজানোর ক্রম: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "ক্রমানুসারে সাজানো চালু করুন/ বন্ধ করুন" + +msgid "Delete" +msgstr "মুছুন" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"%(object_name)s '%(escaped_object)s' মুছে ফেললে এর সম্পর্কিত অবজেক্টগুলোও মুছে " +"যাবে, কিন্তু আপনার নিম্নবর্ণিত অবজেক্টগুলো মোছার অধিকার নেইঃ" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"আপনি কি %(object_name)s \"%(escaped_object)s\" মুছে ফেলার ব্যাপারে নিশ্চিত? " +"নিম্নে বর্ণিত সকল আইটেম মুছে যাবেঃ" + +msgid "Objects" +msgstr "" + +msgid "Yes, I’m sure" +msgstr "হ্যাঁ, আমি নিশ্চিত" + +msgid "No, take me back" +msgstr "না, আমাক পূর্বের জায়গায় ফিরিয়ে নাও" + +msgid "Delete multiple objects" +msgstr "একাধিক জিনিস মুছে ফেলুন" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" + +msgid "Delete?" +msgstr "মুছে ফেলুন?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " %(filter_title)s অনুযায়ী " + +msgid "Summary" +msgstr "সারসংক্ষেপ" + +msgid "Recent actions" +msgstr "" + +msgid "My actions" +msgstr "আমার করনীয়" + +msgid "None available" +msgstr "কিছুই পাওয়া যায়নি" + +msgid "Unknown content" +msgstr "অজানা বিষয়" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"আপনার ড্যাটাবেস ইন্সটলেশন করার ক্ষেত্রে কিছু সমস্যা রয়েছে। নিশ্চিত করুন যে আপনি " +"যথাযথ ড্যাটাবেস টেবিলগুলো তৈরী করেছেন এবং নিশ্চিত করুন যে উক্ত ড্যাটাবেসটি অন্যান্য " +"ব্যবহারকারী দ্বারা ব্যবহারযোগ্য হবে। " + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" + +msgid "Forgotten your password or username?" +msgstr "ইউজার নেইম অথবা পাসওয়ার্ড ভুলে গেছেন?" + +msgid "Toggle navigation" +msgstr "" + +msgid "Start typing to filter…" +msgstr "" + +msgid "Filter navigation items" +msgstr "" + +msgid "Date/time" +msgstr "তারিখ/সময়" + +msgid "User" +msgstr "সদস্য" + +msgid "Action" +msgstr "কার্য" + +msgid "entry" +msgstr "" + +msgid "entries" +msgstr "" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" + +msgid "Show all" +msgstr "সব দেখান" + +msgid "Save" +msgstr "সংরক্ষণ করুন" + +msgid "Popup closing…" +msgstr "" + +msgid "Search" +msgstr "সার্চ" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "মোট %(full_result_count)s" + +msgid "Save as new" +msgstr "নতুনভাবে সংরক্ষণ করুন" + +msgid "Save and add another" +msgstr "সংরক্ষণ করুন এবং আরেকটি যোগ করুন" + +msgid "Save and continue editing" +msgstr "সংরক্ষণ করুন এবং সম্পাদনা চালিয়ে যান" + +msgid "Save and view" +msgstr "সংরক্ষণ করুন এবং দেখুন" + +msgid "Close" +msgstr "বন্ধ করুন" + +#, python-format +msgid "Change selected %(model)s" +msgstr "%(model)s নির্বাচিত অংশটি পরিবর্তন করুন" + +#, python-format +msgid "Add another %(model)s" +msgstr "আরো একটি%(model)s যুক্ত করুন" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "%(model)s নির্বাচিত অংশটি মুছুন " + +#, python-format +msgid "View selected %(model)s" +msgstr "" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "আজকে ওয়েব সাইট আপনার গুনগত সময় দেয়ার জন্য আপনাকে ধন্যবাদ।" + +msgid "Log in again" +msgstr "পুনরায় প্রবেশ করুন" + +msgid "Password change" +msgstr "পাসওয়ার্ড বদলান" + +msgid "Your password was changed." +msgstr "আপনার পাসওয়ার্ড বদলানো হয়েছে।" + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"দয়া করে নিরাপত্তার জন্য আপনার পুরানো পাসওয়ার্ডটি লিখুন, এবং তারপর নতুন পাসওয়ার্ডটি " +"দুইবার লিখুন যাতে করে আপনি সঠিক পাসওয়ার্ডটি লিখেছেন কি না তা আমরা যাচাই করতে " +"পারি। " + +msgid "Change my password" +msgstr "আমার পাসওয়ার্ড পরিবর্তন করুন" + +msgid "Password reset" +msgstr "পাসওয়ার্ড রিসেট করুন" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "আপনার পাসওয়ার্ড দেয়া হয়েছে। আপনি এখন প্রবেশ (লগইন) করতে পারেন।" + +msgid "Password reset confirmation" +msgstr "পাসওয়ার্ড রিসেট নিশ্চিত করুন" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"অনুগ্রহ করে আপনার পাসওয়ার্ড দুবার প্রবেশ করান, যাতে আমরা যাচাই করতে পারি আপনি " +"সঠিকভাবে টাইপ করেছেন।" + +msgid "New password:" +msgstr "নতুন পাসওয়ার্ডঃ" + +msgid "Confirm password:" +msgstr "পাসওয়ার্ড নিশ্চিতকরণঃ" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"পাসওয়ার্ড রিসেট লিঙ্কটি ঠিক নয়, হয়তো এটা ইতোমধ্যে ব্যবহৃত হয়েছে। পাসওয়ার্ড " +"রিসেটের জন্য অনুগ্রহ করে নতুনভাবে আবেদন করুন।" + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"আপনার পাসওয়ার্ড সংযোজন করার জন্য আমরা আপনাকে নির্দেশাবলী সম্বলিত একটি ই-মেইল " +"পাঠাবো, যদি আপনার দেয়া ই-মেইলে আইডিটি এখানে বিদ্যমান থাকে। আপনি খুব দ্রুত তা " +"পেয়ে যাবেন। " + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"আপনি যদি কোন ই-মেইল না পেয়ে থাকে, তবে দয়া করে নিশ্চিত করুন আপনি যে ই-মেইল আইডি " +"দিয়ে নিবন্ধন করেছিলেন তা এখানে লিখেছেন, এবং আপনার স্পাম ফোল্ডার চেক করুন। " + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"আপনি এই ই-মেইলটি পেয়েছেন কারন আপনি %(site_name)s এ আপনার ইউজার একাউন্টের " +"পাসওয়ার্ড রিসেট এর জন্য অনুরোধ করেছেন।" + +msgid "Please go to the following page and choose a new password:" +msgstr "অনুগ্রহ করে নিচের পাতাটিতে যান এবং নতুন পাসওয়ার্ড বাছাই করুনঃ" + +msgid "Your username, in case you’ve forgotten:" +msgstr "আপনার ব্যবহারকারীর নাম, যদি আপনি ভুলে গিয়ে থাকেন:" + +msgid "Thanks for using our site!" +msgstr "আমাদের সাইট ব্যবহারের জন্য ধন্যবাদ!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "%(site_name)s দল" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"আপনার পাসওয়ার্ড ভুলে গিয়েছেন? নিম্নে আপনার ই-মেইল আইডি লিখুন, এবং আমরা আপনাকে " +"নতুন পাসওয়ার্ড সংযোজন করার জন্য নির্দেশাবলী সম্বলিত ই-মেইল পাঠাবো।" + +msgid "Email address:" +msgstr "ইমেইল ঠিকানা:" + +msgid "Reset my password" +msgstr "আমার পাসওয়ার্ড রিসেট করুন" + +msgid "All dates" +msgstr "সকল তারিখ" + +#, python-format +msgid "Select %s" +msgstr "%s বাছাই করুন" + +#, python-format +msgid "Select %s to change" +msgstr "%s পরিবর্তনের জন্য বাছাই করুন" + +#, python-format +msgid "Select %s to view" +msgstr "দেখার জন্য %s নির্বাচন করুন" + +msgid "Date:" +msgstr "তারিখঃ" + +msgid "Time:" +msgstr "সময়ঃ" + +msgid "Lookup" +msgstr "খুঁজুন" + +msgid "Currently:" +msgstr "বর্তমান অবস্থা:" + +msgid "Change:" +msgstr "পরিবর্তন:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..863d048e --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ca/LC_MESSAGES/django 3.po @@ -0,0 +1,750 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Antoni Aloy , 2014-2015,2017,2021 +# Carles Barrobés , 2011-2012,2014 +# duub qnnp, 2015 +# Emilio Carrion, 2022 +# GerardoGa , 2018 +# Gil Obradors Via , 2019 +# Gil Obradors Via , 2019 +# Jannis Leidel , 2011 +# Manel Clos , 2020 +# Marc Compte , 2021 +# Roger Pons , 2015 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:10-0500\n" +"PO-Revision-Date: 2022-07-25 07:05+0000\n" +"Last-Translator: Emilio Carrion\n" +"Language-Team: Catalan (http://www.transifex.com/django/django/language/" +"ca/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ca\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Eliminar els %(verbose_name_plural)s seleccionats" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Eliminat/s %(count)d %(items)s satisfactòriament." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "No es pot esborrar %(name)s" + +msgid "Are you sure?" +msgstr "N'esteu segur?" + +msgid "Administration" +msgstr "Administració" + +msgid "All" +msgstr "Tots" + +msgid "Yes" +msgstr "Sí" + +msgid "No" +msgstr "No" + +msgid "Unknown" +msgstr "Desconegut" + +msgid "Any date" +msgstr "Qualsevol data" + +msgid "Today" +msgstr "Avui" + +msgid "Past 7 days" +msgstr "Últims 7 dies" + +msgid "This month" +msgstr "Aquest mes" + +msgid "This year" +msgstr "Aquest any" + +msgid "No date" +msgstr "Sense data" + +msgid "Has date" +msgstr "Té data" + +msgid "Empty" +msgstr "Buit" + +msgid "Not empty" +msgstr "No buit" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Si us plau, introduïu un %(username)s i contrasenya correctes per un compte " +"de personal. Observeu que ambdós camps són sensibles a majúscules." + +msgid "Action:" +msgstr "Acció:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Afegir un/a altre/a %(verbose_name)s." + +msgid "Remove" +msgstr "Eliminar" + +msgid "Addition" +msgstr "Afegeix" + +msgid "Change" +msgstr "Modificar" + +msgid "Deletion" +msgstr "Supressió" + +msgid "action time" +msgstr "moment de l'acció" + +msgid "user" +msgstr "usuari" + +msgid "content type" +msgstr "tipus de contingut" + +msgid "object id" +msgstr "id de l'objecte" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "'repr' de l'objecte" + +msgid "action flag" +msgstr "indicador de l'acció" + +msgid "change message" +msgstr "missatge del canvi" + +msgid "log entry" +msgstr "entrada del registre" + +msgid "log entries" +msgstr "entrades del registre" + +#, python-format +msgid "Added “%(object)s”." +msgstr "Afegit \"1%(object)s\"." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "Modificat \"%(object)s\" - %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "Eliminat \"%(object)s.\"" + +msgid "LogEntry Object" +msgstr "Objecte entrada del registre" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "Afegit {name} \"{object}\"." + +msgid "Added." +msgstr "Afegit." + +msgid "and" +msgstr "i" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "Canviat {fields} per {name} \"{object}\"." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "Canviats {fields}." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "Eliminat {name} \"{object}\"." + +msgid "No fields changed." +msgstr "Cap camp modificat." + +msgid "None" +msgstr "cap" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" +"Premeu la tecla \"Control\", o \"Command\" en un Mac, per seleccionar més " +"d'un valor." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "El {name} \"{obj}\" fou afegit amb èxit." + +msgid "You may edit it again below." +msgstr "Podeu editar-lo de nou a sota." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"El {name} \"{obj}\" s'ha afegit amb èxit. Podeu afegir un altre {name} a " +"sota." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" +"El {name} \"{obj}\" fou canviat amb èxit. Podeu editar-lo de nou a sota." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" +"El {name} \"{obj}\" s'ha afegit amb èxit. Podeu editar-lo de nou a sota." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" +"El {name} \"{obj}\" fou canviat amb èxit. Podeu afegir un altre {name} a " +"sota." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "El {name} \"{obj}\" fou canviat amb èxit." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Heu de seleccionar els elements per poder realitzar-hi accions. No heu " +"seleccionat cap element." + +msgid "No action selected." +msgstr "No heu seleccionat cap acció." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "El/la %(name)s \"%(obj)s\" s'ha eliminat amb èxit." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "%(name)s amb ID \"%(key)s\" no existeix. Potser va ser eliminat?" + +#, python-format +msgid "Add %s" +msgstr "Afegir %s" + +#, python-format +msgid "Change %s" +msgstr "Modificar %s" + +#, python-format +msgid "View %s" +msgstr "Visualitza %s" + +msgid "Database error" +msgstr "Error de base de dades" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s s'ha modificat amb èxit." +msgstr[1] "%(count)s %(name)s s'han modificat amb èxit." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s seleccionat(s)" +msgstr[1] "Tots %(total_count)s seleccionat(s)" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 de %(cnt)s seleccionats" + +#, python-format +msgid "Change history: %s" +msgstr "Modificar històric: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Esborrar %(class_name)s %(instance)s requeriria esborrar els següents " +"objectes relacionats protegits: %(related_objects)s" + +msgid "Django site admin" +msgstr "Lloc administratiu de Django" + +msgid "Django administration" +msgstr "Administració de Django" + +msgid "Site administration" +msgstr "Administració del lloc" + +msgid "Log in" +msgstr "Iniciar sessió" + +#, python-format +msgid "%(app)s administration" +msgstr "Administració de %(app)s" + +msgid "Page not found" +msgstr "No s'ha pogut trobar la pàgina" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Ho sentim, però no s'ha pogut trobar la pàgina sol·licitada" + +msgid "Home" +msgstr "Inici" + +msgid "Server error" +msgstr "Error del servidor" + +msgid "Server error (500)" +msgstr "Error del servidor (500)" + +msgid "Server Error (500)" +msgstr "Error del servidor (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"S'ha produït un error. Se n'ha informat els administradors del lloc per " +"correu electrònic, i hauria d'arreglar-se en breu. Gràcies per la vostra " +"paciència." + +msgid "Run the selected action" +msgstr "Executar l'acció seleccionada" + +msgid "Go" +msgstr "Anar" + +msgid "Click here to select the objects across all pages" +msgstr "Feu clic aquí per seleccionar els objectes a totes les pàgines" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Seleccioneu tots %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "Netejar la selecció" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Models en l'aplicació %(name)s" + +msgid "Add" +msgstr "Afegir" + +msgid "View" +msgstr "Visualitza" + +msgid "You don’t have permission to view or edit anything." +msgstr "No teniu permisos per veure o editar" + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Primer, entreu un nom d'usuari i una contrasenya. Després podreu editar més " +"opcions de l'usuari." + +msgid "Enter a username and password." +msgstr "Introduïu un nom d'usuari i contrasenya." + +msgid "Change password" +msgstr "Canviar contrasenya" + +msgid "Please correct the error below." +msgstr "Si us plau, corregiu l'error de sota." + +msgid "Please correct the errors below." +msgstr "Si us plau, corregiu els errors mostrats a sota." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "Introduïu una contrasenya per l'usuari %(username)s" + +msgid "Welcome," +msgstr "Benvingut/da," + +msgid "View site" +msgstr "Veure lloc" + +msgid "Documentation" +msgstr "Documentació" + +msgid "Log out" +msgstr "Finalitzar sessió" + +#, python-format +msgid "Add %(name)s" +msgstr "Afegir %(name)s" + +msgid "History" +msgstr "Històric" + +msgid "View on site" +msgstr "Veure al lloc" + +msgid "Filter" +msgstr "Filtre" + +msgid "Clear all filters" +msgstr "Netejar tots els filtres" + +msgid "Remove from sorting" +msgstr "Treure de la ordenació" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Prioritat d'ordenació: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Commutar ordenació" + +msgid "Delete" +msgstr "Eliminar" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Eliminar el/la %(object_name)s '%(escaped_object)s' provocaria l'eliminació " +"d'objectes relacionats, però el vostre compte no te permisos per esborrar " +"els tipus d'objecte següents:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Esborrar %(object_name)s '%(escaped_object)s' requeriria esborrar els " +"següents objectes relacionats protegits:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Esteu segurs de voler esborrar els/les %(object_name)s \"%(escaped_object)s" +"\"? S'esborraran els següents elements relacionats:" + +msgid "Objects" +msgstr "Objectes" + +msgid "Yes, I’m sure" +msgstr "Sí, n'estic segur" + +msgid "No, take me back" +msgstr "No, torna endarrere" + +msgid "Delete multiple objects" +msgstr "Eliminar múltiples objectes" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Esborrar els %(objects_name)s seleccionats faria que s'esborréssin objectes " +"relacionats, però el vostre compte no té permisos per esborrar els següents " +"tipus d'objectes:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Esborrar els %(objects_name)s seleccionats requeriria esborrar els següents " +"objectes relacionats protegits:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"N'esteu segur de voler esborrar els %(objects_name)s seleccionats? " +"S'esborraran tots els objects següents i els seus elements relacionats:" + +msgid "Delete?" +msgstr "Eliminar?" + +#, python-format +msgid " By %(filter_title)s " +msgstr "Per %(filter_title)s " + +msgid "Summary" +msgstr "Resum" + +msgid "Recent actions" +msgstr "Accions recents" + +msgid "My actions" +msgstr "Les meves accions" + +msgid "None available" +msgstr "Cap disponible" + +msgid "Unknown content" +msgstr "Contingut desconegut" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Hi ha algun problema a la instal·lació de la vostra base de dades. Assegureu-" +"vos que s'han creat les taules adients, i que la base de dades és llegible " +"per l'usuari apropiat." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Esteu identificats com a %(username)s, però no esteu autoritzats a accedir a " +"aquesta pàgina. Voleu identificar-vos amb un compte d'usuari diferent?" + +msgid "Forgotten your password or username?" +msgstr "Heu oblidat la vostra contrasenya o nom d'usuari?" + +msgid "Toggle navigation" +msgstr "Canviar mode de navegació" + +msgid "Start typing to filter…" +msgstr "Comença a teclejar per filtrar ..." + +msgid "Filter navigation items" +msgstr "Filtrar els items de navegació" + +msgid "Date/time" +msgstr "Data/hora" + +msgid "User" +msgstr "Usuari" + +msgid "Action" +msgstr "Acció" + +msgid "entry" +msgstr "entrada" + +msgid "entries" +msgstr "entrades" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"Aquest objecte no té historial de canvis. Probablement no es va afegir " +"utilitzant aquest lloc administratiu." + +msgid "Show all" +msgstr "Mostrar tots" + +msgid "Save" +msgstr "Desar" + +msgid "Popup closing…" +msgstr "Tancant finestra emergent..." + +msgid "Search" +msgstr "Cerca" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s resultat" +msgstr[1] "%(counter)s resultats" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s en total" + +msgid "Save as new" +msgstr "Desar com a nou" + +msgid "Save and add another" +msgstr "Desar i afegir-ne un de nou" + +msgid "Save and continue editing" +msgstr "Desar i continuar editant" + +msgid "Save and view" +msgstr "Desa i visualitza" + +msgid "Close" +msgstr "Tanca" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Canvieu el %(model)s seleccionat" + +#, python-format +msgid "Add another %(model)s" +msgstr "Afegeix un altre %(model)s" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Esborra el %(model)s seleccionat" + +#, python-format +msgid "View selected %(model)s" +msgstr "Vista seleccionada %(model)s" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "Gràcies per dedicar temps de qualitat avui a aquesta web." + +msgid "Log in again" +msgstr "Iniciar sessió de nou" + +msgid "Password change" +msgstr "Canvi de contrasenya" + +msgid "Your password was changed." +msgstr "La seva contrasenya ha estat canviada." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Si us plau, introduïu la vostra contrasenya antiga, per seguretat, i tot " +"seguit introduïu la vostra contrasenya nova dues vegades per verificar que " +"l'heu escrita correctament." + +msgid "Change my password" +msgstr "Canviar la meva contrasenya:" + +msgid "Password reset" +msgstr "Restablir contrasenya" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "" +"S'ha canviat la vostra contrasenya. Ara podeu continuar i iniciar sessió." + +msgid "Password reset confirmation" +msgstr "Confirmació de restabliment de contrasenya" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Si us plau, introduïu la vostra nova contrasenya dues vegades, per verificar " +"que l'heu escrita correctament." + +msgid "New password:" +msgstr "Contrasenya nova:" + +msgid "Confirm password:" +msgstr "Confirmar contrasenya:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"L'enllaç de restabliment de contrasenya era invàlid, potser perquè ja s'ha " +"utilitzat. Si us plau, sol·liciteu un nou reestabliment de contrasenya." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Li hem enviat instruccions per establir la seva contrasenya, donat que hi " +"hagi un compte associat al correu introduït. L'hauríeu de rebre en breu." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Si no rebeu un correu, assegureu-vos que heu introduït l'adreça amb la que " +"us vau registrar, i comproveu la vostra carpeta de \"spam\"." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Heu rebut aquest correu perquè vau sol·licitar restablir la contrasenya per " +"al vostre compte d'usuari a %(site_name)s." + +msgid "Please go to the following page and choose a new password:" +msgstr "Si us plau, aneu a la pàgina següent i escolliu una nova contrasenya:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "El vostre nom d'usuari, en cas que l'hagueu oblidat:" + +msgid "Thanks for using our site!" +msgstr "Gràcies per fer ús del nostre lloc!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "L'equip de %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Heu oblidat la vostra contrasenya? Introduïu la vostra adreça de correu " +"electrònic a sota, i us enviarem instruccions per canviar-la." + +msgid "Email address:" +msgstr "Adreça de correu electrònic:" + +msgid "Reset my password" +msgstr "Restablir la meva contrasenya" + +msgid "All dates" +msgstr "Totes les dates" + +#, python-format +msgid "Select %s" +msgstr "Seleccioneu %s" + +#, python-format +msgid "Select %s to change" +msgstr "Seleccioneu %s per modificar" + +#, python-format +msgid "Select %s to view" +msgstr "Selecciona %s per a veure" + +msgid "Date:" +msgstr "Data:" + +msgid "Time:" +msgstr "Hora:" + +msgid "Lookup" +msgstr "Cercar" + +msgid "Currently:" +msgstr "Actualment:" + +msgid "Change:" +msgstr "Canviar:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ckb/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ckb/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..add44d29 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ckb/LC_MESSAGES/django 3.po @@ -0,0 +1,759 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Bakhtawar Barzan, 2021 +# kosar tofiq , 2020 +# pejar hewrami , 2020 +# Swara , 2022 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 07:05+0000\n" +"Last-Translator: Swara , 2022\n" +"Language-Team: Central Kurdish (http://www.transifex.com/django/django/" +"language/ckb/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ckb\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "%(verbose_name_plural)sە هەڵبژێردراوەکان بسڕەوە" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "سەرکەوتووانە %(count)d %(items)sی سڕییەوە." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "ناتوانرێت %(name)s بسڕێتەوە" + +msgid "Are you sure?" +msgstr "ئایا تۆ دڵنیایت؟" + +msgid "Administration" +msgstr "بەڕێوەبەرایەتی" + +msgid "All" +msgstr "هەمووی" + +msgid "Yes" +msgstr "بەڵێ" + +msgid "No" +msgstr "نەخێر" + +msgid "Unknown" +msgstr "نەزانراو" + +msgid "Any date" +msgstr "هەر بەروارێک" + +msgid "Today" +msgstr "ئەمڕۆ" + +msgid "Past 7 days" +msgstr "7 ڕۆژی ڕابردوو" + +msgid "This month" +msgstr "ئەم مانگە" + +msgid "This year" +msgstr "ئەمساڵ" + +msgid "No date" +msgstr "بەروار نییە" + +msgid "Has date" +msgstr "بەرواری هەیە" + +msgid "Empty" +msgstr "بەتاڵ" + +msgid "Not empty" +msgstr "بەتاڵ نییە" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"تکایە %(username)s و تێپەڕەوشە دروستەکە بنوسە بۆ هەژمارێکی ستاف. تێبینی بکە " +"لەوانەیە هەردوو خانەکە دۆخی هەستیار بێت بۆ پیتەکان." + +msgid "Action:" +msgstr "کردار:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "زیادکردنی %(verbose_name)sی تر" + +msgid "Remove" +msgstr "لابردن" + +msgid "Addition" +msgstr "خستنەسەر" + +msgid "Change" +msgstr "گۆڕین" + +msgid "Deletion" +msgstr "سڕینەوە" + +msgid "action time" +msgstr "کاتی کردار" + +msgid "user" +msgstr "بەکارهێنەر" + +msgid "content type" +msgstr "جۆری ناوەڕۆک" + +msgid "object id" +msgstr "ناسنامەی ئۆبجێکت" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "نوێنەرایەتی ئۆبجێکت" + +msgid "action flag" +msgstr "ئاڵای کردار" + +msgid "change message" +msgstr "گۆڕینی پەیام" + +msgid "log entry" +msgstr "ڕاپۆرتی تۆمار" + +msgid "log entries" +msgstr "ڕاپۆرتی تۆمارەکان" + +#, python-format +msgid "Added “%(object)s”." +msgstr "\"%(object)s\"ی زیادکرد." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "\"%(object)s\"— %(changes)s گۆڕدرا" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "\"%(object)s\" سڕایەوە." + +msgid "LogEntry Object" +msgstr "ئۆبجێکتی ڕاپۆرتی تۆمار" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "{name} \"{object}\" زیادکرا." + +msgid "Added." +msgstr "زیادکرا." + +msgid "and" +msgstr "و" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "{fields} بۆ {name} “{object}” گۆڕدرا." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "{fields} گۆڕدرا." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "{name} “{object}” سڕایەوە." + +msgid "No fields changed." +msgstr "هیچ خانەیەک نەگۆڕاوە." + +msgid "None" +msgstr "هیچ" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" +"پەنجە داگرە لەسەر “Control”، یاخود “Command” لەسەر ماک، بۆ هەڵبژاردنی " +"دانەیەک زیاتر." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "{name} “{obj}” بەسەرکەوتوویی زیادکرا." + +msgid "You may edit it again below." +msgstr "دەگونجێت تۆ دووبارە لەخوارەوە دەستکاری بکەیت." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"{name} “{obj}” بەسەرکەوتوویی زیادکرا. دەگونجێت تۆ لە خوارەوە {name}یەکی تر " +"زیادبکەیت." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" +"{name} “{obj}” بەسەرکەوتوویی گۆڕدرا. دەگونجێت تۆ لە خوارەوە دووبارە دەستکاری " +"بکەیتەوە." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" +"{name} “{obj}” بەسەرکەوتوویی زیادکرا. دەگونجێت تۆ لە خوارەوە دووبارە " +"دەستکاری بکەیتەوە." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" +"{name} “{obj}” بەسەرکەوتوویی گۆڕدرا. دەگونجێت تۆ لە خوارەوە {name}یەکی تر " +"زیاد بکەیت." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "{name}ی “{obj}” بەسەرکەوتوویی گۆڕدرا." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"دەبێت بڕگەکان هەڵبژێردرابن تاوەکو کرداریان لەسەر ئەنجام بدەیت. هیچ بڕگەیەک " +"نەگۆڕدراوە." + +msgid "No action selected." +msgstr "هیچ کردارێک هەڵنەبژێردراوە." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "%(name)s\"%(obj)s\" بەسەرکەوتوویی سڕدرایەوە." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "%(name)s بە ناسنامەی \"%(key)s\" بوونی نییە. لەوانەیە سڕدرابێتەوە؟" + +#, python-format +msgid "Add %s" +msgstr "زیادکردنی %s" + +#, python-format +msgid "Change %s" +msgstr "گۆڕینی %s" + +#, python-format +msgid "View %s" +msgstr "بینینی %s" + +msgid "Database error" +msgstr "هەڵەی بنکەدراوە" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s بەسەرکەوتوویی گۆڕدرا." +msgstr[1] "%(count)s %(name)s بەسەرکەوتوویی گۆڕدران." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s هەڵبژێردراوە" +msgstr[1] "هەمووی %(total_count)s هەڵبژێردراون" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 لە %(cnt)s هەڵبژێردراوە" + +#, python-format +msgid "Change history: %s" +msgstr "مێژووی گۆڕین: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(instance)sی %(class_name)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"سڕینەوەی %(instance)sی %(class_name)s پێویستی بە سڕینەوەی ئەم ئۆبجێکتە " +"پەیوەندیدارە پارێزراوانەیە: %(related_objects)s" + +msgid "Django site admin" +msgstr "بەڕێوەبەری پێگەی جەنگۆ" + +msgid "Django administration" +msgstr "بەڕێوەبەرایەتی جەنگۆ" + +msgid "Site administration" +msgstr "بەڕێوەبەرایەتی پێگە" + +msgid "Log in" +msgstr "چوونەژوورەوە" + +#, python-format +msgid "%(app)s administration" +msgstr "بەڕێوەبەرایەتی %(app)s" + +msgid "Page not found" +msgstr "پەڕە نەدۆزرایەوە" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "داوای لێبوردن ئەکەین، بەڵام ناتوانرێت پەڕەی داواکراو بدۆزرێتەوە." + +msgid "Home" +msgstr "ماڵەوە" + +msgid "Server error" +msgstr "هەڵەی ڕاژەکار" + +msgid "Server error (500)" +msgstr "هەڵەی ڕاژەکار (500)" + +msgid "Server Error (500)" +msgstr "هەڵەی ڕاژەکار (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"هەڵەیەک بوونی هەبووە. ڕاپۆرت دراوە بە پێگەی بەڕێوەبەرایەتی لەڕێی ئیمەیڵەوە و " +"دەبێت بەزوویی چاکبکرێت. سوپاس بۆ ئارامگرتنت." + +msgid "Run the selected action" +msgstr "کرداری هەڵبژێردراو جێبەجێ بکە" + +msgid "Go" +msgstr "بڕۆ" + +msgid "Click here to select the objects across all pages" +msgstr "کرتە لێرە بکە بۆ هەڵبژاردنی ئۆبجێکتەکان لە تەواوی هەموو پەڕەکان" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "هەموو %(total_count)s %(module_name)s هەڵبژێرە" + +msgid "Clear selection" +msgstr "پاککردنەوەی هەڵبژاردن" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "مۆدێلەکان لە بەرنامەی %(name)s" + +msgid "Add" +msgstr "زیادکردن" + +msgid "View" +msgstr "بینین" + +msgid "You don’t have permission to view or edit anything." +msgstr "تۆ ڕێگەپێدراو نیت بۆ بینین یان دەستکاری هیچ شتێک." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"سەرەتا، ناوی بەکارهێنەر و تێپەڕەوشە بنوسە. پاشان دەتوانیت دەستکاری زیاتری " +"هەڵبژاردنەکانی بەکارهێنەر بکەیت." + +msgid "Enter a username and password." +msgstr "ناوی بەکارهێنەر و تێپەڕەوشە بنوسە" + +msgid "Change password" +msgstr "گۆڕینی تێپەڕەوشە" + +msgid "Please correct the error below." +msgid_plural "Please correct the errors below." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "تێپەڕەوشەی نوێ بۆ بەکارهێنەری %(username)s بنوسە" + +msgid "Skip to main content" +msgstr "" + +msgid "Welcome," +msgstr "بەخێربێیت،" + +msgid "View site" +msgstr "بینینی پێگە" + +msgid "Documentation" +msgstr "بەڵگەنامە" + +msgid "Log out" +msgstr "چوونەدەرەوە" + +msgid "Breadcrumbs" +msgstr "" + +#, python-format +msgid "Add %(name)s" +msgstr "زیادکردنی %(name)s" + +msgid "History" +msgstr "مێژوو" + +msgid "View on site" +msgstr "بینین لەسەر پێگە" + +msgid "Filter" +msgstr "پاڵاوتن" + +msgid "Clear all filters" +msgstr "پاکردنەوەی هەموو پاڵاوتنەکان" + +msgid "Remove from sorting" +msgstr "لابردن لە ڕیزکردن" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "ڕیزکردنی لە پێشینە: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "ڕیزکردنی پێچەوانە" + +msgid "Toggle theme (current theme: auto)" +msgstr "" + +msgid "Toggle theme (current theme: light)" +msgstr "" + +msgid "Toggle theme (current theme: dark)" +msgstr "" + +msgid "Delete" +msgstr "سڕینەوە" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"سڕینەوەی %(object_name)s ‘%(escaped_object)s‘ دەبێتە هۆی سڕینەوەی ئۆبجێکتی " +"پەیوەندیدار، بەڵام هەژمارەکەت ڕێگەپێدراو نییە بۆ سڕینەوەی ئەم جۆرە " +"ئۆبجێکتانەی تر:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"سڕینەوەی %(object_name)sی ‘%(escaped_object)s‘ پێویستیی بە سڕینەوەی ئەم " +"ئۆبجێکتە پەیوەندیدارە پارێزراوانەیە:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"ئایا تۆ دڵنیایت کە دەتەوێت %(object_name)sی \"%(escaped_object)s\" بسڕیتەوە؟ " +"هەموو ئەم ئۆبجێکتە پەیوەندیدارانەش دەسڕێتەوە:" + +msgid "Objects" +msgstr "ئۆبجێکتەکان" + +msgid "Yes, I’m sure" +msgstr "بەڵێ، من دڵنیام" + +msgid "No, take me back" +msgstr "نەخێر، من بگەڕێنەرەوە دواوە" + +msgid "Delete multiple objects" +msgstr "سڕینەوەی چەندین ئۆبجێکت" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"سڕینەوەی %(objects_name)sی هەڵبژێردراو دەبێتە هۆی سڕینەوەی ئۆبجێکتی " +"پەیوەندیدار، بەڵام هەژمارەکەت ڕێگەپێدراو نییە بۆ سڕینەوەی ئەم جۆرە " +"ئۆبجێکتانەی تر:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"سڕینەوەی %(objects_name)sی هەڵبژێردراو پێویستیی بە سڕینەوەی ئەم ئۆبجێکتە " +"پەیوەندیدارە پارێزراوانەیە:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"ئایا تۆ دڵنیایت دەتەوێت %(objects_name)sی هەڵبژێردراو بسڕیتەوە؟ هەموو ئەم " +"ئۆبجێکت و بڕگە پەیوەندیدارەکانیان دەسڕێنەوە:" + +msgid "Delete?" +msgstr "سڕینەوە؟" + +#, python-format +msgid " By %(filter_title)s " +msgstr "بەپێی %(filter_title)s" + +msgid "Summary" +msgstr "پوختە" + +msgid "Recent actions" +msgstr "کردارە نوێیەکان" + +msgid "My actions" +msgstr "کردارەکانم" + +msgid "None available" +msgstr "هیچ شتيک بەردەست نییە" + +msgid "Unknown content" +msgstr "ناوەڕۆکی نەزانراو" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"هەڵەیەک هەیە لە دامەزراندنی بنکەدراوەکەت. دڵنیاببەرەوە لەوەی خشتە گونجاوەکان " +"دروستکراون، دڵنیاببەرەوە بنکەدراوەکە ئەخوێندرێتەوە لەلایەن بەکارهێنەری " +"گونجاوەوە." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"تۆ ڕەسەنایەتیت هەیە وەکو %(username)s, بەڵام ڕێگەپێدراو نیت بۆ ئەم لاپەڕەیە. " +"دەتەوێت بە هەژمارێکی تر بچیتەژوورەوە؟" + +msgid "Forgotten your password or username?" +msgstr "تێپەڕەوشە یان ناوی بەکارهێنەرەکەت بیرچۆتەوە؟" + +msgid "Toggle navigation" +msgstr "کردنەوەو داخستنی ڕێنیشاندەر" + +msgid "Sidebar" +msgstr "" + +msgid "Start typing to filter…" +msgstr "دەست بکە بە نوسین بۆ پاڵاوتن..." + +msgid "Filter navigation items" +msgstr "بڕگەکانی ڕێنیشاندەر بپاڵێوە" + +msgid "Date/time" +msgstr "بەروار/کات" + +msgid "User" +msgstr "بەکارهێنەر" + +msgid "Action" +msgstr "کردار" + +msgid "entry" +msgid_plural "entries" +msgstr[0] "" +msgstr[1] "" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"ئەم ئۆبجێکتە مێژووی گۆڕانکاری نییە. ڕەنگە لە ڕێگەی بەڕێەوەبەری ئەم پێگەیەوە " +"زیادنەکرابێت." + +msgid "Show all" +msgstr "پیشاندانی هەمووی" + +msgid "Save" +msgstr "پاشەکەوتکردن" + +msgid "Popup closing…" +msgstr "پەنجەرەکە دادەخرێت..." + +msgid "Search" +msgstr "گەڕان" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s ئەنجام" +msgstr[1] "%(counter)s ئەنجام" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)sگشتی" + +msgid "Save as new" +msgstr "پاشەکەوتکردن وەک نوێ" + +msgid "Save and add another" +msgstr "پاشەکەوتی بکەو دانەیەکی تر زیاد بکە" + +msgid "Save and continue editing" +msgstr "پاشەکەوتی بکەو بەردەوامبە لە گۆڕنکاری" + +msgid "Save and view" +msgstr "پاشەکەوتی بکەو پیشانی بدە" + +msgid "Close" +msgstr "داخستن" + +#, python-format +msgid "Change selected %(model)s" +msgstr "هەڵبژێردراوەکان بگۆڕە %(model)s" + +#, python-format +msgid "Add another %(model)s" +msgstr "زیادکردنی %(model)sی تر" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "هەڵبژێردراوەکان بسڕەوە %(model)s" + +#, python-format +msgid "View selected %(model)s" +msgstr "سەیری %(model)s هەڵبژێردراوەکان بکە" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "سوپاس بۆ بەسەربردنی هەندێک کاتێکی ئەمڕۆ لەگەڵ ماڵپەڕەکەدا." + +msgid "Log in again" +msgstr "دووبارە چوونەژوورەوە" + +msgid "Password change" +msgstr "گۆڕینی تێپەڕەوشە" + +msgid "Your password was changed." +msgstr "تێپەڕەوشەت گۆڕدرا." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"لەپێناو پاراستندا تکایە تێپەڕەوشە کۆنەکەت بنووسە، پاشان دووجار تێپەڕەوشە " +"نوێیەکەت بنووسە بۆ ئەوەی بتوانین پشتڕاستی بکەینەوە کە بە دروستی نووسیوتە." + +msgid "Change my password" +msgstr "گۆڕینی تێپەڕەوشەکەم" + +msgid "Password reset" +msgstr "دانانەوەی تێپەڕەوشە" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "تێپەڕەوشەکەت دانرایەوە. لەوانەیە ئێستا بچیتە سەرەوە و بچیتەژوورەوە." + +msgid "Password reset confirmation" +msgstr "دووپاتکردنەوەی دانانەوەی تێپەڕەوشە" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"تکایە دووجار تێپەڕەوشە نوێیەکەت بنوسە، پاشان دەتوانین دڵنیابین کە بە دروستی " +"نوسراوە." + +msgid "New password:" +msgstr "تێپەڕەوشەی نوێ:" + +msgid "Confirm password:" +msgstr "دووپاتکردنەوەی تێپەڕەوشە:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"بەستەری دانانەوەی تێپەڕەوشە نادروست بوو، لەوانەیە لەبەر ئەوەی پێشتر " +"بەکارهاتووە. تکایە داوای دانانەوەی تێپەڕەوشەی نوێ بکە." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"ئێمە ڕێنماییەکانمان بۆ دانانی تێپەڕەوشەکەت ئیمەیڵ بۆت ناردووە، ئەگەر " +"هەژمارێک هەبێت لەگەڵ ئەو ئیمەیڵەی کە نوسیوتە. پێویستە بەم زووانە بەدەستت " +"بگات." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"ئەگەر تۆ نامەی ئەلیکترۆنیت بەدەست نەگەیشت، ئەوا دڵنیا ببەرەوە کە تۆ ئەو " +"ناونیشانەت داخڵکردووە کە پێی تۆمار بوویت، وە فۆڵدەری سپامەکەت بپشکنە." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"تۆ ئەم نامە ئەلیکترۆنیەت بەدەست گەیشتووە لەبەرئەوەی داواکاری دوبارە " +"دانانەوەی تێپەڕە ووشەت کردبوو بۆ هەژماری بەکارهێنەرەکەت لە %(site_name)s." + +msgid "Please go to the following page and choose a new password:" +msgstr "تکایە بڕۆ بۆ پەڕەی دیاریکراو و تێپەڕەوشەیەکی نوێ هەڵبژێرە:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "ناوی بەکارهێنەری تۆ، لە کاتێکدا بیرت چووبێتەوە:" + +msgid "Thanks for using our site!" +msgstr "سوپاس بۆ بەکارهێنانی پێگەکەمان!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "دەستەی %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"تێپەڕەوشەکەت بیرچووەتەوە؟ ئیمەیڵەکەت لەخوارەوە بنوسە، پاشان ئێمە ئێمەیڵی " +"ڕێنمایت بۆ دەنێرین بۆ دانانی دانەیەکی نوێ." + +msgid "Email address:" +msgstr "ناونیشانی ئیمەیڵ:" + +msgid "Reset my password" +msgstr "دانانەوەی تێپەڕەوشەکەم" + +msgid "All dates" +msgstr "هەموو بەروارەکان" + +#, python-format +msgid "Select %s" +msgstr "%s هەڵبژێرە" + +#, python-format +msgid "Select %s to change" +msgstr "%s هەڵبژێرە بۆ گۆڕین" + +#, python-format +msgid "Select %s to view" +msgstr "%s هەڵبژێرە بۆ بینین" + +msgid "Date:" +msgstr "بەروار:" + +msgid "Time:" +msgstr "کات:" + +msgid "Lookup" +msgstr "گەڕان" + +msgid "Currently:" +msgstr "ئێستاکە:" + +msgid "Change:" +msgstr "گۆڕین:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/da/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/da/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..ee5725ec --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/da/LC_MESSAGES/django 3.po @@ -0,0 +1,755 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Christian Joergensen , 2012 +# Dimitris Glezos , 2012 +# Erik Ramsgaard Wognsen , 2020-2023 +# Erik Ramsgaard Wognsen , 2013,2015-2020 +# Finn Gruwier Larsen, 2011 +# Jannis Leidel , 2011 +# valberg , 2014-2015 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 07:05+0000\n" +"Last-Translator: Erik Ramsgaard Wognsen , 2020-2023\n" +"Language-Team: Danish (http://www.transifex.com/django/django/language/da/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: da\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Slet valgte %(verbose_name_plural)s" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d %(items)s blev slettet." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Kan ikke slette %(name)s " + +msgid "Are you sure?" +msgstr "Er du sikker?" + +msgid "Administration" +msgstr "Administration" + +msgid "All" +msgstr "Alle" + +msgid "Yes" +msgstr "Ja" + +msgid "No" +msgstr "Nej" + +msgid "Unknown" +msgstr "Ukendt" + +msgid "Any date" +msgstr "Når som helst" + +msgid "Today" +msgstr "I dag" + +msgid "Past 7 days" +msgstr "De sidste 7 dage" + +msgid "This month" +msgstr "Denne måned" + +msgid "This year" +msgstr "Dette år" + +msgid "No date" +msgstr "Ingen dato" + +msgid "Has date" +msgstr "Har dato" + +msgid "Empty" +msgstr "Tom" + +msgid "Not empty" +msgstr "Ikke tom" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Indtast venligst det korrekte %(username)s og adgangskode for en " +"personalekonto. Bemærk at begge felter kan være versalfølsomme." + +msgid "Action:" +msgstr "Handling" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Tilføj endnu en %(verbose_name)s" + +msgid "Remove" +msgstr "Fjern" + +msgid "Addition" +msgstr "Tilføjelse" + +msgid "Change" +msgstr "Ret" + +msgid "Deletion" +msgstr "Sletning" + +msgid "action time" +msgstr "handlingstid" + +msgid "user" +msgstr "bruger" + +msgid "content type" +msgstr "indholdstype" + +msgid "object id" +msgstr "objekt-ID" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "objekt repr" + +msgid "action flag" +msgstr "handlingsflag" + +msgid "change message" +msgstr "ændringsmeddelelse" + +msgid "log entry" +msgstr "logmeddelelse" + +msgid "log entries" +msgstr "logmeddelelser" + +#, python-format +msgid "Added “%(object)s”." +msgstr "Tilføjede “%(object)s”." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "Ændrede “%(object)s” — %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "Slettede “%(object)s”." + +msgid "LogEntry Object" +msgstr "LogEntry-objekt" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "Tilføjede {name} “{object}”." + +msgid "Added." +msgstr "Tilføjet." + +msgid "and" +msgstr "og" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "Ændrede {fields} for {name} “{object}”." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "Ændrede {fields}." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "Slettede {name} “{object}”." + +msgid "No fields changed." +msgstr "Ingen felter ændret." + +msgid "None" +msgstr "Ingen" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "Hold “Ctrl”, eller “Æbletasten” på Mac, nede for at vælge mere end én." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "{name} “{obj}” blev tilføjet." + +msgid "You may edit it again below." +msgstr "Du kan redigere den/det igen herunder." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"{name} “{obj}” blev tilføjet. Du kan tilføje endnu en/et {name} herunder." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "{name} “{obj}” blev ændret. Du kan redigere den/det igen herunder." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "{name} “{obj}” blev tilføjet. Du kan redigere den/det igen herunder." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" +"{name} “{obj}” blev ændret. Du kan tilføje endnu en/et {name} herunder." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "{name} “{obj}” blev ændret." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Der skal være valgt nogle emner for at man kan udføre handlinger på dem. " +"Ingen emner er blev ændret." + +msgid "No action selected." +msgstr "Ingen handling valgt." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "%(name)s “%(obj)s” blev slettet." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "" +"%(name)s med ID “%(key)s” findes ikke. Måske er objektet blevet slettet?" + +#, python-format +msgid "Add %s" +msgstr "Tilføj %s" + +#, python-format +msgid "Change %s" +msgstr "Ret %s" + +#, python-format +msgid "View %s" +msgstr "Vis %s" + +msgid "Database error" +msgstr "Databasefejl" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s blev ændret." +msgstr[1] "%(count)s %(name)s blev ændret." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s valgt" +msgstr[1] "Alle %(total_count)s valgt" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 af %(cnt)s valgt" + +#, python-format +msgid "Change history: %s" +msgstr "Ændringshistorik: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Sletning af %(class_name)s %(instance)s vil kræve sletning af følgende " +"beskyttede relaterede objekter: %(related_objects)s" + +msgid "Django site admin" +msgstr "Django website-administration" + +msgid "Django administration" +msgstr "Django administration" + +msgid "Site administration" +msgstr "Website-administration" + +msgid "Log in" +msgstr "Log ind" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s administration" + +msgid "Page not found" +msgstr "Siden blev ikke fundet" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Vi beklager, men den ønskede side kunne ikke findes" + +msgid "Home" +msgstr "Hjem" + +msgid "Server error" +msgstr "Serverfejl" + +msgid "Server error (500)" +msgstr "Serverfejl (500)" + +msgid "Server Error (500)" +msgstr "Serverfejl (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Der opstod en fejl. Fejlen er rapporteret til website-administratoren via e-" +"mail, og vil blive rettet hurtigst muligt. Tak for din tålmodighed." + +msgid "Run the selected action" +msgstr "Udfør den valgte handling" + +msgid "Go" +msgstr "Udfør" + +msgid "Click here to select the objects across all pages" +msgstr "Klik her for at vælge objekter på tværs af alle sider" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Vælg alle %(total_count)s %(module_name)s " + +msgid "Clear selection" +msgstr "Ryd valg" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Modeller i applikationen %(name)s" + +msgid "Add" +msgstr "Tilføj" + +msgid "View" +msgstr "Vis" + +msgid "You don’t have permission to view or edit anything." +msgstr "Du har ikke rettigheder til at se eller redigere noget." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Indtast først et brugernavn og en adgangskode. Derefter får du yderligere " +"redigeringsmuligheder." + +msgid "Enter a username and password." +msgstr "Indtast et brugernavn og en adgangskode." + +msgid "Change password" +msgstr "Skift adgangskode" + +msgid "Please correct the error below." +msgid_plural "Please correct the errors below." +msgstr[0] "Ret venligst fejlen herunder." +msgstr[1] "Ret venligst fejlene herunder." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "Indtast en ny adgangskode for brugeren %(username)s." + +msgid "Skip to main content" +msgstr "Gå til hovedindhold" + +msgid "Welcome," +msgstr "Velkommen," + +msgid "View site" +msgstr "Se side" + +msgid "Documentation" +msgstr "Dokumentation" + +msgid "Log out" +msgstr "Log ud" + +msgid "Breadcrumbs" +msgstr "Sti" + +#, python-format +msgid "Add %(name)s" +msgstr "Tilføj %(name)s" + +msgid "History" +msgstr "Historik" + +msgid "View on site" +msgstr "Se på website" + +msgid "Filter" +msgstr "Filtrer" + +msgid "Clear all filters" +msgstr "Nulstil alle filtre" + +msgid "Remove from sorting" +msgstr "Fjern fra sortering" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Sorteringsprioritet: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Skift sortering" + +msgid "Toggle theme (current theme: auto)" +msgstr "Skift tema (nuværende tema: auto)" + +msgid "Toggle theme (current theme: light)" +msgstr "Skift tema (nuværende tema: lyst)" + +msgid "Toggle theme (current theme: dark)" +msgstr "Skift tema (nuværende tema: mørkt)" + +msgid "Delete" +msgstr "Slet" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Hvis du sletter %(object_name)s '%(escaped_object)s', vil du også slette " +"relaterede objekter, men din konto har ikke rettigheder til at slette " +"følgende objekttyper:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Sletning af %(object_name)s ' %(escaped_object)s ' vil kræve sletning af " +"følgende beskyttede relaterede objekter:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Er du sikker på du vil slette %(object_name)s \"%(escaped_object)s\"? Alle " +"de følgende relaterede objekter vil blive slettet:" + +msgid "Objects" +msgstr "Objekter" + +msgid "Yes, I’m sure" +msgstr "Ja, jeg er sikker" + +msgid "No, take me back" +msgstr "Nej, tag mig tilbage" + +msgid "Delete multiple objects" +msgstr "Slet flere objekter" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Sletning af de valgte %(objects_name)s ville resultere i sletning af " +"relaterede objekter, men din konto har ikke tilladelse til at slette " +"følgende typer af objekter:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Sletning af de valgte %(objects_name)s vil kræve sletning af følgende " +"beskyttede relaterede objekter:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Er du sikker på du vil slette de valgte %(objects_name)s? Alle de følgende " +"objekter og deres relaterede emner vil blive slettet:" + +msgid "Delete?" +msgstr "Slet?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " Efter %(filter_title)s " + +msgid "Summary" +msgstr "Sammendrag" + +msgid "Recent actions" +msgstr "Seneste handlinger" + +msgid "My actions" +msgstr "Mine handlinger" + +msgid "None available" +msgstr "Ingen tilgængelige" + +msgid "Unknown content" +msgstr "Ukendt indhold" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Der er noget galt med databaseinstallationen. Kontroller om " +"databasetabellerne er blevet oprettet og at databasen er læsbar for den " +"pågældende bruger." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Du er logget ind som %(username)s, men du har ikke tilladelse til at tilgå " +"denne site. Vil du logge ind med en anden brugerkonto?" + +msgid "Forgotten your password or username?" +msgstr "Har du glemt dit password eller brugernavn?" + +msgid "Toggle navigation" +msgstr "Vis/skjul navigation" + +msgid "Sidebar" +msgstr "Sidebjælke" + +msgid "Start typing to filter…" +msgstr "Skriv for at filtrere…" + +msgid "Filter navigation items" +msgstr "Filtrer navigationsemner" + +msgid "Date/time" +msgstr "Dato/tid" + +msgid "User" +msgstr "Bruger" + +msgid "Action" +msgstr "Funktion" + +msgid "entry" +msgid_plural "entries" +msgstr[0] "post" +msgstr[1] "poster" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"Dette objekt har ingen ændringshistorik. Det blev formentlig ikke tilføjet " +"via dette administrations-site" + +msgid "Show all" +msgstr "Vis alle" + +msgid "Save" +msgstr "Gem" + +msgid "Popup closing…" +msgstr "Popup lukker…" + +msgid "Search" +msgstr "Søg" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s resultat" +msgstr[1] "%(counter)s resultater" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s i alt" + +msgid "Save as new" +msgstr "Gem som ny" + +msgid "Save and add another" +msgstr "Gem og tilføj endnu en" + +msgid "Save and continue editing" +msgstr "Gem og fortsæt med at redigere" + +msgid "Save and view" +msgstr "Gem og vis" + +msgid "Close" +msgstr "Luk" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Redigér valgte %(model)s" + +#, python-format +msgid "Add another %(model)s" +msgstr "Tilføj endnu en %(model)s" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Slet valgte %(model)s" + +#, python-format +msgid "View selected %(model)s" +msgstr "Vis valgte %(model)s" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "Tak for den kvalitetstid du brugte på websitet i dag." + +msgid "Log in again" +msgstr "Log ind igen" + +msgid "Password change" +msgstr "Skift adgangskode" + +msgid "Your password was changed." +msgstr "Din adgangskode blev ændret." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Indtast venligst din gamle adgangskode for en sikkerheds skyld og indtast så " +"din nye adgangskode to gange, så vi kan være sikre på, at den er indtastet " +"korrekt." + +msgid "Change my password" +msgstr "Skift min adgangskode" + +msgid "Password reset" +msgstr "Nulstil adgangskode" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Din adgangskode er blevet sat. Du kan logge ind med den nu." + +msgid "Password reset confirmation" +msgstr "Bekræftelse for nulstilling af adgangskode" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Indtast venligst din nye adgangskode to gange, så vi kan være sikre på, at " +"den er indtastet korrekt." + +msgid "New password:" +msgstr "Ny adgangskode:" + +msgid "Confirm password:" +msgstr "Bekræft ny adgangskode:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Linket for nulstilling af adgangskoden er ugyldigt, måske fordi det allerede " +"har været brugt. Anmod venligst påny om nulstilling af adgangskoden." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Vi har sendt dig en e-mail med instruktioner for at indstille din " +"adgangskode, hvis en konto med den angivne e-mail-adresse findes. Du burde " +"modtage den snarest." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Hvis du ikke modtager en e-mail, så tjek venligst, at du har indtastet den e-" +"mail-adresse, du registrerede dig med, og tjek din spam-mappe." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Du modtager denne e-mail, fordi du har anmodet om en nulstilling af " +"adgangskoden til din brugerkonto ved %(site_name)s ." + +msgid "Please go to the following page and choose a new password:" +msgstr "Gå venligst til denne side og vælg en ny adgangskode:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "Hvis du skulle have glemt dit brugernavn er det:" + +msgid "Thanks for using our site!" +msgstr "Tak fordi du brugte vores website!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "Med venlig hilsen %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Har du glemt din adgangskode? Skriv din e-mail-adresse herunder, så sender " +"vi dig instruktioner i at vælge en ny adgangskode." + +msgid "Email address:" +msgstr "E-mail-adresse:" + +msgid "Reset my password" +msgstr "Nulstil min adgangskode" + +msgid "All dates" +msgstr "Alle datoer" + +#, python-format +msgid "Select %s" +msgstr "Vælg %s" + +#, python-format +msgid "Select %s to change" +msgstr "Vælg %s, der skal ændres" + +#, python-format +msgid "Select %s to view" +msgstr "Vælg %s, der skal vises" + +msgid "Date:" +msgstr "Dato:" + +msgid "Time:" +msgstr "Tid:" + +msgid "Lookup" +msgstr "Slå op" + +msgid "Currently:" +msgstr "Nuværende:" + +msgid "Change:" +msgstr "Ændring:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/dsb/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/dsb/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..d93b01a9 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/dsb/LC_MESSAGES/django 3.po @@ -0,0 +1,760 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Michael Wolf , 2016-2023 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 07:05+0000\n" +"Last-Translator: Michael Wolf , 2016-2023\n" +"Language-Team: Lower Sorbian (http://www.transifex.com/django/django/" +"language/dsb/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: dsb\n" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || " +"n%100==4 ? 2 : 3);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Wubrane %(verbose_name_plural)s lašowaś" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d %(items)s su se wulašowali." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "%(name)s njedajo se lašowaś" + +msgid "Are you sure?" +msgstr "Sćo se wěsty?" + +msgid "Administration" +msgstr "Administracija" + +msgid "All" +msgstr "Wšykne" + +msgid "Yes" +msgstr "Jo" + +msgid "No" +msgstr "Ně" + +msgid "Unknown" +msgstr "Njeznaty" + +msgid "Any date" +msgstr "Někaki datum" + +msgid "Today" +msgstr "Źinsa" + +msgid "Past 7 days" +msgstr "Zachadne 7 dnjow" + +msgid "This month" +msgstr "Toś ten mjasec" + +msgid "This year" +msgstr "W tom lěśe" + +msgid "No date" +msgstr "Žeden datum" + +msgid "Has date" +msgstr "Ma datum" + +msgid "Empty" +msgstr "Prozny" + +msgid "Not empty" +msgstr "Njeprozny" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Pšosym zapódajśo korektne %(username)s a gronidło za personalne konto. " +"Źiwajśo na to, až wobej póli móžotej mjazy wjeliko- a małopisanim rozeznawaś." + +msgid "Action:" +msgstr "Akcija:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Dalšne %(verbose_name)s pśidaś" + +msgid "Remove" +msgstr "Wótpóraś" + +msgid "Addition" +msgstr "Pśidanje" + +msgid "Change" +msgstr "Změniś" + +msgid "Deletion" +msgstr "Wulašowanje" + +msgid "action time" +msgstr "akciski cas" + +msgid "user" +msgstr "wužywaŕ" + +msgid "content type" +msgstr "wopśimjeśowy typ" + +msgid "object id" +msgstr "objektowy id" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "objektowa reprezentacija" + +msgid "action flag" +msgstr "akciske markěrowanje" + +msgid "change message" +msgstr "změnowa powěźeńka" + +msgid "log entry" +msgstr "protokolowy zapisk" + +msgid "log entries" +msgstr "protokolowe zapiski" + +#, python-format +msgid "Added “%(object)s”." +msgstr "„%(object)s“ pśidane." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "„%(object)s“ změnjone - %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "„%(object)s“ wulašowane." + +msgid "LogEntry Object" +msgstr "Objekt LogEntry" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "{name} „{object}“ pśidany." + +msgid "Added." +msgstr "Pśidany." + +msgid "and" +msgstr "a" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "{fields} za {name} „{object}“ změnjone." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "{fields} změnjone." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "Deleted {name} „{object}“ wulašowane." + +msgid "No fields changed." +msgstr "Žedne póla změnjone." + +msgid "None" +msgstr "Žeden" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "´Źaržćo „ctrl“ abo „cmd“ na Mac tłocony, aby wusej jadnogo wubrał." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "{name} „{obj}“ jo se wuspěšnje pśidał." + +msgid "You may edit it again below." +msgstr "Móźośo dołojce znowego wobźěłaś." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"{name} „{obj}“ jo se wuspěšnje pśidał. Móžośo dołojce dalšne {name} pśidaś." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" +"{name} „{obj}“ jo se wuspěšnje změnił. Móžośo jen dołojce znowego wobźěłowaś." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" +"{name} „{obj}“ jo se wuspěšnje pśidał. Móžośo jen dołojce znowego wobźěłowaś." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" +"{name} „{obj}“ jo se wuspěšnje změnił. Móžośo dołojce dalšne {name} pśidaś." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "{name} „{obj}“ jo se wuspěšnje změnił." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Zapiski muse se wubraś, aby akcije na nje nałožowało. Zapiski njejsu se " +"změnili." + +msgid "No action selected." +msgstr "Žedna akcija wubrana." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "%(name)s „%(obj)s“ jo se wuspěšnje wulašował." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "%(name)s z ID „%(key)s“ njeeksistěrujo. Jo se snaź wulašowało?" + +#, python-format +msgid "Add %s" +msgstr "%s pśidaś" + +#, python-format +msgid "Change %s" +msgstr "%s změniś" + +#, python-format +msgid "View %s" +msgstr "%s pokazaś" + +msgid "Database error" +msgstr "Zmólka datoweje banki" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s jo se wuspěšnje změnił." +msgstr[1] "%(count)s %(name)s stej se wuspěšnje změniłej." +msgstr[2] "%(count)s %(name)s su se wuspěšnje změnili." +msgstr[3] "%(count)s %(name)s jo se wuspěšnje změniło." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s wubrany" +msgstr[1] "Wšykne %(total_count)s wubranej" +msgstr[2] "Wšykne %(total_count)s wubrane" +msgstr[3] "Wšykne %(total_count)s wubranych" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 z %(cnt)s wubranych" + +#, python-format +msgid "Change history: %s" +msgstr "Změnowa historija: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Aby se %(class_name)s %(instance)s lašowało, muse se slědujuce šćitane " +"objekty lašowaś: %(related_objects)s" + +msgid "Django site admin" +msgstr "Administrator sedła Django" + +msgid "Django administration" +msgstr "Administracija Django" + +msgid "Site administration" +msgstr "Sedłowa administracija" + +msgid "Log in" +msgstr "Pśizjawiś" + +#, python-format +msgid "%(app)s administration" +msgstr "Administracija %(app)s" + +msgid "Page not found" +msgstr "Bok njejo se namakał" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Jo nam luto, ale pominany bok njedajo se namakaś." + +msgid "Home" +msgstr "Startowy bok" + +msgid "Server error" +msgstr "Serwerowa zmólka" + +msgid "Server error (500)" +msgstr "Serwerowa zmólka (500)" + +msgid "Server Error (500)" +msgstr "Serwerowa zmólka (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Zmólka jo nastała. Jo se sedłowym administratoram pśez e-mail k wěsći dała a " +"by dejała se skóro wótpóraś. Źěkujom se za wašu sćerpmosć." + +msgid "Run the selected action" +msgstr "Wubranu akciju wuwjasć" + +msgid "Go" +msgstr "Start" + +msgid "Click here to select the objects across all pages" +msgstr "Klikniśo how, aby objekty wšych bokow wubrał" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Wubjeŕśo wšykne %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "Wuběrk lašowaś" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Modele w nałoženju %(name)s" + +msgid "Add" +msgstr "Pśidaś" + +msgid "View" +msgstr "Pokazaś" + +msgid "You don’t have permission to view or edit anything." +msgstr "Njamaśo pšawo něco pokazaś abo wobźěłaś" + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Zapódajśo nejpjerwjej wužywarske mě a gronidło. Pótom móžośo dalšne " +"wužywarske nastajenja wobźěłowaś." + +msgid "Enter a username and password." +msgstr "Zapódajśo wužywarske mě a gronidło." + +msgid "Change password" +msgstr "Gronidło změniś" + +msgid "Please correct the error below." +msgid_plural "Please correct the errors below." +msgstr[0] "Pšosym korigěrujśo slědujucu zmólku." +msgstr[1] "Pšosym korigěrujśo slědujucej zmólce." +msgstr[2] "Pšosym korigěrujśo slědujuce zmólki." +msgstr[3] "Pšosym korigěrujśo slědujuce zmólki." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "Zapódajśo nowe gronidło za wužywarja %(username)s." + +msgid "Skip to main content" +msgstr "Dalej ku głownemu wopśimjeśeju" + +msgid "Welcome," +msgstr "Witajśo," + +msgid "View site" +msgstr "Sedło pokazaś" + +msgid "Documentation" +msgstr "Dokumentacija" + +msgid "Log out" +msgstr "Wótzjawiś" + +msgid "Breadcrumbs" +msgstr "Klěbowe srjodki" + +#, python-format +msgid "Add %(name)s" +msgstr "%(name)s pśidaś" + +msgid "History" +msgstr "Historija" + +msgid "View on site" +msgstr "Na sedle pokazaś" + +msgid "Filter" +msgstr "Filtrowaś" + +msgid "Clear all filters" +msgstr "Wšykne filtry lašowaś" + +msgid "Remove from sorting" +msgstr "Ze sortěrowanja wótpóraś" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Sortěrowański rěd: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Sortěrowanje pśešaltowaś" + +msgid "Toggle theme (current theme: auto)" +msgstr "Drastwu změniś (auto)" + +msgid "Toggle theme (current theme: light)" +msgstr "Drastwu změniś (swětły)" + +msgid "Toggle theme (current theme: dark)" +msgstr "Drastwu změniś (śamny)" + +msgid "Delete" +msgstr "Lašowaś" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Gaž se %(object_name)s '%(escaped_object)s' lašujo, se pśisłušne objekty " +"wulašuju, ale wašo konto njama pšawo slědujuce typy objektow lašowaś: " + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Aby se %(object_name)s '%(escaped_object)s' lašujo, muse se slědujuce " +"šćitane pśisłušne objekty lašowaś:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Cośo napšawdu %(object_name)s „%(escaped_object)s“ lašowaś? Wšykne slědujuce " +"pśisłušne zapiski se wulašuju: " + +msgid "Objects" +msgstr "Objekty" + +msgid "Yes, I’m sure" +msgstr "Jo, som se wěsty" + +msgid "No, take me back" +msgstr "Ně, pšosym slědk" + +msgid "Delete multiple objects" +msgstr "Někotare objekty lašowaś" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Gaž lašujośo wubrany %(objects_name)s, se pśisłušne objekty wulašuju, ale " +"wašo konto njama pšawo slědujuce typy objektow lašowaś: " + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Aby wubrany %(objects_name)s lašowało, muse se slědujuce šćitane pśisłušne " +"objekty lašowaś:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Cośo napšawdu wubrany %(objects_name)s lašowaś? Wšykne slědujuce objekty a " +"jich pśisłušne zapiski se wulašuju:" + +msgid "Delete?" +msgstr "Lašowaś?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " Pó %(filter_title)s " + +msgid "Summary" +msgstr "Zespominanje" + +msgid "Recent actions" +msgstr "Nejnowše akcije" + +msgid "My actions" +msgstr "Móje akcije" + +msgid "None available" +msgstr "Žeden k dispoziciji" + +msgid "Unknown content" +msgstr "Njeznate wopśimjeśe" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Něco jo z wašeju instalaciju datoweje banki kśiwje šło. Pśeznańśo se, až " +"wótpowědne tabele datoweje banki su se napórali a pótom, až datowa banka " +"dajo se wót wótpówědnego wužywarja cytaś." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Sćo ako %(username)s awtentificěrowany, ale njamaśo pśistup na toś ten bok. " +"Cośo se pla drugego konta pśizjawiś?" + +msgid "Forgotten your password or username?" +msgstr "Sćo swójo gronidło abo wužywarske mě zabył?" + +msgid "Toggle navigation" +msgstr "Nawigaciju pśešaltowaś" + +msgid "Sidebar" +msgstr "Bocnica" + +msgid "Start typing to filter…" +msgstr "Pišćo, aby filtrował …" + +msgid "Filter navigation items" +msgstr "Nawigaciske zapiski filtrowaś" + +msgid "Date/time" +msgstr "Datum/cas" + +msgid "User" +msgstr "Wužywaŕ" + +msgid "Action" +msgstr "Akcija" + +msgid "entry" +msgid_plural "entries" +msgstr[0] "zapisk" +msgstr[1] "zapiska" +msgstr[2] "zapiski" +msgstr[3] "zapiskow" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"Toś ten objekt njama změnowu historiju. Jo se nejskerjej pśez toś to " +"administratorowe sedło pśidał." + +msgid "Show all" +msgstr "Wšykne pokazaś" + +msgid "Save" +msgstr "Składowaś" + +msgid "Popup closing…" +msgstr "Wuskokujuce wokno se zacynja…" + +msgid "Search" +msgstr "Pytaś" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s wuslědk" +msgstr[1] "%(counter)s wuslědka" +msgstr[2] "%(counter)s wuslědki" +msgstr[3] "%(counter)s wuslědkow" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s dogromady" + +msgid "Save as new" +msgstr "Ako nowy składowaś" + +msgid "Save and add another" +msgstr "Składowaś a dalšny pśidaś" + +msgid "Save and continue editing" +msgstr "Składowaś a dalej wobźěłowaś" + +msgid "Save and view" +msgstr "Składowaś a pokazaś" + +msgid "Close" +msgstr "Zacyniś" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Wubrane %(model)s změniś" + +#, python-format +msgid "Add another %(model)s" +msgstr "Dalšny %(model)s pśidaś" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Wubrane %(model)s lašowaś" + +#, python-format +msgid "View selected %(model)s" +msgstr "Wubrany %(model)s pokazaś" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "" +"Wjeliki źěk, až sćo sebje brał źinsa cas za pśeglědowanje kwality websedła." + +msgid "Log in again" +msgstr "Hyšći raz pśizjawiś" + +msgid "Password change" +msgstr "Gronidło změniś" + +msgid "Your password was changed." +msgstr "Wašo gronidło jo se změniło." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Pšosym zapódajśo k swójej wěstośe swójo stare gronidło a pótom swójo nowe " +"gronidło dwójcy, aby my mógli pśeglědowaś, lěc sćo jo korektnje zapisał." + +msgid "Change my password" +msgstr "Mójo gronidło změniś" + +msgid "Password reset" +msgstr "Gronidło jo se slědk stajiło" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Wašo gronidło jo se póstajiło. Móžośo pókšacowaś a se něnto pśizjawiś." + +msgid "Password reset confirmation" +msgstr "Wobkšuśenje slědkstajenja gronidła" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Pšosym zapódajśo swójo nowe gronidło dwójcy, aby my mógli pśeglědowaś, lěc " +"sći jo korektnje zapisał." + +msgid "New password:" +msgstr "Nowe gronidło:" + +msgid "Confirm password:" +msgstr "Gronidło wobkšuśiś:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Wótkaz za slědkstajenje gronidła jo njepłaśiwy był, snaź dokulaž jo se južo " +"wužył. Pšosym pšosćo wó nowe slědkstajenje gronidła." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Smy wam instrukcije za nastajenje wašogo gronidła pśez e-mail pósłali, jolic " +"konto ze zapódaneju e-mailoweju adresu eksistěrujo. Wy by dejał ju skóro " +"dostaś." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Jolic mejlku njedostawaśo, pśeznańśo se, až sćo adresu zapódał, z kótarejuž " +"sćo zregistrěrował, a pśeglědajśo swój spamowy zarědnik." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Dostawaśo toś tu mejlku, dokulaž sćo za swójo wužywarske konto na " +"%(site_name)s wó slědkstajenje gronidła pšosył." + +msgid "Please go to the following page and choose a new password:" +msgstr "Pšosym źiśo k slědujucemu bokoju a wubjeŕśo nowe gronidło:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "Wašo wužywarske mě, jolic sćo jo zabył:" + +msgid "Thanks for using our site!" +msgstr "Wjeliki źěk za wužywanje našogo sedła!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "Team %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Sćo swójo gronidło zabył? Zapódajśo dołojce swóju e-mailowu adresu a " +"pósćelomy wam instrukcije za nastajenje nowego gronidła pśez e-mail." + +msgid "Email address:" +msgstr "E-mailowa adresa:" + +msgid "Reset my password" +msgstr "Mójo gronidło slědk stajiś" + +msgid "All dates" +msgstr "Wšykne daty" + +#, python-format +msgid "Select %s" +msgstr "%s wubraś" + +#, python-format +msgid "Select %s to change" +msgstr "%s wubraś, aby se změniło" + +#, python-format +msgid "Select %s to view" +msgstr "%s wubraś, kótaryž ma se pokazaś" + +msgid "Date:" +msgstr "Datum:" + +msgid "Time:" +msgstr "Cas:" + +msgid "Lookup" +msgstr "Pytanje" + +msgid "Currently:" +msgstr "Tuchylu:" + +msgid "Change:" +msgstr "Změniś:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/eo/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/eo/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..909ae8ae --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/eo/LC_MESSAGES/django 3.po @@ -0,0 +1,728 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Batist D 🐍 , 2012-2013 +# Batist D 🐍 , 2013-2019 +# Claude Paroz , 2016 +# Dinu Gherman , 2011 +# kristjan , 2012 +# Matthieu Desplantes , 2021 +# Meiyer , 2022 +# Nikolay Korotkiy , 2017 +# Adamo Mesha , 2012 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:10-0500\n" +"PO-Revision-Date: 2022-05-25 07:05+0000\n" +"Last-Translator: Meiyer , 2022\n" +"Language-Team: Esperanto (http://www.transifex.com/django/django/language/" +"eo/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: eo\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Forigi elektitajn %(verbose_name_plural)sn" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Sukcese forigis %(count)d %(items)s." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Ne povas forigi %(name)s" + +msgid "Are you sure?" +msgstr "Ĉu vi certas?" + +msgid "Administration" +msgstr "Administrado" + +msgid "All" +msgstr "Ĉio" + +msgid "Yes" +msgstr "Jes" + +msgid "No" +msgstr "Ne" + +msgid "Unknown" +msgstr "Nekonata" + +msgid "Any date" +msgstr "Ajna dato" + +msgid "Today" +msgstr "Hodiaŭ" + +msgid "Past 7 days" +msgstr "Lastaj 7 tagoj" + +msgid "This month" +msgstr "Ĉi tiu monato" + +msgid "This year" +msgstr "Ĉi tiu jaro" + +msgid "No date" +msgstr "Neniu dato" + +msgid "Has date" +msgstr "Havas daton" + +msgid "Empty" +msgstr "Malplena" + +msgid "Not empty" +msgstr "Ne malplena" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Bonvolu enigi la ĝustajn %(username)sn kaj pasvorton por personara konto. " +"Notu, ke ambaŭ kampoj povas esti uskleco-distingaj." + +msgid "Action:" +msgstr "Ago:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Aldoni alian %(verbose_name)sn" + +msgid "Remove" +msgstr "Forigi" + +msgid "Addition" +msgstr "Aldono" + +msgid "Change" +msgstr "Ŝanĝi" + +msgid "Deletion" +msgstr "Forviŝo" + +msgid "action time" +msgstr "aga tempo" + +msgid "user" +msgstr "uzanto" + +msgid "content type" +msgstr "enhava tipo" + +msgid "object id" +msgstr "objekta identigaĵo" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "objekta prezento" + +msgid "action flag" +msgstr "aga marko" + +msgid "change message" +msgstr "ŝanĝmesaĝo" + +msgid "log entry" +msgstr "protokolero" + +msgid "log entries" +msgstr "protokoleroj" + +#, python-format +msgid "Added “%(object)s”." +msgstr "Aldono de “%(object)s”" + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "Ŝanĝo de “%(object)s” — %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "Forigo de “%(object)s”" + +msgid "LogEntry Object" +msgstr "Protokolera objekto" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "Aldonita(j) {name} “{object}”." + +msgid "Added." +msgstr "Aldonita." + +msgid "and" +msgstr "kaj" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "Ŝanĝita(j) {fields} por {name} “{object}”." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "Ŝanĝita(j) {fields}." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "Forigita(j) {name} “{object}”." + +msgid "No fields changed." +msgstr "Neniu kampo ŝanĝita." + +msgid "None" +msgstr "Neniu" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "La {name} “{obj}” estis sukcese aldonita(j)." + +msgid "You may edit it again below." +msgstr "Eblas redakti ĝin sube." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Elementoj devas esti elektitaj por agi je ili. Neniu elemento estis ŝanĝita." + +msgid "No action selected." +msgstr "Neniu ago elektita." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "La %(name)s “%(obj)s” estis sukcese forigita(j)." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "" + +#, python-format +msgid "Add %s" +msgstr "Aldoni %sn" + +#, python-format +msgid "Change %s" +msgstr "Ŝanĝi %s" + +#, python-format +msgid "View %s" +msgstr "Vidi %sn" + +msgid "Database error" +msgstr "Datumbaza eraro" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s estis sukcese ŝanĝita." +msgstr[1] "%(count)s %(name)s estis sukcese ŝanĝitaj." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s elektitaj" +msgstr[1] "Ĉiuj %(total_count)s elektitaj" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 el %(cnt)s elektita" + +#, python-format +msgid "Change history: %s" +msgstr "Ŝanĝa historio: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Forigi la %(class_name)s-n “%(instance)s” postulus forigi la sekvajn " +"protektitajn rilatajn objektojn: %(related_objects)s" + +msgid "Django site admin" +msgstr "Dĵanga reteja administrado" + +msgid "Django administration" +msgstr "Dĵanga administrado" + +msgid "Site administration" +msgstr "Reteja administrado" + +msgid "Log in" +msgstr "Ensaluti" + +#, python-format +msgid "%(app)s administration" +msgstr "Administrado de %(app)s" + +msgid "Page not found" +msgstr "Paĝo ne trovita" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Bedaŭrinde la petita paĝo ne estis trovita." + +msgid "Home" +msgstr "Ĉefpaĝo" + +msgid "Server error" +msgstr "Servila eraro" + +msgid "Server error (500)" +msgstr "Servila eraro (500)" + +msgid "Server Error (500)" +msgstr "Servila eraro (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" + +msgid "Run the selected action" +msgstr "Lanĉi la elektitan agon" + +msgid "Go" +msgstr "Ek" + +msgid "Click here to select the objects across all pages" +msgstr "Klaku ĉi-tie por elekti la objektojn trans ĉiuj paĝoj" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Elekti ĉiuj %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "Viŝi elekton" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Modeloj en la aplikaĵo “%(name)s”" + +msgid "Add" +msgstr "Aldoni" + +msgid "View" +msgstr "Vidi" + +msgid "You don’t have permission to view or edit anything." +msgstr "" + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" + +msgid "Enter a username and password." +msgstr "Enigu salutnomon kaj pasvorton." + +msgid "Change password" +msgstr "Ŝanĝi pasvorton" + +msgid "Please correct the error below." +msgstr "Bonvolu ĝustigi la eraron sube." + +msgid "Please correct the errors below." +msgstr "Bonvolu ĝustigi la erarojn sube." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "Enigu novan pasvorton por la uzanto %(username)s." + +msgid "Welcome," +msgstr "Bonvenon," + +msgid "View site" +msgstr "Vidi retejon" + +msgid "Documentation" +msgstr "Dokumentaro" + +msgid "Log out" +msgstr "Elsaluti" + +#, python-format +msgid "Add %(name)s" +msgstr "Aldoni %(name)sn" + +msgid "History" +msgstr "Historio" + +msgid "View on site" +msgstr "Vidi sur retejo" + +msgid "Filter" +msgstr "Filtri" + +msgid "Clear all filters" +msgstr "" + +msgid "Remove from sorting" +msgstr "Forigi el ordigado" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Ordiga prioritato: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Ŝalti ordigadon" + +msgid "Delete" +msgstr "Forigi" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Foriganti la %(object_name)s '%(escaped_object)s' rezultus en foriganti " +"rilatajn objektojn, sed via konto ne havas permeson por forigi la sekvantajn " +"tipojn de objektoj:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Forigi la %(object_name)s '%(escaped_object)s' postulus forigi la sekvajn " +"protektitajn rilatajn objektojn:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Ĉu vi certas, ke vi volas forigi %(object_name)s \"%(escaped_object)s\"? " +"Ĉiuj el la sekvaj rilataj eroj estos forigitaj:" + +msgid "Objects" +msgstr "Objektoj" + +msgid "Yes, I’m sure" +msgstr "Jes, mi certas" + +msgid "No, take me back" +msgstr "Ne, reen" + +msgid "Delete multiple objects" +msgstr "Forigi plurajn objektojn" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Forigi la %(objects_name)s rezultus en forigi rilatajn objektojn, sed via " +"konto ne havas permeson por forigi la sekvajn tipojn de objektoj:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Forigi la %(objects_name)s postulus forigi la sekvajn protektitajn rilatajn " +"objektojn:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Ĉu vi certas, ke vi volas forigi la elektitajn %(objects_name)s? Ĉiuj el la " +"sekvaj objektoj kaj iliaj rilataj eroj estos forigita:" + +msgid "Delete?" +msgstr "Forviŝi?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " Laŭ %(filter_title)s " + +msgid "Summary" +msgstr "Resumo" + +msgid "Recent actions" +msgstr "Lastaj agoj" + +msgid "My actions" +msgstr "Miaj agoj" + +msgid "None available" +msgstr "Neniu disponebla" + +msgid "Unknown content" +msgstr "Nekonata enhavo" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Vi estas aŭtentikigita kiel %(username)s, sed ne havas permeson aliri tiun " +"paĝon. Ĉu vi ŝatus ensaluti per alia konto?" + +msgid "Forgotten your password or username?" +msgstr "Ĉu vi forgesis vian pasvorton aŭ vian salutnomon?" + +msgid "Toggle navigation" +msgstr "Ŝalti navigadon" + +msgid "Start typing to filter…" +msgstr "" + +msgid "Filter navigation items" +msgstr "" + +msgid "Date/time" +msgstr "Dato/horo" + +msgid "User" +msgstr "Uzanto" + +msgid "Action" +msgstr "Ago" + +msgid "entry" +msgstr "" + +msgid "entries" +msgstr "" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"Ĉi tiu objekto ne havas historion de ŝanĝoj. Ĝi verŝajne ne estis aldonita " +"per ĉi tiu administrejo." + +msgid "Show all" +msgstr "Montri ĉion" + +msgid "Save" +msgstr "Konservi" + +msgid "Popup closing…" +msgstr "Ŝprucfenesto fermiĝas…" + +msgid "Search" +msgstr "Serĉu" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s resulto" +msgstr[1] "%(counter)s rezultoj" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s entute" + +msgid "Save as new" +msgstr "Konservi kiel novan" + +msgid "Save and add another" +msgstr "Konservi kaj aldoni alian" + +msgid "Save and continue editing" +msgstr "Konservi kaj daŭre redakti" + +msgid "Save and view" +msgstr "Konservi kaj vidi" + +msgid "Close" +msgstr "Fermi" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Redaktu elektitan %(model)sn" + +#, python-format +msgid "Add another %(model)s" +msgstr "Aldoni alian %(model)sn" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Forigi elektitan %(model)sn" + +#, python-format +msgid "View selected %(model)s" +msgstr "" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "" + +msgid "Log in again" +msgstr "Ensaluti denove" + +msgid "Password change" +msgstr "Pasvorta ŝanĝo" + +msgid "Your password was changed." +msgstr "Via pasvorto estis sukcese ŝanĝita." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Bonvolu entajpi vian malnovan pasvorton pro sekureco, kaj entajpi vian novan " +"pasvorton dufoje, por ke ni estu certaj, ke vi tajpis ĝin ĝuste." + +msgid "Change my password" +msgstr "Ŝanĝi mian passvorton" + +msgid "Password reset" +msgstr "Pasvorta rekomencigo" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Via pasvorto estis ŝanĝita. Vi povas ensaluti nun." + +msgid "Password reset confirmation" +msgstr "Konfirmo de restarigo de pasvorto" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Bonvolu entajpi vian novan pasvorton dufoje, tiel ni povas konfirmi ke vi " +"ĝuste tajpis ĝin." + +msgid "New password:" +msgstr "Nova pasvorto:" + +msgid "Confirm password:" +msgstr "Konfirmi pasvorton:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"La ligilo por restarigi pasvorton estis malvalida, eble ĉar ĝi jam estis " +"uzita. Bonvolu denove peti restarigon de pasvorto." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Ni sendis al vi instrukciojn por starigi vian pasvorton, se ekzistas konto " +"kun la retadreso, kiun vi provizis. Vi devus ricevi ilin post mallonge." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Se vi ne ricevas retmesaĝon, bonvole certiĝu ke vi entajpis la adreson per " +"kiu vi registriĝis, kaj kontrolu en via spamujo." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Vi ricevis ĉi tiun retpoŝton ĉar vi petis pasvortan rekomencigon por via " +"uzanta konto ĉe %(site_name)s." + +msgid "Please go to the following page and choose a new password:" +msgstr "Bonvolu iri al la sekvanta paĝo kaj elekti novan pasvorton:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "Via uzantnomo, se vi forgesis ĝin:" + +msgid "Thanks for using our site!" +msgstr "Dankon pro uzo de nia retejo!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "La %(site_name)s teamo" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Ĉu vi forgesis vian pasvorton? Entajpu vian retpoŝtadreson sube kaj ni " +"sendos al vi retpoŝte instrukciojn por ŝanĝi ĝin." + +msgid "Email address:" +msgstr "Retpoŝto:" + +msgid "Reset my password" +msgstr "Rekomencigi mian pasvorton" + +msgid "All dates" +msgstr "Ĉiuj datoj" + +#, python-format +msgid "Select %s" +msgstr "Elekti %sn" + +#, python-format +msgid "Select %s to change" +msgstr "Elekti %sn por ŝanĝi" + +#, python-format +msgid "Select %s to view" +msgstr "Elektu %sn por vidi" + +msgid "Date:" +msgstr "Dato:" + +msgid "Time:" +msgstr "Horo:" + +msgid "Lookup" +msgstr "Trarigardo" + +msgid "Currently:" +msgstr "Nuntempe:" + +msgid "Change:" +msgstr "Ŝanĝo:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/et/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/et/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..4a961f3c --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/et/LC_MESSAGES/django 3.po @@ -0,0 +1,753 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# eallik , 2011 +# Erlend , 2020 +# Jannis Leidel , 2011 +# Janno Liivak , 2013-2015 +# Martin , 2015,2022-2023 +# Martin , 2016,2019-2020 +# Marti Raudsepp , 2016 +# Ragnar Rebase , 2019 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 07:05+0000\n" +"Last-Translator: Martin , 2015,2022-2023\n" +"Language-Team: Estonian (http://www.transifex.com/django/django/language/" +"et/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: et\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Kustuta valitud %(verbose_name_plural)s" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d %(items)s kustutamine õnnestus." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Ei saa kustutada %(name)s" + +msgid "Are you sure?" +msgstr "Kas olete kindel?" + +msgid "Administration" +msgstr "Administreerimine" + +msgid "All" +msgstr "Kõik" + +msgid "Yes" +msgstr "Jah" + +msgid "No" +msgstr "Ei" + +msgid "Unknown" +msgstr "Tundmatu" + +msgid "Any date" +msgstr "Suvaline kuupäev" + +msgid "Today" +msgstr "Täna" + +msgid "Past 7 days" +msgstr "Viimased 7 päeva" + +msgid "This month" +msgstr "Käesolev kuu" + +msgid "This year" +msgstr "Käesolev aasta" + +msgid "No date" +msgstr "Kuupäev puudub" + +msgid "Has date" +msgstr "Kuupäev olemas" + +msgid "Empty" +msgstr "Tühi" + +msgid "Not empty" +msgstr "Mitte tühi" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Palun sisestage personali kontole õige %(username)s ja parool. Teadke, et " +"mõlemad väljad võivad olla tõstutundlikud." + +msgid "Action:" +msgstr "Toiming:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Lisa veel üks %(verbose_name)s" + +msgid "Remove" +msgstr "Eemalda" + +msgid "Addition" +msgstr "Lisamine" + +msgid "Change" +msgstr "Muuda" + +msgid "Deletion" +msgstr "Kustutamine" + +msgid "action time" +msgstr "toimingu aeg" + +msgid "user" +msgstr "kasutaja" + +msgid "content type" +msgstr "sisutüüp" + +msgid "object id" +msgstr "objekti id" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "objekti esitus" + +msgid "action flag" +msgstr "toimingu lipp" + +msgid "change message" +msgstr "muudatuse tekst" + +msgid "log entry" +msgstr "logisissekanne" + +msgid "log entries" +msgstr "logisissekanded" + +#, python-format +msgid "Added “%(object)s”." +msgstr "Lisati “%(object)s”." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "Muudeti “%(object)s” — %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "Kustutati “%(object)s.”" + +msgid "LogEntry Object" +msgstr "Objekt LogEntry" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "Lisati {name} “{object}”." + +msgid "Added." +msgstr "Lisatud." + +msgid "and" +msgstr "ja" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "Muudeti {fields} -> {name} “{object}”." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "Muudetud {fields}." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "Kustutati {name} “{object}”." + +msgid "No fields changed." +msgstr "Ühtegi välja ei muudetud." + +msgid "None" +msgstr "Puudub" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "Hoia all “Control” või “Command” Macil, et valida rohkem kui üks." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "{name} “{obj}” lisamine õnnestus." + +msgid "You may edit it again below." +msgstr "Võite seda uuesti muuta." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"{name} “{obj}” lisamine õnnestus. Allpool saate lisada järgmise {name}." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "{name} “{obj}” muutmine õnnestus. Allpool saate seda uuesti muuta." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "{name} “{obj}” lisamine õnnestus. Allpool saate seda uuesti muuta." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "{name} ”{obj}” muutmine õnnestus. Allpool saate lisada uue {name}." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "{name} “{obj}” muutmine õnnestus." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Palun märgistage elemendid, millega soovite toiminguid sooritada. Ühtegi " +"elementi ei muudetud." + +msgid "No action selected." +msgstr "Toiming valimata." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "%(name)s “%(obj)s” kustutamine õnnestus." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "%(name)s ID-ga “%(key)s” ei eksisteeri. Võib-olla on see kustutatud?" + +#, python-format +msgid "Add %s" +msgstr "Lisa %s" + +#, python-format +msgid "Change %s" +msgstr "Muuda %s" + +#, python-format +msgid "View %s" +msgstr "Vaata %s" + +msgid "Database error" +msgstr "Andmebaasi viga" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s muutmine õnnestus." +msgstr[1] "%(count)s %(name)s muutmine õnnestus." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s valitud" +msgstr[1] "Kõik %(total_count)s valitud" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "valitud 0/%(cnt)s" + +#, python-format +msgid "Change history: %s" +msgstr "Muudatuste ajalugu: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Et kustutada %(class_name)s %(instance)s, on vaja kustutada järgmised " +"kaitstud seotud objektid: %(related_objects)s" + +msgid "Django site admin" +msgstr "Django administreerimisliides" + +msgid "Django administration" +msgstr "Django administreerimisliides" + +msgid "Site administration" +msgstr "Saidi administreerimine" + +msgid "Log in" +msgstr "Sisene" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s administreerimine" + +msgid "Page not found" +msgstr "Lehte ei leitud" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Vabandame, kuid soovitud lehte ei leitud." + +msgid "Home" +msgstr "Kodu" + +msgid "Server error" +msgstr "Serveri viga" + +msgid "Server error (500)" +msgstr "Serveri viga (500)" + +msgid "Server Error (500)" +msgstr "Serveri Viga (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Ilmnes viga. Sellest on e-posti teel teavitatud lehe administraatorit ja " +"viga parandatakse esimesel võimalusel. Täname kannatlikkuse eest." + +msgid "Run the selected action" +msgstr "Käivita valitud toiming" + +msgid "Go" +msgstr "Mine" + +msgid "Click here to select the objects across all pages" +msgstr "Kliki siin, et märgistada objektid üle kõigi lehekülgede" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Märgista kõik %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "Tühjenda valik" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Rakenduse %(name)s moodulid" + +msgid "Add" +msgstr "Lisa" + +msgid "View" +msgstr "Vaata" + +msgid "You don’t have permission to view or edit anything." +msgstr "Teil pole õigust midagi vaadata ega muuta." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Kõigepealt sisestage kasutajatunnus ja salasõna. Seejärel saate muuta " +"täiendavaid kasutajaandmeid." + +msgid "Enter a username and password." +msgstr "Sisestage kasutajanimi ja salasõna." + +msgid "Change password" +msgstr "Muuda salasõna" + +msgid "Please correct the error below." +msgid_plural "Please correct the errors below." +msgstr[0] "Palun parandage allolev viga." +msgstr[1] "Palun parandage allolevad vead." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "Sisestage uus salasõna kasutajale %(username)s" + +msgid "Skip to main content" +msgstr "" + +msgid "Welcome," +msgstr "Tere tulemast," + +msgid "View site" +msgstr "Vaata saiti" + +msgid "Documentation" +msgstr "Dokumentatsioon" + +msgid "Log out" +msgstr "Logi välja" + +msgid "Breadcrumbs" +msgstr "" + +#, python-format +msgid "Add %(name)s" +msgstr "Lisa %(name)s" + +msgid "History" +msgstr "Ajalugu" + +msgid "View on site" +msgstr "Näita lehel" + +msgid "Filter" +msgstr "Filtreeri" + +msgid "Clear all filters" +msgstr "Tühjenda kõik filtrid" + +msgid "Remove from sorting" +msgstr "Eemalda sorteerimisest" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Sorteerimisjärk: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Sorteerimine" + +msgid "Toggle theme (current theme: auto)" +msgstr "" + +msgid "Toggle theme (current theme: light)" +msgstr "" + +msgid "Toggle theme (current theme: dark)" +msgstr "" + +msgid "Delete" +msgstr "Kustuta" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Selleks, et kustutada %(object_name)s '%(escaped_object)s', on vaja " +"kustutada lisaks ka kõik seotud objecktid, aga teil puudub õigus järgnevat " +"tüüpi objektide kustutamiseks:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Et kustutada %(object_name)s '%(escaped_object)s', on vaja kustutada " +"järgmised kaitstud seotud objektid:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Kas olete kindel, et soovite kustutada objekti %(object_name)s " +"\"%(escaped_object)s\"? Kõik järgnevad seotud objektid kustutatakse koos " +"sellega:" + +msgid "Objects" +msgstr "Objektid" + +msgid "Yes, I’m sure" +msgstr "Jah, olen kindel" + +msgid "No, take me back" +msgstr "Ei, mine tagasi" + +msgid "Delete multiple objects" +msgstr "Kustuta mitu objekti" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Kui kustutada valitud %(objects_name)s, peaks kustutama ka seotud objektid, " +"aga sinu kasutajakontol pole õigusi järgmiste objektitüüpide kustutamiseks:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Et kustutada valitud %(objects_name)s, on vaja kustutada ka järgmised " +"kaitstud seotud objektid:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Kas oled kindel, et soovid kustutada valitud %(objects_name)s? Kõik " +"järgnevad objektid ja seotud objektid kustutatakse:" + +msgid "Delete?" +msgstr "Kustutan?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " %(filter_title)s " + +msgid "Summary" +msgstr "Kokkuvõte" + +msgid "Recent actions" +msgstr "Hiljutised toimingud" + +msgid "My actions" +msgstr "Minu toimingud" + +msgid "None available" +msgstr "Ei leitud ühtegi" + +msgid "Unknown content" +msgstr "Tundmatu sisu" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"On tekkinud viga seoses andmebaasiga. Veenduge, et kõik vajalikud " +"andmebaasitabelid on loodud ja andmebaas on loetav vastava kasutaja poolt." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Olete sisse logitud kasutajana %(username)s, kuid teil puudub ligipääs " +"lehele. Kas te soovite teise kontoga sisse logida?" + +msgid "Forgotten your password or username?" +msgstr "Unustasite oma parooli või kasutajanime?" + +msgid "Toggle navigation" +msgstr "Lülita navigeerimine sisse" + +msgid "Sidebar" +msgstr "" + +msgid "Start typing to filter…" +msgstr "" + +msgid "Filter navigation items" +msgstr "" + +msgid "Date/time" +msgstr "Kuupäev/kellaaeg" + +msgid "User" +msgstr "Kasutaja" + +msgid "Action" +msgstr "Toiming" + +msgid "entry" +msgid_plural "entries" +msgstr[0] "sissekanne" +msgstr[1] "sissekanded" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"Sellel objektil puudub muudatuste ajalugu. Tõenäoliselt ei lisatud objekti " +"läbi selle administreerimisliidese." + +msgid "Show all" +msgstr "Näita kõiki" + +msgid "Save" +msgstr "Salvesta" + +msgid "Popup closing…" +msgstr "Hüpikaken sulgub…" + +msgid "Search" +msgstr "Otsing" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s tulemus" +msgstr[1] "%(counter)s tulemust" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "Kokku %(full_result_count)s" + +msgid "Save as new" +msgstr "Salvesta uuena" + +msgid "Save and add another" +msgstr "Salvesta ja lisa uus" + +msgid "Save and continue editing" +msgstr "Salvesta ja jätka muutmist" + +msgid "Save and view" +msgstr "Salvesta ja vaata" + +msgid "Close" +msgstr "Sulge" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Muuda valitud %(model)s" + +#, python-format +msgid "Add another %(model)s" +msgstr "Lisa veel üks %(model)s" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Kustuta valitud %(model)s" + +#, python-format +msgid "View selected %(model)s" +msgstr "Vaata valitud %(model)s" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "Tänan, et veetsite aega meie lehel." + +msgid "Log in again" +msgstr "Logi uuesti sisse" + +msgid "Password change" +msgstr "Salasõna muutmine" + +msgid "Your password was changed." +msgstr "Teie salasõna on vahetatud." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Turvalisuse tagamiseks palun sisestage oma praegune salasõna ja seejärel uus " +"salasõna. Veendumaks, et uue salasõna sisestamisel ei tekkinud vigu, palun " +"sisestage see kaks korda." + +msgid "Change my password" +msgstr "Muuda salasõna" + +msgid "Password reset" +msgstr "Uue parooli loomine" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Teie salasõna on määratud. Võite nüüd sisse logida." + +msgid "Password reset confirmation" +msgstr "Uue salasõna loomise kinnitamine" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Palun sisestage uus salasõna kaks korda, et saaksime veenduda, et " +"sisestamisel ei tekkinud vigu." + +msgid "New password:" +msgstr "Uus salasõna:" + +msgid "Confirm password:" +msgstr "Kinnita salasõna:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Uue salasõna loomise link ei olnud korrektne. Võimalik, et seda on varem " +"kasutatud. Esitage uue salasõna taotlus uuesti." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Saatsime teile meilile parooli muutmise juhendi. Kui teie poolt sisestatud e-" +"posti aadressiga konto on olemas, siis jõuab kiri peagi kohale." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Kui te ei saa kirja kätte siis veenduge, et sisestasite just selle e-posti " +"aadressi, millega registreerisite. Kontrollige ka oma rämpsposti kausta." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Saite käesoleva kirja kuna soovisite muuta lehel %(site_name)s oma " +"kasutajakontoga seotud parooli." + +msgid "Please go to the following page and choose a new password:" +msgstr "Palun minge järmisele lehele ning sisestage uus salasõna" + +msgid "Your username, in case you’ve forgotten:" +msgstr "Teie kasutajatunnus juhuks, kui olete unustanud:" + +msgid "Thanks for using our site!" +msgstr "Täname meie lehte külastamast!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "%(site_name)s meeskond" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Unustasite oma salasõna? Sisestage oma e-posti aadress ja saadame meilile " +"juhised uue saamiseks." + +msgid "Email address:" +msgstr "E-posti aadress:" + +msgid "Reset my password" +msgstr "Reseti parool" + +msgid "All dates" +msgstr "Kõik kuupäevad" + +#, python-format +msgid "Select %s" +msgstr "Vali %s" + +#, python-format +msgid "Select %s to change" +msgstr "Vali %s mida muuta" + +#, python-format +msgid "Select %s to view" +msgstr "Vali %s vaatamiseks" + +msgid "Date:" +msgstr "Kuupäev:" + +msgid "Time:" +msgstr "Aeg:" + +msgid "Lookup" +msgstr "Otsi" + +msgid "Currently:" +msgstr "Hetkel:" + +msgid "Change:" +msgstr "Muuda:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/eu/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/eu/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..79c00b18 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/eu/LC_MESSAGES/django 3.po @@ -0,0 +1,732 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Aitzol Naberan , 2013,2016 +# Eneko Illarramendi , 2017-2019,2022 +# Jannis Leidel , 2011 +# julen, 2012-2013 +# julen, 2013 +# Urtzi Odriozola , 2017 +# Yoaira García , 2021 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:10-0500\n" +"PO-Revision-Date: 2022-07-25 07:05+0000\n" +"Last-Translator: Eneko Illarramendi \n" +"Language-Team: Basque (http://www.transifex.com/django/django/language/eu/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: eu\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Ezabatu aukeratutako %(verbose_name_plural)s" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d %(items)s elementu ezabatu dira." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Ezin da %(name)s ezabatu" + +msgid "Are you sure?" +msgstr "Ziur al zaude?" + +msgid "Administration" +msgstr "Kudeaketa" + +msgid "All" +msgstr "Dena" + +msgid "Yes" +msgstr "Bai" + +msgid "No" +msgstr "Ez" + +msgid "Unknown" +msgstr "Ezezaguna" + +msgid "Any date" +msgstr "Edozein data" + +msgid "Today" +msgstr "Gaur" + +msgid "Past 7 days" +msgstr "Aurreko 7 egunak" + +msgid "This month" +msgstr "Hilabete hau" + +msgid "This year" +msgstr "Urte hau" + +msgid "No date" +msgstr "Datarik ez" + +msgid "Has date" +msgstr "Data dauka" + +msgid "Empty" +msgstr "Hutsik" + +msgid "Not empty" +msgstr "" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Idatzi kudeaketa gunerako %(username)s eta pasahitz zuzena. Kontuan izan " +"biek maiuskula/minuskulak desberdintzen dituztela." + +msgid "Action:" +msgstr "Ekintza:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Gehitu beste %(verbose_name)s bat" + +msgid "Remove" +msgstr "Kendu" + +msgid "Addition" +msgstr "Gehitzea" + +msgid "Change" +msgstr "Aldatu" + +msgid "Deletion" +msgstr "Ezabatzea" + +msgid "action time" +msgstr "Ekintza hordua" + +msgid "user" +msgstr "erabiltzailea" + +msgid "content type" +msgstr "eduki mota" + +msgid "object id" +msgstr "objetuaren id-a" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "objeturaren adierazpena" + +msgid "action flag" +msgstr "Ekintza botoia" + +msgid "change message" +msgstr "Mezua aldatu" + +msgid "log entry" +msgstr "Log sarrera" + +msgid "log entries" +msgstr "log sarrerak" + +#, python-format +msgid "Added “%(object)s”." +msgstr "\"%(object)s\" gehituta." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "\"%(object)s\" ezabatuta." + +msgid "LogEntry Object" +msgstr "LogEntry objetua" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "{name} \"{object}\" gehituta." + +msgid "Added." +msgstr "Gehituta" + +msgid "and" +msgstr "eta" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "" + +#, python-brace-format +msgid "Changed {fields}." +msgstr "{fields} aldatuta." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "{name} \"{object}\" ezabatuta." + +msgid "No fields changed." +msgstr "Ez da eremurik aldatu." + +msgid "None" +msgstr "Bat ere ez" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" +"Bat baino gehiago hautatzeko, sakatu \"Kontrol\" tekla edo \"Command\" Mac " +"batean." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "" + +msgid "You may edit it again below." +msgstr "Aldaketa gehiago egin ditzazkezu jarraian." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"{name} \"{obj}\" ondo gehitu da. Beste {name} bat gehitu dezakezu jarraian." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" +"{name} \"{obj}\" ondo aldatu da. Aldaketa gehiago egin ditzazkezu jarraian." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" +"{name} \"{obj}\" ondo gehitu da. Aldaketa gehiago egin ditzazkezu jarraian." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Elementuak aukeratu behar dira beraien gain ekintzak burutzeko. Ez da " +"elementurik aldatu." + +msgid "No action selected." +msgstr "Ez dago ekintzarik aukeratuta." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "%(name)s \"%(obj)s\" ondo ezabatu da." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "" +"\"%(key)s\" ID-a duen %(name)s ez da existitzen. Agian ezabatua izan da?" + +#, python-format +msgid "Add %s" +msgstr "Gehitu %s" + +#, python-format +msgid "Change %s" +msgstr "Aldatu %s" + +#, python-format +msgid "View %s" +msgstr "%s ikusi" + +msgid "Database error" +msgstr "Errorea datu-basean" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(name)s %(count)s ondo aldatu da." +msgstr[1] "%(count)s %(name)s ondo aldatu dira." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "Guztira %(total_count)s aukeratuta" +msgstr[1] "Guztira %(total_count)s aukeratuta" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "Guztira %(cnt)s, 0 aukeratuta" + +#, python-format +msgid "Change history: %s" +msgstr "Aldaketen historia: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"%(class_name)s klaseko %(instance)s instantziak ezabatzeak erlazionatutako " +"objektu hauek ezabatzea eragingo du:\n" +"%(related_objects)s" + +msgid "Django site admin" +msgstr "Django kudeaketa gunea" + +msgid "Django administration" +msgstr "Django kudeaketa" + +msgid "Site administration" +msgstr "Webgunearen kudeaketa" + +msgid "Log in" +msgstr "Sartu" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s kudeaketa" + +msgid "Page not found" +msgstr "Ez da orririk aurkitu" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Sentitzen dugu, baina eskatutako orria ezin da aurkitu." + +msgid "Home" +msgstr "Hasiera" + +msgid "Server error" +msgstr "Zerbitzariaren errorea" + +msgid "Server error (500)" +msgstr "Zerbitzariaren errorea (500)" + +msgid "Server Error (500)" +msgstr "Zerbitzariaren errorea (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" + +msgid "Run the selected action" +msgstr "Burutu aukeratutako ekintza" + +msgid "Go" +msgstr "Joan" + +msgid "Click here to select the objects across all pages" +msgstr "Egin klik hemen orri guztietako objektuak aukeratzeko" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Hautatu %(total_count)s %(module_name)s guztiak" + +msgid "Clear selection" +msgstr "Garbitu hautapena" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "%(name)s aplikazioaren modeloak" + +msgid "Add" +msgstr "Gehitu" + +msgid "View" +msgstr "Ikusi" + +msgid "You don’t have permission to view or edit anything." +msgstr "Ez duzu ezer ikusteko edo editatzeko baimenik." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Lehenik, sartu erabiltzailea eta pasahitza bat. Gero, editatzeko aukera " +"gehiago izango dituzu. " + +msgid "Enter a username and password." +msgstr "Sartu erabiltzaile izen eta pasahitz bat." + +msgid "Change password" +msgstr "Aldatu pasahitza" + +msgid "Please correct the error below." +msgstr "Mesedez zuzendu erroreak behean." + +msgid "Please correct the errors below." +msgstr "Mesedez zuzendu erroreak behean." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "" +"Idatzi pasahitz berria %(username)s erabiltzailearentzat." + +msgid "Welcome," +msgstr "Ongi etorri," + +msgid "View site" +msgstr "Webgunea ikusi" + +msgid "Documentation" +msgstr "Dokumentazioa" + +msgid "Log out" +msgstr "Irten" + +#, python-format +msgid "Add %(name)s" +msgstr "Gehitu %(name)s" + +msgid "History" +msgstr "Historia" + +msgid "View on site" +msgstr "Webgunean ikusi" + +msgid "Filter" +msgstr "Iragazkia" + +msgid "Clear all filters" +msgstr "Garbitu filtro guztiak." + +msgid "Remove from sorting" +msgstr "Kendu ordenaziotik" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Ordenatzeko lehentasuna: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Txandakatu ordenazioa" + +msgid "Delete" +msgstr "Ezabatu" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"%(object_name)s ezabatzean bere '%(escaped_object)s' ere ezabatzen dira, " +"baina zure kontuak ez dauka baimenik objetu mota hauek ezabatzeko:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"%(object_name)s '%(escaped_object)s' ezabatzeak erlazionatutako objektu " +"babestu hauek ezabatzea eskatzen du:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Ziur zaude %(object_name)s \"%(escaped_object)s\" ezabatu nahi dituzula? " +"Erlazionaturik dauden hurrengo elementuak ere ezabatuko dira:" + +msgid "Objects" +msgstr "Objetuak" + +msgid "Yes, I’m sure" +msgstr "bai, ziur nago " + +msgid "No, take me back" +msgstr "Ez, itzuli atzera" + +msgid "Delete multiple objects" +msgstr "Ezabatu hainbat objektu" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Aukeratutako %(objects_name)s ezabatzeak erlazionatutako objektuak ezabatzea " +"eskatzen du baina zure kontuak ez dauka baimen nahikorik objektu mota hauek " +"ezabatzeko: " + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Aukeratutako %(objects_name)s ezabatzeak erlazionatutako objektu babestu " +"hauek ezabatzea eskatzen du:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Ziur zaude aukeratutako %(objects_name)s ezabatu nahi duzula? Objektu guzti " +"hauek eta erlazionatutako elementu guztiak ezabatuko dira:" + +msgid "Delete?" +msgstr "Ezabatu?" + +#, python-format +msgid " By %(filter_title)s " +msgstr "Irizpidea: %(filter_title)s" + +msgid "Summary" +msgstr "Laburpena" + +msgid "Recent actions" +msgstr "Azken ekintzak" + +msgid "My actions" +msgstr "Nire ekintzak" + +msgid "None available" +msgstr "Ez dago ezer" + +msgid "Unknown content" +msgstr "Eduki ezezaguna" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"%(username)s bezala autentikatu zara, baina ez daukazu orrialde honetara " +"sarbidea. Nahi al duzu kontu ezberdin batez sartu?" + +msgid "Forgotten your password or username?" +msgstr "Pasahitza edo erabiltzaile-izena ahaztu duzu?" + +msgid "Toggle navigation" +msgstr "" + +msgid "Start typing to filter…" +msgstr "Hasi idazten filtratzeko..." + +msgid "Filter navigation items" +msgstr "" + +msgid "Date/time" +msgstr "Data/ordua" + +msgid "User" +msgstr "Erabiltzailea" + +msgid "Action" +msgstr "Ekintza" + +msgid "entry" +msgstr "sarrera" + +msgid "entries" +msgstr "sarrerak" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" + +msgid "Show all" +msgstr "Erakutsi dena" + +msgid "Save" +msgstr "Gorde" + +msgid "Popup closing…" +msgstr "Popup leihoa ixten..." + +msgid "Search" +msgstr "Bilatu" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "Emaitza %(counter)s " +msgstr[1] "%(counter)s emaitza" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s guztira" + +msgid "Save as new" +msgstr "Gorde berri gisa" + +msgid "Save and add another" +msgstr "Gorde eta beste bat gehitu" + +msgid "Save and continue editing" +msgstr "Gorde eta editatzen jarraitu" + +msgid "Save and view" +msgstr "Gorde eta ikusi" + +msgid "Close" +msgstr "Itxi" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Aldatu aukeratutako %(model)s" + +#, python-format +msgid "Add another %(model)s" +msgstr "Gehitu beste %(model)s" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Ezabatu aukeratutako %(model)s" + +#, python-format +msgid "View selected %(model)s" +msgstr "" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "" + +msgid "Log in again" +msgstr "Hasi saioa berriro" + +msgid "Password change" +msgstr "Aldatu pasahitza" + +msgid "Your password was changed." +msgstr "Zure pasahitza aldatu egin da." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Mesedez, sartu zure pasahitza zaharra segurtasunagatik, gero sartu berria bi " +"aldiz, ondo idatzita dagoela ziurtatzeko. " + +msgid "Change my password" +msgstr "Nire pasahitza aldatu" + +msgid "Password reset" +msgstr "Berrezarri pasahitza" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Zure pasahitza ezarri da. Orain aurrera egin eta sartu zaitezke." + +msgid "Password reset confirmation" +msgstr "Pasahitza berrezartzeko berrespena" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "Idatzi pasahitz berria birritan ondo idatzita dagoela ziurta dezagun." + +msgid "New password:" +msgstr "Pasahitz berria:" + +msgid "Confirm password:" +msgstr "Berretsi pasahitza:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Pasahitza berrezartzeko loturak baliogabea dirudi. Baliteke lotura aurretik " +"erabilita egotea. Eskatu berriro pasahitza berrezartzea." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Zure pasahitza jartzeko aginduak bidali dizkizugu... sartu duzun posta " +"elektronikoarekin konturen bat baldin badago. Laster jasoko dituzu." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Posta elektronikorik jasotzen ez baduzu, ziurtatu erregistratu duzun " +"helbidean sartu zarela, eta zure spam horria begiratu. " + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Mezu hau %(site_name)s webgunean pasahitza berrezartzea eskatu duzulako jaso " +"duzu." + +msgid "Please go to the following page and choose a new password:" +msgstr "Zoaz hurrengo orrira eta aukeratu pasahitz berria:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "Zure erabiltzaile-izena, ahaztu baduzu:" + +msgid "Thanks for using our site!" +msgstr "Mila esker gure webgunea erabiltzeagatik!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "%(site_name)s webguneko taldea" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Pasahitza ahaztu zaizu? Sartu zure helbidea behean, eta berria jartzeko " +"argibideak bidaliko dizkizugu " + +msgid "Email address:" +msgstr "Helbide elektronikoa:" + +msgid "Reset my password" +msgstr "Berrezarri pasahitza" + +msgid "All dates" +msgstr "Data guztiak" + +#, python-format +msgid "Select %s" +msgstr "Aukeratu %s" + +#, python-format +msgid "Select %s to change" +msgstr "Aukeratu %s aldatzeko" + +#, python-format +msgid "Select %s to view" +msgstr "Aukeratu %s ikusteko" + +msgid "Date:" +msgstr "Data:" + +msgid "Time:" +msgstr "Ordua:" + +msgid "Lookup" +msgstr "Lookup" + +msgid "Currently:" +msgstr "Oraingoa:" + +msgid "Change:" +msgstr "Aldatu:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/fa/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/fa/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..9a2187da --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/fa/LC_MESSAGES/django 3.po @@ -0,0 +1,751 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Ahmad Hosseini , 2020 +# Ali Nikneshan , 2015,2020 +# Ali Vakilzade , 2015 +# Aly Ahmady , 2022 +# Amir Ajorloo , 2020 +# Arash Fazeli , 2012 +# Farshad Asadpour, 2021 +# Jannis Leidel , 2011 +# MJafar Mashhadi , 2018 +# Mohammad Hossein Mojtahedi , 2017,2019 +# Pouya Abbassi, 2016 +# rahim agh , 2021 +# Reza Mohammadi , 2013-2014 +# Sajad Rahimi , 2021 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:10-0500\n" +"PO-Revision-Date: 2022-05-25 07:05+0000\n" +"Last-Translator: Aly Ahmady , 2022\n" +"Language-Team: Persian (http://www.transifex.com/django/django/language/" +"fa/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: fa\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "حذف %(verbose_name_plural)s های انتخاب شده" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d تا %(items)s با موفقیت حذف شدند." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "امکان حذف %(name)s نیست." + +msgid "Are you sure?" +msgstr "آیا مطمئن هستید؟" + +msgid "Administration" +msgstr "مدیریت" + +msgid "All" +msgstr "همه" + +msgid "Yes" +msgstr "بله" + +msgid "No" +msgstr "خیر" + +msgid "Unknown" +msgstr "ناشناخته" + +msgid "Any date" +msgstr "هر تاریخی" + +msgid "Today" +msgstr "امروز" + +msgid "Past 7 days" +msgstr "۷ روز اخیر" + +msgid "This month" +msgstr "این ماه" + +msgid "This year" +msgstr "امسال" + +msgid "No date" +msgstr "بدون تاریخ" + +msgid "Has date" +msgstr "دارای تاریخ" + +msgid "Empty" +msgstr "خالی" + +msgid "Not empty" +msgstr "غیر خالی" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"لطفا %(username)s و گذرواژه را برای یک حساب کارمند وارد کنید.\n" +"توجه داشته باشید که ممکن است هر دو به کوچکی و بزرگی حروف حساس باشند." + +msgid "Action:" +msgstr "اقدام:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "افزودن یک %(verbose_name)s دیگر" + +msgid "Remove" +msgstr "حذف" + +msgid "Addition" +msgstr "افزودن" + +msgid "Change" +msgstr "تغییر" + +msgid "Deletion" +msgstr "کاستن" + +msgid "action time" +msgstr "زمان اقدام" + +msgid "user" +msgstr "کاربر" + +msgid "content type" +msgstr "نوع محتوی" + +msgid "object id" +msgstr "شناسهٔ شیء" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "صورت شیء" + +msgid "action flag" +msgstr "نشانه عمل" + +msgid "change message" +msgstr "پیغام تغییر" + +msgid "log entry" +msgstr "مورد اتفاقات" + +msgid "log entries" +msgstr "موارد اتفاقات" + +#, python-format +msgid "Added “%(object)s”." +msgstr "\"%(object)s\" افروده شد." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "تغییر یافت \"%(object)s\" - %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "\"%(object)s\" حدف شد." + +msgid "LogEntry Object" +msgstr "شئ LogEntry" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "{name} \"{object}\" اضافه شد." + +msgid "Added." +msgstr "اضافه شد" + +msgid "and" +msgstr "و" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "{fields} برای {name} \"{object}\" تغییر یافتند." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "{fields} تغییر یافتند." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "{name} \"{object}\" حذف شد." + +msgid "No fields changed." +msgstr "فیلدی تغییر نیافته است." + +msgid "None" +msgstr "هیچ" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" +"برای انتخاب بیش از یکی، کلید \"Control\"، یا \"Command\" روی Mac، را نگه " +"دارید." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "{name} \"{obj}\" با موفقیت اضافه شد." + +msgid "You may edit it again below." +msgstr "می‌توانید مجدداً ویرایش کنید." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"{name} \"{obj}\" با موفقیت اضافه شد. شما میتوانید {name} دیگری در قسمت پایین " +"اضافه کنید." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" +"{name} \"{obj}\" با موفقیت تغییر یافت. شما میتوانید دوباره آنرا در قسمت " +"پایین ویرایش کنید." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" +" {name} \"{obj}\" به موفقیت اضافه شد. شما میتوانید در قسمت پایین، دوباره آن " +"را ویرایش کنید." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" +"{name} \"{obj}\" با موفقیت تغییر یافت. شما میتوانید {name} دیگری در قسمت " +"پایین اضافه کنید." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "{name} \"{obj}\" با موفقیت تغییر یافت." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"آیتم ها باید به منظور انجام عملیات بر روی آنها انتخاب شوند. هیچ آیتمی با " +"تغییر نیافته است." + +msgid "No action selected." +msgstr "فعالیتی انتخاب نشده" + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "%(name)s·\"%(obj)s\" با موفقیت حذف شد." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "%(name)s با کلید «%(key)s» وجود ندارد. ممکن است حذف شده باشد." + +#, python-format +msgid "Add %s" +msgstr "اضافه کردن %s" + +#, python-format +msgid "Change %s" +msgstr "تغییر %s" + +#, python-format +msgid "View %s" +msgstr "مشاهده %s" + +msgid "Database error" +msgstr "خطا در بانک اطلاعاتی" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s با موفقیت تغییر کرد." +msgstr[1] "%(count)s %(name)s با موفقیت تغییر کرد." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "همه موارد %(total_count)s انتخاب شده" +msgstr[1] "همه موارد %(total_count)s انتخاب شده" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 از %(cnt)s انتخاب شده‌اند" + +#, python-format +msgid "Change history: %s" +msgstr "تاریخچهٔ تغییر: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"برای حذف %(class_name)s %(instance)s لازم است اشیای حفاظت شدهٔ زیر هم حذف " +"شوند: %(related_objects)s" + +msgid "Django site admin" +msgstr "مدیریت وب‌گاه Django" + +msgid "Django administration" +msgstr "مدیریت Django" + +msgid "Site administration" +msgstr "مدیریت وب‌گاه" + +msgid "Log in" +msgstr "ورود" + +#, python-format +msgid "%(app)s administration" +msgstr "مدیریت ‎%(app)s‎" + +msgid "Page not found" +msgstr "صفحه یافت نشد" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "شرمنده، صفحه مورد تقاضا یافت نشد." + +msgid "Home" +msgstr "شروع" + +msgid "Server error" +msgstr "خطای سرور" + +msgid "Server error (500)" +msgstr "خطای سرور (500)" + +msgid "Server Error (500)" +msgstr "خطای سرور (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"مشکلی پیش آمده. این مشکل از طریق ایمیل به مدیران وب‌گاه اطلاع داده شد و به " +"زودی اصلاح می‌گردد. از صبر شما متشکریم." + +msgid "Run the selected action" +msgstr "اجرای حرکت انتخاب شده" + +msgid "Go" +msgstr "برو" + +msgid "Click here to select the objects across all pages" +msgstr "برای انتخاب موجودیت‌ها در تمام صفحات اینجا را کلیک کنید" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "انتخاب تمامی %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "لغو انتخاب‌ها" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "مدلها در برنامه %(name)s " + +msgid "Add" +msgstr "اضافه کردن" + +msgid "View" +msgstr "مشاهده" + +msgid "You don’t have permission to view or edit anything." +msgstr "شما اجازهٔ مشاهده یا ویرایش چیزی را ندارید." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"ابتدا یک نام کاربری و گذرواژه وارد کنید. سپس می توانید مشخصات دیگر کاربر را " +"ویرایش کنید." + +msgid "Enter a username and password." +msgstr "یک نام کاربری و رمز عبور را وارد کنید." + +msgid "Change password" +msgstr "تغییر گذرواژه" + +msgid "Please correct the error below." +msgstr "لطفاً خطای زیر را تصحیح کنید." + +msgid "Please correct the errors below." +msgstr "لطفاً خطاهای زیر را تصحیح کنید." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "برای کابر %(username)s یک گذرنامهٔ جدید وارد کنید." + +msgid "Welcome," +msgstr "خوش آمدید،" + +msgid "View site" +msgstr "نمایش وبگاه" + +msgid "Documentation" +msgstr "مستندات" + +msgid "Log out" +msgstr "خروج" + +#, python-format +msgid "Add %(name)s" +msgstr "اضافه‌کردن %(name)s" + +msgid "History" +msgstr "تاریخچه" + +msgid "View on site" +msgstr "مشاهده در وب‌گاه" + +msgid "Filter" +msgstr "فیلتر" + +msgid "Clear all filters" +msgstr "پاک کردن همه فیلترها" + +msgid "Remove from sorting" +msgstr "حذف از مرتب سازی" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "اولویت مرتب‌سازی: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "تعویض مرتب سازی" + +msgid "Delete" +msgstr "حذف" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"حذف %(object_name)s·'%(escaped_object)s' می تواند باعث حذف اشیاء مرتبط شود. " +"اما حساب شما دسترسی لازم برای حذف اشیای از انواع زیر را ندارد:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"حذف %(object_name)s '%(escaped_object)s' نیاز به حذف موجودیت‌های مرتبط محافظت " +"شده ذیل دارد:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"آیا مطمئنید که می‌خواهید %(object_name)s·\"%(escaped_object)s\" را حذف کنید؟ " +"کلیهٔ اشیای مرتبط زیر حذف خواهند شد:" + +msgid "Objects" +msgstr "اشیاء" + +msgid "Yes, I’m sure" +msgstr "بله، مطمئن هستم." + +msgid "No, take me back" +msgstr "نه، من را برگردان" + +msgid "Delete multiple objects" +msgstr "حذف اشیاء متعدد" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"حذف %(objects_name)s انتخاب شده منجر به حذف موجودیت‌های مرتبط خواهد شد، ولی " +"شناسه شما اجازه حذف اینگونه از موجودیت‌های ذیل را ندارد:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"حذف %(objects_name)s انتخاب شده نیاز به حذف موجودیت‌های مرتبط محافظت شده ذیل " +"دارد:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"آیا در خصوص حذف %(objects_name)s انتخاب شده اطمینان دارید؟ تمام موجودیت‌های " +"ذیل به همراه موارد مرتبط با آنها حذف خواهند شد:" + +msgid "Delete?" +msgstr "حذف؟" + +#, python-format +msgid " By %(filter_title)s " +msgstr "براساس %(filter_title)s " + +msgid "Summary" +msgstr "خلاصه" + +msgid "Recent actions" +msgstr "فعالیتهای اخیر" + +msgid "My actions" +msgstr "فعالیتهای من" + +msgid "None available" +msgstr "چیزی در دسترس نیست" + +msgid "Unknown content" +msgstr "محتوا ناشناخته" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"در نصب بانک اطلاعاتی شما مشکلی وجود دارد. مطمئن شوید که جداول مربوطه به " +"درستی ایجاد شده‌اند و اطمینان حاصل کنید که بانک اطلاعاتی توسط کاربر مربوطه " +"قابل خواندن می باشد." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"شما به عنوان %(username)sوارد شده اید. ولی اجازه مشاهده صفحه فوق را نداریدو " +"آیا مایلید با کاربر دیگری وارد شوید؟" + +msgid "Forgotten your password or username?" +msgstr "گذرواژه یا نام کاربری خود را فراموش کرده‌اید؟" + +msgid "Toggle navigation" +msgstr "تعویض جهت یابی" + +msgid "Start typing to filter…" +msgstr "آغار به کار نوشتن برای فیلترکردن ..." + +msgid "Filter navigation items" +msgstr "فیلتر کردن آیتم های مسیریابی" + +msgid "Date/time" +msgstr "تاریخ/ساعت" + +msgid "User" +msgstr "کاربر" + +msgid "Action" +msgstr "عمل" + +msgid "entry" +msgstr "ورودی" + +msgid "entries" +msgstr "ورودی ها" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"این شیء هنوز تاریخچه تغییرات ندارد. ممکن است توسط این وب‌گاه مدیر ساخته نشده " +"باشد." + +msgid "Show all" +msgstr "نمایش همه" + +msgid "Save" +msgstr "ذخیره" + +msgid "Popup closing…" +msgstr "در حال بستن پنجره..." + +msgid "Search" +msgstr "جستجو" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s نتیجه" +msgstr[1] "%(counter)s نتیجه" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "در مجموع %(full_result_count)s تا" + +msgid "Save as new" +msgstr "ذخیره به عنوان جدید" + +msgid "Save and add another" +msgstr "ذخیره و ایجاد یکی دیگر" + +msgid "Save and continue editing" +msgstr "ذخیره و ادامهٔ ویرایش" + +msgid "Save and view" +msgstr "ذخیره و نمایش" + +msgid "Close" +msgstr "بستن" + +#, python-format +msgid "Change selected %(model)s" +msgstr "تغییر دادن %(model)s انتخاب شده" + +#, python-format +msgid "Add another %(model)s" +msgstr "افزدون %(model)s دیگر" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "حذف کردن %(model)s انتخاب شده" + +#, python-format +msgid "View selected %(model)s" +msgstr "نمایش %(model)sهای انتخاب شده" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "" +"از شما ممنون هستیم که زمان با ارزش خود را برای این تارنما امروز صرف کرده اید" + +msgid "Log in again" +msgstr "ورود دوباره" + +msgid "Password change" +msgstr "تغییر گذرواژه" + +msgid "Your password was changed." +msgstr "گذرواژهٔ شما تغییر یافت." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"برای امنیت بیشتر٬ لطفا گذرواژه قدیمی خود را وارد کنید٬ سپس گذرواژه جدیدتان " +"را دوبار وارد کنید تا ما بتوانیم چک کنیم که به درستی تایپ کرده‌اید. " + +msgid "Change my password" +msgstr "تغییر گذرواژهٔ من" + +msgid "Password reset" +msgstr "ایجاد گذرواژهٔ جدید" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "گذرواژهٔ جدیدتان تنظیم شد. اکنون می‌توانید وارد وب‌گاه شوید." + +msgid "Password reset confirmation" +msgstr "تأیید گذرواژهٔ جدید" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"گذرواژهٔ جدیدتان را دوبار وارد کنید تا ما بتوانیم چک کنیم که به درستی تایپ " +"کرده‌اید." + +msgid "New password:" +msgstr "گذرواژهٔ جدید:" + +msgid "Confirm password:" +msgstr "تکرار گذرواژه:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"پیوند ایجاد گذرواژهٔ جدید نامعتبر بود، احتمالاً به این علت که قبلاً از آن " +"استفاده شده است. لطفاً برای یک گذرواژهٔ جدید درخواست دهید." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"دستورالعمل تنظیم گذرواژه را برایتان ایمیل کردیم. اگر با ایمیلی که وارد کردید " +"اکانتی وجود داشته باشد باید به زودی این دستورالعمل‌ها را دریافت کنید." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"اگر ایمیلی دریافت نمی‌کنید، لطفاً بررسی کنید که آدرسی که وارد کرده‌اید همان است " +"که با آن ثبت نام کرده‌اید، و پوشهٔ اسپم خود را نیز چک کنید." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"شما این ایمیل را بخاطر تقاضای تغییر رمز حساب در %(site_name)s. دریافت کرده " +"اید." + +msgid "Please go to the following page and choose a new password:" +msgstr "لطفاً به صفحهٔ زیر بروید و یک گذرواژهٔ جدید انتخاب کنید:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "نام کاربری‌تان، چنانچه احیاناً یادتان رفته است:" + +msgid "Thanks for using our site!" +msgstr "ممنون از استفادهٔ شما از وب‌گاه ما" + +#, python-format +msgid "The %(site_name)s team" +msgstr "گروه %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"گذرواژه خود را فراموش کرده اید؟ آدرس ایمیل خود را وارد کنید و ما مراحل تعیین " +"کلمه عبور جدید را برای شما ایمیل می‌کنیم." + +msgid "Email address:" +msgstr "آدرس ایمیل:" + +msgid "Reset my password" +msgstr "ایجاد گذرواژهٔ جدید" + +msgid "All dates" +msgstr "همهٔ تاریخ‌ها" + +#, python-format +msgid "Select %s" +msgstr "%s انتخاب کنید" + +#, python-format +msgid "Select %s to change" +msgstr "%s را برای تغییر انتخاب کنید" + +#, python-format +msgid "Select %s to view" +msgstr "%s را برای مشاهده انتخاب کنید" + +msgid "Date:" +msgstr "تاریخ:" + +msgid "Time:" +msgstr "زمان:" + +msgid "Lookup" +msgstr "جستجو" + +msgid "Currently:" +msgstr "در حال حاضر:" + +msgid "Change:" +msgstr "تغییر یافته:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ga/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ga/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..d87a66b4 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ga/LC_MESSAGES/django 3.po @@ -0,0 +1,715 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Jannis Leidel , 2011 +# Luke Blaney , 2019 +# Michael Thornhill , 2011-2012,2015 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2019-06-22 21:17+0000\n" +"Last-Translator: Luke Blaney \n" +"Language-Team: Irish (http://www.transifex.com/django/django/language/ga/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ga\n" +"Plural-Forms: nplurals=5; plural=(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : " +"4);\n" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "D'éirigh le scriosadh %(count)d %(items)s." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Ní féidir scriosadh %(name)s " + +msgid "Are you sure?" +msgstr "An bhfuil tú cinnte?" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Scrios %(verbose_name_plural) roghnaithe" + +msgid "Administration" +msgstr "Riarachán" + +msgid "All" +msgstr "Gach" + +msgid "Yes" +msgstr "Tá" + +msgid "No" +msgstr "Níl" + +msgid "Unknown" +msgstr "Gan aithne" + +msgid "Any date" +msgstr "Aon dáta" + +msgid "Today" +msgstr "Inniu" + +msgid "Past 7 days" +msgstr "7 lá a chuaigh thart" + +msgid "This month" +msgstr "Táim cinnte" + +msgid "This year" +msgstr "An blian seo" + +msgid "No date" +msgstr "Gan dáta" + +msgid "Has date" +msgstr "Le dáta" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Cuir isteach an %(username)s agus focal faire ceart le haghaidh cuntas " +"foirne. Tabhair faoi deara go bhféadfadh an dá réimsí a cás-íogair." + +msgid "Action:" +msgstr "Aicsean:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Cuir eile %(verbose_name)s" + +msgid "Remove" +msgstr "Tóg amach" + +msgid "Addition" +msgstr "" + +msgid "Change" +msgstr "Athraigh" + +msgid "Deletion" +msgstr "Scriosadh" + +msgid "action time" +msgstr "am aicsean" + +msgid "user" +msgstr "úsáideoir" + +msgid "content type" +msgstr "" + +msgid "object id" +msgstr "id oibiacht" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "repr oibiacht" + +msgid "action flag" +msgstr "brat an aicsean" + +msgid "change message" +msgstr "teachtaireacht athrú" + +msgid "log entry" +msgstr "loga iontráil" + +msgid "log entries" +msgstr "loga iontrálacha" + +#, python-format +msgid "Added \"%(object)s\"." +msgstr "\"%(object)s\" curtha isteach." + +#, python-format +msgid "Changed \"%(object)s\" - %(changes)s" +msgstr "\"%(object)s\" - %(changes)s aithrithe" + +#, python-format +msgid "Deleted \"%(object)s.\"" +msgstr "\"%(object)s.\" scrioste" + +msgid "LogEntry Object" +msgstr "Oibiacht LogEntry" + +#, python-brace-format +msgid "Added {name} \"{object}\"." +msgstr "{name} curtha leis \"{object}\"." + +msgid "Added." +msgstr "Curtha leis." + +msgid "and" +msgstr "agus" + +#, python-brace-format +msgid "Changed {fields} for {name} \"{object}\"." +msgstr "{fields} athrithe don {name} \"{object}\"." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "{fields} athrithe." + +#, python-brace-format +msgid "Deleted {name} \"{object}\"." +msgstr "{name} scrioste: \"{object}\"." + +msgid "No fields changed." +msgstr "Dada réimse aithraithe" + +msgid "None" +msgstr "Dada" + +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "" +"Coinnigh síos \"Control\", nó \"Command\" ar Mac chun níos mó ná ceann " +"amháin a roghnú." + +#, python-brace-format +msgid "The {name} \"{obj}\" was added successfully." +msgstr "Bhí {name} \"{obj}\" curtha leis go rathúil" + +msgid "You may edit it again below." +msgstr "Thig leat é a athrú arís faoi seo." + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." +msgstr "" +"D'athraigh {name} \"{obj}\" go rathúil.\n" +"Thig leat é a athrú arís faoi seo." + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." +msgstr "" +"D'athraigh {name} \"{obj}\" go rathúil.\n" +"Thig leat {name} eile a chuir leis." + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." +msgstr "D'athraigh {name} \"{obj}\" go rathúil." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Ní mór Míreanna a roghnú chun caingne a dhéanamh orthu. Níl aon mhíreanna a " +"athrú." + +msgid "No action selected." +msgstr "Uimh gníomh roghnaithe." + +#, python-format +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "Bhí %(name)s \"%(obj)s\" scrioste go rathúil." + +#, python-format +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" +msgstr "Níl%(name)s ann le aitheantais \"%(key)s\". B'fhéidir gur scriosadh é?" + +#, python-format +msgid "Add %s" +msgstr "Cuir %s le" + +#, python-format +msgid "Change %s" +msgstr "Aithrigh %s" + +#, python-format +msgid "View %s" +msgstr "Amharc ar %s" + +msgid "Database error" +msgstr "Botún bunachar sonraí" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s athraithe go rathúil" +msgstr[1] "%(count)s %(name)s athraithe go rathúil" +msgstr[2] "%(count)s %(name)s athraithe go rathúil" +msgstr[3] "%(count)s %(name)s athraithe go rathúil" +msgstr[4] "%(count)s %(name)s athraithe go rathúil" + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s roghnaithe" +msgstr[1] "Gach %(total_count)s roghnaithe" +msgstr[2] "Gach %(total_count)s roghnaithe" +msgstr[3] "Gach %(total_count)s roghnaithe" +msgstr[4] "Gach %(total_count)s roghnaithe" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 as %(cnt)s roghnaithe." + +#, python-format +msgid "Change history: %s" +msgstr "Athraigh stáir %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Teastaíodh scriosadh %(class_name)s %(instance)s scriosadh na rudaí a " +"bhaineann leis: %(related_objects)s" + +msgid "Django site admin" +msgstr "Riarthóir suíomh Django" + +msgid "Django administration" +msgstr "Riarachán Django" + +msgid "Site administration" +msgstr "Riaracháin an suíomh" + +msgid "Log in" +msgstr "Logáil isteach" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s riaracháin" + +msgid "Page not found" +msgstr "Ní bhfuarthas an leathanach" + +msgid "We're sorry, but the requested page could not be found." +msgstr "Tá brón orainn, ach ní bhfuarthas an leathanach iarraite." + +msgid "Home" +msgstr "Baile" + +msgid "Server error" +msgstr "Botún freastalaí" + +msgid "Server error (500)" +msgstr "Botún freastalaí (500)" + +msgid "Server Error (500)" +msgstr "Botún Freastalaí (500)" + +msgid "" +"There's been an error. It's been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Tharla earráid. Tuairiscíodh don riarthóirí suíomh tríd an ríomhphost agus " +"ba chóir a shocrú go luath. Go raibh maith agat as do foighne." + +msgid "Run the selected action" +msgstr "Rith an gníomh roghnaithe" + +msgid "Go" +msgstr "Té" + +msgid "Click here to select the objects across all pages" +msgstr "" +"Cliceáil anseo chun na hobiacht go léir a roghnú ar fud gach leathanach" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Roghnaigh gach %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "Scroiseadh modhnóir" + +msgid "" +"First, enter a username and password. Then, you'll be able to edit more user " +"options." +msgstr "" +"Ar dtús, iontráil ainm úsaideoir agus focal faire. Ansin, beidh tú in ann " +"cuir in eagar níos mó roghaí úsaideoira." + +msgid "Enter a username and password." +msgstr "Cuir isteach ainm úsáideora agus focal faire." + +msgid "Change password" +msgstr "Athraigh focal faire" + +msgid "Please correct the error below." +msgstr "Ceartaigh an botún thíos le do thoil." + +msgid "Please correct the errors below." +msgstr "Le do thoil cheartú earráidí thíos." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "" +"Iontráil focal faire nua le hadhaigh an úsaideor %(username)s." + +msgid "Welcome," +msgstr "Fáilte" + +msgid "View site" +msgstr "Breatnaigh ar an suíomh" + +msgid "Documentation" +msgstr "Doiciméadúchán" + +msgid "Log out" +msgstr "Logáil amach" + +#, python-format +msgid "Add %(name)s" +msgstr "Cuir %(name)s le" + +msgid "History" +msgstr "Stair" + +msgid "View on site" +msgstr "Breath ar suíomh" + +msgid "Filter" +msgstr "Scagaire" + +msgid "Remove from sorting" +msgstr "Bain as sórtáil" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Sórtáil tosaíocht: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Toggle sórtáil" + +msgid "Delete" +msgstr "Cealaigh" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Má scriossan tú %(object_name)s '%(escaped_object)s' scriosfaidh oibiachtí " +"gaolta. Ach níl cead ag do cuntas na oibiacht a leanúint a scriosadh:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Bheadh Scriosadh an %(object_name)s '%(escaped_object)s' a cheangal ar an " +"méid seo a leanas a scriosadh nithe cosanta a bhaineann le:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"An bhfuil tú cinnte na %(object_name)s \"%(escaped_object)s\" a scroiseadh?" +"Beidh gach oibiacht a leanúint scroiste freisin:" + +msgid "Objects" +msgstr "Oibiachtaí" + +msgid "Yes, I'm sure" +msgstr "Táim cinnte" + +msgid "No, take me back" +msgstr "Ní hea, tóg ar ais mé" + +msgid "Delete multiple objects" +msgstr "Scrios na réadanna" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Scriosadh an roghnaithe %(objects_name)s a bheadh mar thoradh ar na nithe " +"gaolmhara a scriosadh, ach níl cead do chuntas a scriosadh na cineálacha seo " +"a leanas na cuspóirí:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Teastaíonn scriosadh na %(objects_name)s roghnaithe scriosadh na hoibiacht " +"gaolta cosainte a leanúint:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"An bhfuil tú cinnte gur mian leat a scriosadh %(objects_name)s roghnaithe? " +"Beidh gach ceann de na nithe seo a leanas agus a n-ítimí gaolta scroiste:" + +msgid "View" +msgstr "Amharc ar" + +msgid "Delete?" +msgstr "Cealaigh?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " Trí %(filter_title)s " + +msgid "Summary" +msgstr "Achoimre" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Samhlacha ins an %(name)s iarratais" + +msgid "Add" +msgstr "Cuir le" + +msgid "You don't have permission to view or edit anything." +msgstr "" + +msgid "Recent actions" +msgstr "" + +msgid "My actions" +msgstr "" + +msgid "None available" +msgstr "Dada ar fáil" + +msgid "Unknown content" +msgstr "Inneachair anaithnid" + +msgid "" +"Something's wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Tá rud éigin mícheart le suitéail do bunachar sonraí. Déan cinnte go bhfuil " +"boird an bunachar sonraI cruthaithe cheana, agus déan cinnte go bhfuil do " +"úsaideoir in ann an bunacchar sonraí a léamh." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" + +msgid "Forgotten your password or username?" +msgstr "Dearmad déanta ar do focal faire nó ainm úsaideora" + +msgid "Date/time" +msgstr "Dáta/am" + +msgid "User" +msgstr "Úsaideoir" + +msgid "Action" +msgstr "Aicsean" + +msgid "" +"This object doesn't have a change history. It probably wasn't added via this " +"admin site." +msgstr "" +"Níl stáir aitraithe ag an oibiacht seo agús is dócha ná cuir le tríd an an " +"suíomh riarachán." + +msgid "Show all" +msgstr "Taispéan gach rud" + +msgid "Save" +msgstr "Sábháil" + +msgid "Popup closing…" +msgstr "" + +msgid "Search" +msgstr "Cuardach" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s toradh" +msgstr[1] "%(counter)s torthaí" +msgstr[2] "%(counter)s torthaí" +msgstr[3] "%(counter)s torthaí" +msgstr[4] "%(counter)s torthaí" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s iomlán" + +msgid "Save as new" +msgstr "Sabháil mar nua" + +msgid "Save and add another" +msgstr "Sabháil agus cuir le ceann eile" + +msgid "Save and continue editing" +msgstr "Sábhail agus lean ag cuir in eagar" + +msgid "Save and view" +msgstr "Sabháil agus amharc ar" + +msgid "Close" +msgstr "Druid" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Athraigh roghnaithe %(model)s" + +#, python-format +msgid "Add another %(model)s" +msgstr "Cuir le %(model)s" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Scrios roghnaithe %(model)s" + +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Go raibh maith agat le hadhaigh do cuairt ar an suíomh idirlínn inniú." + +msgid "Log in again" +msgstr "Logáil isteacj arís" + +msgid "Password change" +msgstr "Athrú focal faire" + +msgid "Your password was changed." +msgstr "Bhí do focal faire aithraithe." + +msgid "" +"Please enter your old password, for security's sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Le do thoil, iontráil do sean-focal faire, ar son slándáil, agus ansin " +"iontráil do focal faire dhá uaire cé go mbeimid in ann a seiceal go bhfuil " +"sé scríobhte isteach i gceart." + +msgid "Change my password" +msgstr "Athraigh mo focal faire" + +msgid "Password reset" +msgstr "Athsocraigh focal faire" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Tá do focal faire réidh. Is féidir leat logáil isteach anois." + +msgid "Password reset confirmation" +msgstr "Deimhniú athshocraigh focal faire" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Le do thoil, iontráil do focal faire dhá uaire cé go mbeimid in ann a " +"seiceal go bhfuil sé scríobhte isteach i gceart." + +msgid "New password:" +msgstr "Focal faire nua:" + +msgid "Confirm password:" +msgstr "Deimhnigh focal faire:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Bhí nasc athshocraigh an focal faire mícheart, b'fheidir mar go raibh sé " +"úsaidte cheana. Le do thoil, iarr ar athsocraigh focal faire nua." + +msgid "" +"We've emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" + +msgid "" +"If you don't receive an email, please make sure you've entered the address " +"you registered with, and check your spam folder." +msgstr "" + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" + +msgid "Please go to the following page and choose a new password:" +msgstr "" +"Le do thoil té go dtí an leathanach a leanúint agus roghmaigh focal faire " +"nua:" + +msgid "Your username, in case you've forgotten:" +msgstr "Do ainm úsaideoir, má tá dearmad déanta agat." + +msgid "Thanks for using our site!" +msgstr "Go raibh maith agat le hadhaigh do cuairt!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "Foireann an %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we'll email " +"instructions for setting a new one." +msgstr "" + +msgid "Email address:" +msgstr "Seoladh ríomhphoist:" + +msgid "Reset my password" +msgstr "Athsocraigh mo focal faire" + +msgid "All dates" +msgstr "Gach dáta" + +#, python-format +msgid "Select %s" +msgstr "Roghnaigh %s" + +#, python-format +msgid "Select %s to change" +msgstr "Roghnaigh %s a athrú" + +#, python-format +msgid "Select %s to view" +msgstr "" + +msgid "Date:" +msgstr "Dáta:" + +msgid "Time:" +msgstr "Am:" + +msgid "Lookup" +msgstr "Cuardach" + +msgid "Currently:" +msgstr "Faoi láthair:" + +msgid "Change:" +msgstr "Athraigh:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..7ebcabbe --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/gd/LC_MESSAGES/django 3.po @@ -0,0 +1,754 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# GunChleoc, 2015-2017,2021 +# GunChleoc, 2015 +# GunChleoc, 2015 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-09-21 10:22+0200\n" +"PO-Revision-Date: 2021-10-27 12:57+0000\n" +"Last-Translator: GunChleoc\n" +"Language-Team: Gaelic, Scottish (http://www.transifex.com/django/django/" +"language/gd/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: gd\n" +"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : " +"(n > 2 && n < 20) ? 2 : 3;\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Sguab às na %(verbose_name_plural)s a chaidh a thaghadh" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Chaidh %(count)d %(items)s a sguabadh às." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Chan urrainn dhuinn %(name)s a sguabadh às" + +msgid "Are you sure?" +msgstr "A bheil thu cinnteach?" + +msgid "Administration" +msgstr "Rianachd" + +msgid "All" +msgstr "Na h-uile" + +msgid "Yes" +msgstr "Tha" + +msgid "No" +msgstr "Chan eil" + +msgid "Unknown" +msgstr "Chan eil fhios" + +msgid "Any date" +msgstr "Ceann-là sam bith" + +msgid "Today" +msgstr "An-diugh" + +msgid "Past 7 days" +msgstr "Na 7 làithean seo chaidh" + +msgid "This month" +msgstr "Am mìos seo" + +msgid "This year" +msgstr "Am bliadhna" + +msgid "No date" +msgstr "Gun cheann-là" + +msgid "Has date" +msgstr "Tha ceann-là aige" + +msgid "Empty" +msgstr "Falamh" + +msgid "Not empty" +msgstr "Neo-fhalamh" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Cuir a-steach %(username)s agus facal-faire ceart airson cunntas neach-" +"obrach. Thoir an aire gum bi aire do litrichean mòra ’s beaga air an dà " +"raon, ma dh’fhaoidte." + +msgid "Action:" +msgstr "Gnìomh:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Cuir %(verbose_name)s eile ris" + +msgid "Remove" +msgstr "Thoir air falbh" + +msgid "Addition" +msgstr "Cur ris" + +msgid "Change" +msgstr "Atharraich" + +msgid "Deletion" +msgstr "Sguabadh às" + +msgid "action time" +msgstr "àm a’ ghnìomha" + +msgid "user" +msgstr "cleachdaiche" + +msgid "content type" +msgstr "seòrsa susbainte" + +msgid "object id" +msgstr "id an oibceict" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "riochdachadh oibseict" + +msgid "action flag" +msgstr "bratach a’ ghnìomha" + +msgid "change message" +msgstr "teachdaireachd atharrachaidh" + +msgid "log entry" +msgstr "innteart loga" + +msgid "log entries" +msgstr "innteartan loga" + +#, python-format +msgid "Added “%(object)s”." +msgstr "Chaidh “%(object)s” a chur ris." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "Chaidh “%(object)s” atharrachadh – %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "Chaidh “%(object)s” a sguabadh às." + +msgid "LogEntry Object" +msgstr "Oibseact innteart an loga" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "Chaidh {name} “{object}” a chur ris." + +msgid "Added." +msgstr "Chaidh a chur ris." + +msgid "and" +msgstr "agus" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "Chaidh {fields} atharrachadh airson {name} “{object}”." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "Chaidh {fields} atharrachadh." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "Chaidh {name} “{object}” a sguabadh às." + +msgid "No fields changed." +msgstr "Cha deach raon atharrachadh." + +msgid "None" +msgstr "Chan eil gin" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "Cum sìos “Control” no “Command” air Mac gus iomadh nì a thaghadh." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "Chaidh {name} “{obj}” a chur ris." + +msgid "You may edit it again below." +msgstr "’S urrainn dhut a dheasachadh a-rithist gu h-ìosal." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"Chaidh {name} “%{obj}” a chur ris. ’S urrainn dhut {name} eile a chur ris gu " +"h-ìosal." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" +"Chaidh {name} “{obj}” atharrachadh. ’S urrainn dhut a dheasachadh a-rithist " +"gu h-ìosal." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" +"Chaidh {name} “{obj}” a chur ris. ’S urrainn dhut a dheasachadh a-rithist gu " +"h-ìosal." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" +"Chaidh {name} “{obj}” atharrachadh. ’S urrainn dhut {name} eile a chur ris " +"gu h-ìosal." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "Chaidh {name} “{obj}” atharrachadh." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Feumaidh tu nithean a thaghadh mus dèan thu gnìomh orra. Cha deach nì " +"atharrachadh." + +msgid "No action selected." +msgstr "Cha deach gnìomh a thaghadh." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "Chaidh %(name)s “%(obj)s” a sguabadh às." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "" +"Chan eil %(name)s leis an ID \"%(key)s\" ann. 'S dòcha gun deach a sguabadh " +"às?" + +#, python-format +msgid "Add %s" +msgstr "Cuir %s ris" + +#, python-format +msgid "Change %s" +msgstr "Atharraich %s" + +#, python-format +msgid "View %s" +msgstr "Seall %s" + +msgid "Database error" +msgstr "Mearachd an stòir-dhàta" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "Chaidh %(count)s %(name)s atharrachadh." +msgstr[1] "Chaidh %(count)s %(name)s atharrachadh." +msgstr[2] "Chaidh %(count)s %(name)s atharrachadh." +msgstr[3] "Chaidh %(count)s %(name)s atharrachadh." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "Chaidh %(total_count)s a thaghadh" +msgstr[1] "Chaidh a h-uile %(total_count)s a thaghadh" +msgstr[2] "Chaidh a h-uile %(total_count)s a thaghadh" +msgstr[3] "Chaidh a h-uile %(total_count)s a thaghadh" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "Chaidh 0 à %(cnt)s a thaghadh" + +#, python-format +msgid "Change history: %s" +msgstr "Eachdraidh nan atharraichean: %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Gus %(class_name)s %(instance)s a sguabadh às, bhiodh againn ris na h-" +"oibseactan dàimheach dìonta seo a sguabadh às cuideachd: %(related_objects)s" + +msgid "Django site admin" +msgstr "Rianachd làraich Django" + +msgid "Django administration" +msgstr "Rianachd Django" + +msgid "Site administration" +msgstr "Rianachd na làraich" + +msgid "Log in" +msgstr "Clàraich a-steach" + +#, python-format +msgid "%(app)s administration" +msgstr "Rianachd %(app)s" + +msgid "Page not found" +msgstr "Cha deach an duilleag a lorg" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Tha sinn duilich ach cha do lorg sinn an duilleag a dh’iarr thu." + +msgid "Home" +msgstr "Dhachaigh" + +msgid "Server error" +msgstr "Mearachd an fhrithealaiche" + +msgid "Server error (500)" +msgstr "Mearachd an fhrithealaiche (500)" + +msgid "Server Error (500)" +msgstr "Mearachd an fhrithealaiche (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Chaidh rudeigin cearr. Fhuair rianairean na làraich aithris air a’ phost-d " +"agus tha sinn an dùil gun dèid a chàradh a dh’aithghearr. Mòran taing airson " +"d’ fhoighidinn." + +msgid "Run the selected action" +msgstr "Ruith an gnìomh a thagh thu" + +msgid "Go" +msgstr "Siuthad" + +msgid "Click here to select the objects across all pages" +msgstr "" +"Briog an-seo gus na h-oibseactan a thaghadh air feadh nan duilleagan uile" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Tagh a h-uile %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "Falamhaich an taghadh" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Modailean ann an aplacaid %(name)s" + +msgid "Add" +msgstr "Cuir ris" + +msgid "View" +msgstr "Seall" + +msgid "You don’t have permission to view or edit anything." +msgstr "Chan eil cead agad gus dad a shealltainn no a dheasachadh." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Cuir ainm-cleachdaiche is facal-faire a-steach an toiseach. ’S urrainn dhut " +"barrachd roghainnean a’ chleachdaiche a dheasachadh an uairsin." + +msgid "Enter a username and password." +msgstr "Cuir ainm-cleachdaiche ’s facal-faire a-steach." + +msgid "Change password" +msgstr "Atharraich am facal-faire" + +msgid "Please correct the error below." +msgstr "Feuch an cuir thu a’ mhearachd gu h-ìosal gu ceart." + +msgid "Please correct the errors below." +msgstr "Feuch an cuir thu na mearachdan gu h-ìosal gu ceart." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "" +"Cuir a-steach facal-faire ùr airson a’ chleachdaiche %(username)s." + +msgid "Welcome," +msgstr "Fàilte," + +msgid "View site" +msgstr "Seall an làrach" + +msgid "Documentation" +msgstr "Docamaideadh" + +msgid "Log out" +msgstr "Clàraich a-mach" + +#, python-format +msgid "Add %(name)s" +msgstr "Cuir %(name)s ris" + +msgid "History" +msgstr "An eachdraidh" + +msgid "View on site" +msgstr "Seall e air an làrach" + +msgid "Filter" +msgstr "Criathraich" + +msgid "Clear all filters" +msgstr "Falamhaich gach crithrag" + +msgid "Remove from sorting" +msgstr "Thoir air falbh on t-seòrsachadh" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Prìomhachas an t-seòrsachaidh: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Toglaich an seòrsachadh" + +msgid "Delete" +msgstr "Sguab às" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Nan sguabadh tu às %(object_name)s “%(escaped_object)s”, rachadh oibseactan " +"dàimheach a sguabadh às cuideachd ach chan eil cead aig a’ chunntas agad gus " +"na seòrsaichean de dh’oibseact seo a sguabadh às:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Nan sguabadh tu às %(object_name)s “%(escaped_object)s”, bhiodh againn ris " +"na h-oibseactan dàimheach dìonta seo a sguabadh às cuideachd:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"A bheil thu cinnteach gu bheil thu airson %(object_name)s " +"“%(escaped_object)s” a sguabadh às? Thèid a h-uile nì dàimheach a sguabadh " +"às cuideachd:" + +msgid "Objects" +msgstr "Oibseactan" + +msgid "Yes, I’m sure" +msgstr "Tha mi cinnteach" + +msgid "No, take me back" +msgstr "Chan eil, air ais leam" + +msgid "Delete multiple objects" +msgstr "Sguab às iomadh oibseact" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Nan sguabadh tu às a’ %(objects_name)s a thagh thu, rachadh oibseactan " +"dàimheach a sguabadh às cuideachd ach chan eil cead aig a’ chunntas agad gus " +"na seòrsaichean de dh’oibseact seo a sguabadh às:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Nan sguabadh tu às a’ %(objects_name)s a thagh thu, bhiodh againn ris na h-" +"oibseactan dàimheach dìonta seo a sguabadh às cuideachd:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"A bheil thu cinnteach gu bheil thu airson a’ %(objects_name)s a thagh thu a " +"sguabadh às? Thèid a h-uile oibseact seo ’s na nithean dàimheach aca a " +"sguabadh às:" + +msgid "Delete?" +msgstr "A bheil thu airson a sguabadh às?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " le %(filter_title)s " + +msgid "Summary" +msgstr "Gearr-chunntas" + +msgid "Recent actions" +msgstr "Gnìomhan o chionn goirid" + +msgid "My actions" +msgstr "Na gnìomhan agam" + +msgid "None available" +msgstr "Chan eil gin ann" + +msgid "Unknown content" +msgstr "Susbaint nach aithne dhuinn" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Chaidh rudeigin cearr le stàladh an stòir-dhàta agad. Dèan cinnteach gun " +"deach na clàran stòir-dhàta iomchaidh a chruthachadh agus gur urrainn dhan " +"chleachdaiche iomchaidh an stòr-dàta a leughadh." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Chaidh do dhearbhadh mar %(username)s ach chan eil ùghdarras agad gus an " +"duilleag seo inntrigeadh. Am bu toigh leat clàradh a-steach le cunntas eile?" + +msgid "Forgotten your password or username?" +msgstr "" +"An do dhìochuimhnich thu am facal-faire no an t-ainm-cleachdaiche agad?" + +msgid "Toggle navigation" +msgstr "Toglaich an t-seòladaireachd" + +msgid "Start typing to filter…" +msgstr "Tòisich air sgrìobhadh airson criathradh…" + +msgid "Filter navigation items" +msgstr "Criathraich nithean na seòladaireachd" + +msgid "Date/time" +msgstr "Ceann-là ’s àm" + +msgid "User" +msgstr "Cleachdaiche" + +msgid "Action" +msgstr "Gnìomh" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"Chan eil eachdraidh nan atharraichean aig an oibseact seo. Dh’fhaoidte nach " +"deach a chur ris leis an làrach rianachd seo." + +msgid "Show all" +msgstr "Seall na h-uile" + +msgid "Save" +msgstr "Sàbhail" + +msgid "Popup closing…" +msgstr "Tha a’ phriob-uinneag ’ga dùnadh…" + +msgid "Search" +msgstr "Lorg" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s toradh" +msgstr[1] "%(counter)s thoradh" +msgstr[2] "%(counter)s toraidhean" +msgstr[3] "%(counter)s toradh" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s gu h-iomlan" + +msgid "Save as new" +msgstr "Sàbhail mar fhear ùr" + +msgid "Save and add another" +msgstr "Sàbhail is cuir fear eile ris" + +msgid "Save and continue editing" +msgstr "Sàbhail is deasaich a-rithist" + +msgid "Save and view" +msgstr "Sàbhail is seall" + +msgid "Close" +msgstr "Dùin" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Atharraich a’ %(model)s a thagh thu" + +#, python-format +msgid "Add another %(model)s" +msgstr "Cuir %(model)s eile ris" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Sguab às a’ %(model)s a thagh thu" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "" +"Mòran taing gun do chuir thu seachad deagh-àm air an làrach-lìn an-diugh." + +msgid "Log in again" +msgstr "Clàraich a-steach a-rithist" + +msgid "Password change" +msgstr "Atharrachadh an facail-fhaire" + +msgid "Your password was changed." +msgstr "Chaidh am facal-faire agad atharrachadh." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Cuir a-steach an seann fhacal-faire agad ri linn tèarainteachd agus cuir a-" +"steach am facal-faire ùr agad dà thuras an uairsin ach an dearbhaich sinn " +"nach do rinn thu mearachd sgrìobhaidh." + +msgid "Change my password" +msgstr "Atharraich am facal-faire agam" + +msgid "Password reset" +msgstr "Ath-shuidheachadh an fhacail-fhaire" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "" +"Chaidh am facal-faire agad a shuidheachadh. Faodaidh tu clàradh a-steach a-" +"nis." + +msgid "Password reset confirmation" +msgstr "Dearbhadh air ath-shuidheachadh an fhacail-fhaire" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Cuir a-steach am facal-faire ùr agad dà thuras ach an dearbhaich sinn nach " +"do rinn thu mearachd sgrìobhaidh." + +msgid "New password:" +msgstr "Am facal-faire ùr:" + +msgid "Confirm password:" +msgstr "Dearbhaich am facal-faire:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Bha an ceangal gus am facal-faire ath-suidheachadh mì-dhligheach; ’s dòcha " +"gun deach a chleachdadh mar-thà. Iarr ath-shuidheachadh an fhacail-fhaire às " +"ùr." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Chuir sinn stiùireadh thugad air mar a dh’ath-shuidhicheas tu am facal-faire " +"agad air a’ phost-d dhan chunntas puist-d a chuir thu a-steach. Bu chòir " +"dhut fhaighinn a dh’aithghearr." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Mura faigh thu post-d, dèan cinnteach gun do chuir thu a-steach an seòladh " +"puist-d leis an do chlàraich thu agus thoir sùil air pasgan an spama agad." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Fhuair thu am post-d seo air sgàth ’s gun do dh’iarr thu ath-shuidheachadh " +"an fhacail-fhaire agad airson a’ chunntais cleachdaiche agad air " +"%(site_name)s." + +msgid "Please go to the following page and choose a new password:" +msgstr "Tadhail air an duilleag seo is tagh facal-faire ùr:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "" +"Seo an t-ainm-cleachdaiche agad air eagal ’s gun do dhìochuimhnich thu e:" + +msgid "Thanks for using our site!" +msgstr "Mòran taing airson an làrach againn a chleachdadh!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "Sgioba %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Na dhìochuimhnich thu am facal-faire agad? Cuir a-steach an seòladh puist-d " +"agad gu h-ìosal agus cuiridh sinn stiùireadh thugad gus fear ùr a " +"shuidheachadh air a’ phost-d." + +msgid "Email address:" +msgstr "Seòladh puist-d:" + +msgid "Reset my password" +msgstr "Ath-shuidhich am facal-faire agam" + +msgid "All dates" +msgstr "A h-uile ceann-là" + +#, python-format +msgid "Select %s" +msgstr "Tagh %s" + +#, python-format +msgid "Select %s to change" +msgstr "Tagh %s gus atharrachadh" + +#, python-format +msgid "Select %s to view" +msgstr "Tagh %s gus a shealltainn" + +msgid "Date:" +msgstr "Ceann-là:" + +msgid "Time:" +msgstr "Àm:" + +msgid "Lookup" +msgstr "Lorg" + +msgid "Currently:" +msgstr "An-dràsta:" + +msgid "Change:" +msgstr "Atharrachadh:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..cda734ec --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/he/LC_MESSAGES/django 3.po @@ -0,0 +1,717 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# 534b44a19bf18d20b71ecc4eb77c572f_db336e9 , 2011 +# Jannis Leidel , 2011 +# Meir Kriheli , 2011-2015,2017,2019-2020 +# Menachem G., 2021 +# Yaron Shahrabani , 2020-2021 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-09-21 10:22+0200\n" +"PO-Revision-Date: 2021-11-02 07:48+0000\n" +"Last-Translator: Menachem G.\n" +"Language-Team: Hebrew (http://www.transifex.com/django/django/language/he/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: he\n" +"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % " +"1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "מחק %(verbose_name_plural)s שנבחרו" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d %(items)s נמחקו בהצלחה." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "לא ניתן למחוק %(name)s" + +msgid "Are you sure?" +msgstr "להמשיך?" + +msgid "Administration" +msgstr "ניהול" + +msgid "All" +msgstr "הכול" + +msgid "Yes" +msgstr "כן" + +msgid "No" +msgstr "לא" + +msgid "Unknown" +msgstr "לא ידוע" + +msgid "Any date" +msgstr "כל תאריך" + +msgid "Today" +msgstr "היום" + +msgid "Past 7 days" +msgstr "בשבוע האחרון" + +msgid "This month" +msgstr "החודש" + +msgid "This year" +msgstr "השנה" + +msgid "No date" +msgstr "ללא תאריך" + +msgid "Has date" +msgstr "עם תאריך" + +msgid "Empty" +msgstr "ריק" + +msgid "Not empty" +msgstr "לא ריק" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"נא להזין את %(username)s והסיסמה הנכונים לחשבון איש צוות. נא לשים לב כי שני " +"השדות רגישים לאותיות גדולות/קטנות." + +msgid "Action:" +msgstr "פעולה" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "הוספת %(verbose_name)s" + +msgid "Remove" +msgstr "להסיר" + +msgid "Addition" +msgstr "הוספה" + +msgid "Change" +msgstr "שינוי" + +msgid "Deletion" +msgstr "מחיקה" + +msgid "action time" +msgstr "זמן פעולה" + +msgid "user" +msgstr "משתמש" + +msgid "content type" +msgstr "סוג תוכן" + +msgid "object id" +msgstr "מזהה אובייקט" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "ייצוג אובייקט" + +msgid "action flag" +msgstr "דגל פעולה" + +msgid "change message" +msgstr "הערה לשינוי" + +msgid "log entry" +msgstr "רישום יומן" + +msgid "log entries" +msgstr "רישומי יומן" + +#, python-format +msgid "Added “%(object)s”." +msgstr "„%(object)s” נוסף." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "„%(object)s” נמחקו." + +msgid "LogEntry Object" +msgstr "אובייקט LogEntry" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "" + +msgid "Added." +msgstr "נוסף." + +msgid "and" +msgstr "ו" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "" + +#, python-brace-format +msgid "Changed {fields}." +msgstr " {fields} שונו." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "" + +msgid "No fields changed." +msgstr "אף שדה לא השתנה." + +msgid "None" +msgstr "ללא" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "יש להחזיק \"Control\" או \"Command\" במק, כדי לבחור יותר מאחד." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "" + +msgid "You may edit it again below." +msgstr "ניתן לערוך שוב מתחת." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "יש לסמן פריטים כדי לבצע עליהם פעולות. לא שונו פריטים." + +msgid "No action selected." +msgstr "לא נבחרה פעולה." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "" + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "" + +#, python-format +msgid "Add %s" +msgstr "הוספת %s" + +#, python-format +msgid "Change %s" +msgstr "שינוי %s" + +#, python-format +msgid "View %s" +msgstr "צפיה ב%s" + +msgid "Database error" +msgstr "שגיאת בסיס נתונים" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "שינוי %(count)s %(name)s בוצע בהצלחה." +msgstr[1] "שינוי %(count)s %(name)s בוצע בהצלחה." +msgstr[2] "שינוי %(count)s %(name)s בוצע בהצלחה." +msgstr[3] "שינוי %(count)s %(name)s בוצע בהצלחה." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s נבחר" +msgstr[1] "כל ה־%(total_count)s נבחרו" +msgstr[2] "כל ה־%(total_count)s נבחרו" +msgstr[3] "כל ה־%(total_count)s נבחרו" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 מ %(cnt)s נבחרים" + +#, python-format +msgid "Change history: %s" +msgstr "היסטוריית שינוי: %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"מחיקת %(class_name)s %(instance)s תדרוש מחיקת האובייקטים הקשורים והמוגנים " +"הבאים: %(related_objects)s" + +msgid "Django site admin" +msgstr "ניהול אתר Django" + +msgid "Django administration" +msgstr "ניהול Django" + +msgid "Site administration" +msgstr "ניהול אתר" + +msgid "Log in" +msgstr "כניסה" + +#, python-format +msgid "%(app)s administration" +msgstr "ניהול %(app)s" + +msgid "Page not found" +msgstr "דף לא קיים" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "אנו מתנצלים, העמוד המבוקש אינו קיים." + +msgid "Home" +msgstr "דף הבית" + +msgid "Server error" +msgstr "שגיאת שרת" + +msgid "Server error (500)" +msgstr "שגיאת שרת (500)" + +msgid "Server Error (500)" +msgstr "שגיאת שרת (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" + +msgid "Run the selected action" +msgstr "הפעל את הפעולה שבחרת בה." + +msgid "Go" +msgstr "בצע" + +msgid "Click here to select the objects across all pages" +msgstr "לחיצה כאן תבחר את האובייקטים בכל העמודים" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "בחירת כל %(total_count)s ה־%(module_name)s" + +msgid "Clear selection" +msgstr "איפוס בחירה" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "מודלים ביישום %(name)s" + +msgid "Add" +msgstr "הוספה" + +msgid "View" +msgstr "צפיה" + +msgid "You don’t have permission to view or edit anything." +msgstr "אין לך כלל הרשאות צפיה או עריכה." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"ראשית יש להזין שם משתמש וססמה. לאחר מכן ניתן יהיה לערוך אפשרויות משתמש " +"נוספות." + +msgid "Enter a username and password." +msgstr "נא לשים שם משתמש וסיסמה." + +msgid "Change password" +msgstr "שינוי סיסמה" + +msgid "Please correct the error below." +msgstr "נא לתקן את השגיאה מתחת." + +msgid "Please correct the errors below." +msgstr "נא לתקן את השגיאות מתחת." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "יש להזין סיסמה חדשה עבור המשתמש %(username)s." + +msgid "Welcome," +msgstr "שלום," + +msgid "View site" +msgstr "צפיה באתר" + +msgid "Documentation" +msgstr "תיעוד" + +msgid "Log out" +msgstr "יציאה" + +#, python-format +msgid "Add %(name)s" +msgstr "הוספת %(name)s" + +msgid "History" +msgstr "היסטוריה" + +msgid "View on site" +msgstr "צפיה באתר" + +msgid "Filter" +msgstr "סינון" + +msgid "Clear all filters" +msgstr "ניקוי כל הסינונים" + +msgid "Remove from sorting" +msgstr "הסרה ממיון" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "עדיפות מיון: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "החלף כיוון מיון" + +msgid "Delete" +msgstr "מחיקה" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"מחיקת %(object_name)s '%(escaped_object)s' מצריכה מחיקת אובייקטים מקושרים, " +"אך לחשבון שלך אין הרשאות למחיקת סוגי האובייקטים הבאים:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"מחיקת ה%(object_name)s '%(escaped_object)s' תדרוש מחיקת האובייקטים הקשורים " +"והמוגנים הבאים:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"האם ברצונך למחוק את %(object_name)s \"%(escaped_object)s\"? כל הפריטים " +"הקשורים הבאים יימחקו:" + +msgid "Objects" +msgstr "אובייקטים" + +msgid "Yes, I’m sure" +msgstr "כן, בבטחה" + +msgid "No, take me back" +msgstr "לא, קח אותי חזרה." + +msgid "Delete multiple objects" +msgstr "מחק כמה פריטים" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"מחיקת ב%(objects_name)s הנבחרת תביא במחיקת אובייקטים קשורים, אבל החשבון שלך " +"אינו הרשאה למחוק את הסוגים הבאים של אובייקטים:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"מחיקת ה%(objects_name)s אשר סימנת תדרוש מחיקת האובייקטים הקשורים והמוגנים " +"הבאים:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"האם אתה בטוח שאתה רוצה למחוק את ה%(objects_name)s הנבחר? כל האובייקטים הבאים " +"ופריטים הקשורים להם יימחקו:" + +msgid "Delete?" +msgstr "מחיקה ?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " לפי %(filter_title)s " + +msgid "Summary" +msgstr "סיכום" + +msgid "Recent actions" +msgstr "פעולות אחרונות" + +msgid "My actions" +msgstr "הפעולות שלי" + +msgid "None available" +msgstr "לא נמצאו" + +msgid "Unknown content" +msgstr "תוכן לא ידוע" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"משהו שגוי בהתקנת בסיס הנתונים שלך. יש לוודא יצירת הטבלאות המתאימות וקיום " +"הרשאות קריאה על בסיס הנתונים עבור המשתמש המתאים." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"התחברת בתור %(username)s, אך אין לך הרשאות גישה לעמוד זה. האם ברצונך להתחבר " +"בתור משתמש אחר?" + +msgid "Forgotten your password or username?" +msgstr "שכחת את שם המשתמש והסיסמה שלך ?" + +msgid "Toggle navigation" +msgstr "" + +msgid "Start typing to filter…" +msgstr "התחל להקליד כדי לסנן..." + +msgid "Filter navigation items" +msgstr "סנן פריטי ניווט" + +msgid "Date/time" +msgstr "תאריך/שעה" + +msgid "User" +msgstr "משתמש" + +msgid "Action" +msgstr "פעולה" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "לאובייקט זה אין היסטוריית שינויים. כנראה לא נוסף דרך ממשק הניהול." + +msgid "Show all" +msgstr "הצג הכל" + +msgid "Save" +msgstr "שמירה" + +msgid "Popup closing…" +msgstr "חלון צץ נסגר..." + +msgid "Search" +msgstr "חיפוש" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "תוצאה %(counter)s" +msgstr[1] "%(counter)s תוצאות" +msgstr[2] "%(counter)s תוצאות" +msgstr[3] "%(counter)s תוצאות" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s סה\"כ" + +msgid "Save as new" +msgstr "שמירה כחדש" + +msgid "Save and add another" +msgstr "שמירה והוספת אחר" + +msgid "Save and continue editing" +msgstr "שמירה והמשך עריכה" + +msgid "Save and view" +msgstr "שמירה וצפיה" + +msgid "Close" +msgstr "סגירה" + +#, python-format +msgid "Change selected %(model)s" +msgstr "שינוי %(model)s הנבחר." + +#, python-format +msgid "Add another %(model)s" +msgstr "הוספת %(model)s נוסף." + +#, python-format +msgid "Delete selected %(model)s" +msgstr "מחיקת %(model)s הנבחר." + +msgid "Thanks for spending some quality time with the web site today." +msgstr "" + +msgid "Log in again" +msgstr "התחבר/י שוב" + +msgid "Password change" +msgstr "שינוי סיסמה" + +msgid "Your password was changed." +msgstr "סיסמתך שונתה." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"נא להזין את הססמה הישנה שלך, למען האבטחה, ולאחר מכן את הססמה החדשה שלך " +"פעמיים כדי שנוכל לוודא שהקלדת אותה נכון." + +msgid "Change my password" +msgstr "שנה את סיסמתי" + +msgid "Password reset" +msgstr "איפוס סיסמה" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "ססמתך נשמרה. כעת ניתן להתחבר." + +msgid "Password reset confirmation" +msgstr "אימות איפוס סיסמה" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "נא להזין את סיסמתך החדשה פעמיים כדי שנוכל לוודא שהקלדת אותה כראוי." + +msgid "New password:" +msgstr "סיסמה חדשה:" + +msgid "Confirm password:" +msgstr "אימות סיסמה:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"הקישור לאיפוס הסיסמה אינו חוקי. ייתכן והשתמשו בו כבר. נא לבקש איפוס סיסמה " +"חדש." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"שלחנו לך הוראות לקביעת הססמה, בהנחה שקיים חשבון עם כתובת הדואר האלקטרוני " +"שהזנת. ההוראות אמורות להתקבל בקרוב." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"אם לא קיבלת דואר אלקטרוני, נא לוודא שהזנת את הכתובת שנרשמת עימה ושההודעה לא " +"נחתה בתיקיית דואר הזבל." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"הודעה זו נשלחה אליך עקב בקשתך לאיפוס הסיסמה עבור המשתמש שלך באתר " +"%(site_name)s." + +msgid "Please go to the following page and choose a new password:" +msgstr "נא להגיע לעמוד הבא ולבחור סיסמה חדשה:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "שם המשתמש שלך במקרה ושכחת:" + +msgid "Thanks for using our site!" +msgstr "תודה על השימוש באתר שלנו!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "צוות %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"שכחת את הססמה שלך? נא להזין את כתובת הדואר האלקטרוני מתחת ואנו נשלח הוראות " +"לקביעת ססמה חדשה." + +msgid "Email address:" +msgstr "כתובת דוא\"ל:" + +msgid "Reset my password" +msgstr "אפס את סיסמתי" + +msgid "All dates" +msgstr "כל התאריכים" + +#, python-format +msgid "Select %s" +msgstr "בחירת %s" + +#, python-format +msgid "Select %s to change" +msgstr "בחירת %s לשינוי" + +#, python-format +msgid "Select %s to view" +msgstr "בחירת %s לצפיה" + +msgid "Date:" +msgstr "תאריך:" + +msgid "Time:" +msgstr "שעה:" + +msgid "Lookup" +msgstr "חפש" + +msgid "Currently:" +msgstr "נוכחי:" + +msgid "Change:" +msgstr "שינוי:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/hi/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/hi/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..16fb69d6 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/hi/LC_MESSAGES/django 3.po @@ -0,0 +1,706 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# alkuma , 2013 +# Chandan kumar , 2012 +# Jannis Leidel , 2011 +# Pratik , 2013 +# Sandeep Satavlekar , 2011 +# Vaarun Sinha, 2022 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:10-0500\n" +"PO-Revision-Date: 2022-07-25 07:05+0000\n" +"Last-Translator: Vaarun Sinha\n" +"Language-Team: Hindi (http://www.transifex.com/django/django/language/hi/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: hi\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "चुने हुए %(verbose_name_plural)s हटा दीजिये " + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d %(items)s सफलतापूर्वक हटा दिया गया है |" + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "%(name)s नहीं हटा सकते" + +msgid "Are you sure?" +msgstr "क्या आप निश्चित हैं?" + +msgid "Administration" +msgstr "" + +msgid "All" +msgstr "सभी" + +msgid "Yes" +msgstr "हाँ" + +msgid "No" +msgstr "नहीं" + +msgid "Unknown" +msgstr "अनजान" + +msgid "Any date" +msgstr "कोई भी तारीख" + +msgid "Today" +msgstr "आज" + +msgid "Past 7 days" +msgstr "पिछले 7 दिन" + +msgid "This month" +msgstr "इस महीने" + +msgid "This year" +msgstr "इस साल" + +msgid "No date" +msgstr "" + +msgid "Has date" +msgstr "" + +msgid "Empty" +msgstr "" + +msgid "Not empty" +msgstr "" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"कृपया कर्मचारी खाते का सही %(username)s व कूटशब्द भरें। भरते समय दीर्घाक्षर और लघु अक्षर " +"का खयाल रखें।" + +msgid "Action:" +msgstr " क्रिया:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "एक और %(verbose_name)s जोड़ें " + +msgid "Remove" +msgstr "निकालें" + +msgid "Addition" +msgstr "" + +msgid "Change" +msgstr "बदलें" + +msgid "Deletion" +msgstr "" + +msgid "action time" +msgstr "कार्य के लिए समय" + +msgid "user" +msgstr "" + +msgid "content type" +msgstr "" + +msgid "object id" +msgstr "वस्तु की आईडी " + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "वस्तु का निरूपण" + +msgid "action flag" +msgstr "कार्य ध्वज" + +msgid "change message" +msgstr "परिवर्तन सन्देश" + +msgid "log entry" +msgstr "लॉग प्रविष्टि" + +msgid "log entries" +msgstr "लॉग प्रविष्टियाँ" + +#, python-format +msgid "Added “%(object)s”." +msgstr "" + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "" + +msgid "LogEntry Object" +msgstr "LogEntry ऑब्जेक्ट" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "" + +msgid "Added." +msgstr "" + +msgid "and" +msgstr "और" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "" + +#, python-brace-format +msgid "Changed {fields}." +msgstr "" + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "" + +msgid "No fields changed." +msgstr "कोई क्षेत्र नहीं बदला" + +msgid "None" +msgstr "कोई नहीं" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "" + +msgid "You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "कार्रवाई हेतु आयटम सही अनुक्रम में चुने जाने चाहिए | कोई आइटम नहीं बदले गये हैं." + +msgid "No action selected." +msgstr "कोई कार्रवाई नहीं चुनी है |" + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "" + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "" + +#, python-format +msgid "Add %s" +msgstr "%s बढाएं" + +#, python-format +msgid "Change %s" +msgstr "%s बदलो" + +#, python-format +msgid "View %s" +msgstr "" + +msgid "Database error" +msgstr "डेटाबेस त्रुटि" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s का परिवर्तन कामयाब हुआ |" +msgstr[1] "%(count)s %(name)s का परिवर्तन कामयाब हुआ |" + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s चुने" +msgstr[1] "सभी %(total_count)s चुने " + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "%(cnt)s में से 0 चुने" + +#, python-format +msgid "Change history: %s" +msgstr "इतिहास बदलो: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" + +msgid "Django site admin" +msgstr "ज्याँगो साइट प्रशासन" + +msgid "Django administration" +msgstr "ज्याँगो प्रशासन" + +msgid "Site administration" +msgstr "साइट प्रशासन" + +msgid "Log in" +msgstr "लॉगिन" + +#, python-format +msgid "%(app)s administration" +msgstr "" + +msgid "Page not found" +msgstr "पृष्ठ लापता" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "" + +msgid "Home" +msgstr "गृह" + +msgid "Server error" +msgstr "सर्वर त्रुटि" + +msgid "Server error (500)" +msgstr "सर्वर त्रुटि (500)" + +msgid "Server Error (500)" +msgstr "सर्वर त्रुटि (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" + +msgid "Run the selected action" +msgstr "चयनित कार्रवाई चलाइये" + +msgid "Go" +msgstr "आगे बढ़े" + +msgid "Click here to select the objects across all pages" +msgstr "सभी पृष्ठों पर मौजूद वस्तुओं को चुनने के लिए यहाँ क्लिक करें " + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "तमाम %(total_count)s %(module_name)s चुनें" + +msgid "Clear selection" +msgstr "चयन खालिज किया जाये " + +#, python-format +msgid "Models in the %(name)s application" +msgstr "%(name)s अनुप्रयोग के प्रतिरूप" + +msgid "Add" +msgstr "बढाएं" + +msgid "View" +msgstr "" + +msgid "You don’t have permission to view or edit anything." +msgstr "" + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" + +msgid "Enter a username and password." +msgstr "उपयोगकर्ता का नाम और कूटशब्द दर्ज करें." + +msgid "Change password" +msgstr "पासवर्ड बदलें" + +msgid "Please correct the error below." +msgstr "" + +msgid "Please correct the errors below." +msgstr "" + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "%(username)s प्रवोक्ता के लिए नयी कूटशब्द दर्ज करें ।" + +msgid "Welcome," +msgstr "आपका स्वागत है," + +msgid "View site" +msgstr "" + +msgid "Documentation" +msgstr "दस्तावेज़ीकरण" + +msgid "Log out" +msgstr "लॉग आउट" + +#, python-format +msgid "Add %(name)s" +msgstr "%(name)s बढाएं" + +msgid "History" +msgstr "इतिहास" + +msgid "View on site" +msgstr "साइट पे देखें" + +msgid "Filter" +msgstr "छन्नी" + +msgid "Clear all filters" +msgstr "" + +msgid "Remove from sorting" +msgstr "श्रेणीकरण से हटाये " + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "श्रेणीकरण प्राथमिकता : %(priority_number)s" + +msgid "Toggle sorting" +msgstr "टॉगल श्रेणीकरण" + +msgid "Delete" +msgstr "मिटाएँ" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"%(object_name)s '%(escaped_object)s' को मिटाने पर सम्बंधित वस्तुएँ भी मिटा दी " +"जाएगी, परन्तु आप के खाते में निम्नलिखित प्रकार की वस्तुओं को मिटाने की अनुमति नहीं हैं |" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"%(object_name)s '%(escaped_object)s' को हटाने के लिए उनसे संबंधित निम्नलिखित " +"संरक्षित वस्तुओं को हटाने की आवश्यकता होगी:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"क्या आप %(object_name)s \"%(escaped_object)s\" हटाना चाहते हैं? निम्नलिखित सभी " +"संबंधित वस्तुएँ नष्ट की जाएगी" + +msgid "Objects" +msgstr "" + +msgid "Yes, I’m sure" +msgstr "" + +msgid "No, take me back" +msgstr "" + +msgid "Delete multiple objects" +msgstr "अनेक वस्तुएं हटाएँ" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"चयनित %(objects_name)s हटाने पर उस से सम्बंधित वस्तुएं भी हट जाएगी, परन्तु आपके खाते में " +"वस्तुओं के निम्नलिखित प्रकार हटाने की अनुमति नहीं है:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"चयनित %(objects_name)s को हटाने के पश्चात् निम्नलिखित संरक्षित संबंधित वस्तुओं को हटाने " +"की आवश्यकता होगी |" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"क्या आप ने पक्का तय किया हैं की चयनित %(objects_name)s को नष्ट किया जाये ? " +"निम्नलिखित सभी वस्तुएं और उनसे सम्बंधित वस्तुए भी नष्ट की जाएगी:" + +msgid "Delete?" +msgstr "मिटाएँ ?" + +#, python-format +msgid " By %(filter_title)s " +msgstr "%(filter_title)s द्वारा" + +msgid "Summary" +msgstr "" + +msgid "Recent actions" +msgstr "" + +msgid "My actions" +msgstr "" + +msgid "None available" +msgstr " कोई भी उपलब्ध नहीं" + +msgid "Unknown content" +msgstr "अज्ञात सामग्री" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" + +msgid "Forgotten your password or username?" +msgstr "अपना पासवर्ड या उपयोगकर्ता नाम भूल गये हैं?" + +msgid "Toggle navigation" +msgstr "" + +msgid "Start typing to filter…" +msgstr "" + +msgid "Filter navigation items" +msgstr "" + +msgid "Date/time" +msgstr "तिथि / समय" + +msgid "User" +msgstr "उपभोक्ता" + +msgid "Action" +msgstr "कार्य" + +msgid "entry" +msgstr "" + +msgid "entries" +msgstr "" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" + +msgid "Show all" +msgstr "सभी दिखाएँ" + +msgid "Save" +msgstr "सुरक्षित कीजिये" + +msgid "Popup closing…" +msgstr "" + +msgid "Search" +msgstr "खोज" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s परिणाम" +msgstr[1] "%(counter)s परिणाम" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s कुल परिणाम" + +msgid "Save as new" +msgstr "नये सा सहेजें" + +msgid "Save and add another" +msgstr "सहेजें और एक और जोडें" + +msgid "Save and continue editing" +msgstr "सहेजें और संपादन करें" + +msgid "Save and view" +msgstr "" + +msgid "Close" +msgstr "" + +#, python-format +msgid "Change selected %(model)s" +msgstr "" + +#, python-format +msgid "Add another %(model)s" +msgstr "" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "" + +#, python-format +msgid "View selected %(model)s" +msgstr "" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "" + +msgid "Log in again" +msgstr "फिर से लॉगिन कीजिए" + +msgid "Password change" +msgstr "कूटशब्द बदलें" + +msgid "Your password was changed." +msgstr "आपके कूटशब्द को बदला गया है" + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" + +msgid "Change my password" +msgstr "कूटशब्द बदलें" + +msgid "Password reset" +msgstr "कूटशब्द पुनस्थाप" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "आपके कूटशब्द को स्थापित किया गया है । अब आप लॉगिन कर सकते है ।" + +msgid "Password reset confirmation" +msgstr "कूटशब्द पुष्टि" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "कृपया आपके नये कूटशब्द को दो बार दर्ज करें ताकि हम उसकी सत्याप्ती कर सकते है ।" + +msgid "New password:" +msgstr "नया कूटशब्द " + +msgid "Confirm password:" +msgstr "कूटशब्द पुष्टि कीजिए" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"कूटशब्द पुनस्थाप संपर्क अमान्य है, संभावना है कि उसे उपयोग किया गया है। कृपया फिर से कूटशब्द " +"पुनस्थाप की आवेदन करें ।" + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"आपको यह डाक इसलिए आई है क्योंकि आप ने %(site_name)s पर अपने खाते का कूटशब्द बदलने का " +"अनुरोध किया था |" + +msgid "Please go to the following page and choose a new password:" +msgstr "कृपया निम्नलिखित पृष्ठ पर नया कूटशब्द चुनिये :" + +msgid "Your username, in case you’ve forgotten:" +msgstr "" + +msgid "Thanks for using our site!" +msgstr "हमारे साइट को उपयोग करने के लिए धन्यवाद ।" + +#, python-format +msgid "The %(site_name)s team" +msgstr "%(site_name)s दल" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" + +msgid "Email address:" +msgstr "डाक पता -" + +msgid "Reset my password" +msgstr " मेरे कूटशब्द की पुनःस्थापना" + +msgid "All dates" +msgstr "सभी तिथियों" + +#, python-format +msgid "Select %s" +msgstr "%s चुनें" + +#, python-format +msgid "Select %s to change" +msgstr "%s के बदली के लिए चयन करें" + +#, python-format +msgid "Select %s to view" +msgstr "" + +msgid "Date:" +msgstr "तिथि:" + +msgid "Time:" +msgstr "समय:" + +msgid "Lookup" +msgstr "लुक अप" + +msgid "Currently:" +msgstr "फ़िलहाल - " + +msgid "Change:" +msgstr "बदलाव -" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/hsb/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/hsb/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..3367be02 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/hsb/LC_MESSAGES/django 3.po @@ -0,0 +1,759 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Michael Wolf , 2016-2023 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 07:05+0000\n" +"Last-Translator: Michael Wolf , 2016-2023\n" +"Language-Team: Upper Sorbian (http://www.transifex.com/django/django/" +"language/hsb/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: hsb\n" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || " +"n%100==4 ? 2 : 3);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Wubrane %(verbose_name_plural)s zhašeć" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d %(items)s je so wuspěšnje zhašało." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "%(name)s njeda so zhašeć." + +msgid "Are you sure?" +msgstr "Sće wěsty?" + +msgid "Administration" +msgstr "Administracija" + +msgid "All" +msgstr "Wšě" + +msgid "Yes" +msgstr "Haj" + +msgid "No" +msgstr "Ně" + +msgid "Unknown" +msgstr "Njeznaty" + +msgid "Any date" +msgstr "Někajki datum" + +msgid "Today" +msgstr "Dźensa" + +msgid "Past 7 days" +msgstr "Zańdźene 7 dnjow" + +msgid "This month" +msgstr "Tutón měsac" + +msgid "This year" +msgstr "Lětsa" + +msgid "No date" +msgstr "Žadyn datum" + +msgid "Has date" +msgstr "Ma datum" + +msgid "Empty" +msgstr "Prózdny" + +msgid "Not empty" +msgstr "Njeprózdny" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Prošu zapodajće korektne %(username)s a hesło za personalne konto. Dźiwajće " +"na to, zo wobě poli móžetej mjez wulko- a małopisanjom rozeznawać." + +msgid "Action:" +msgstr "Akcija:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Přidajće nowe %(verbose_name)s" + +msgid "Remove" +msgstr "Wotstronić" + +msgid "Addition" +msgstr "Přidaće" + +msgid "Change" +msgstr "Změnić" + +msgid "Deletion" +msgstr "Zhašenje" + +msgid "action time" +msgstr "akciski čas" + +msgid "user" +msgstr "wužiwar" + +msgid "content type" +msgstr "wobsahowy typ" + +msgid "object id" +msgstr "objektowy id" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "objektowa reprezentacija" + +msgid "action flag" +msgstr "akciske markěrowanje" + +msgid "change message" +msgstr "změnowa powěsć" + +msgid "log entry" +msgstr "protokolowy zapisk" + +msgid "log entries" +msgstr "protokolowe zapiski" + +#, python-format +msgid "Added “%(object)s”." +msgstr "Je so „%(object)s“ přidał." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "Je so „%(object)s“ změnił - %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "Je so „%(object)s“ zhašał." + +msgid "LogEntry Object" +msgstr "Objekt LogEntry" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "Je so {name} „{object}“ přidał." + +msgid "Added." +msgstr "Přidaty." + +msgid "and" +msgstr "a" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "Je so {fields} za {name} „{object}“ změnił." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "{fields} změnjene." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "Je so {name} „{object}“ zhašał." + +msgid "No fields changed." +msgstr "Žane pola změnjene." + +msgid "None" +msgstr "Žadyn" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" +"Dźeržće „ctrl“ abo „cmd“ na Mac stłóčeny, zo byšće wjace hač jedyn wubrał." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "{name} „{obj}“ je so wuspěšnje přidał." + +msgid "You may edit it again below." +msgstr "Móžeće deleka unowa wobdźěłać." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"{name} „{obj}“ je so wuspěšnje přidał. Móžeće deleka dalši {name} přidać." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "{name} „{obj}“ je so wuspěšnje změnił. Móžeće jón deleka wobdźěłować." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "{name} „{obj}“ je so wuspěšnje přidał. Móžeće jón deleka wobdźěłować." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" +"{name} „{obj}“ je so wuspěšnje změnił. Móžeće deleka dalši {name} přidać." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "{name} „{obj}“ je so wuspěšnje změnił." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Dyrbiće zapiski wubrać, zo byšće akcije z nimi wuwjesć. Zapiski njejsu so " +"změnili." + +msgid "No action selected." +msgstr "žana akcija wubrana." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "%(name)s „%(obj)s“ je so wuspěšnje zhašał." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "%(name)s z ID „%(key)s“ njeeksistuje. Je so snano zhašało?" + +#, python-format +msgid "Add %s" +msgstr "%s přidać" + +#, python-format +msgid "Change %s" +msgstr "%s změnić" + +#, python-format +msgid "View %s" +msgstr "%s pokazać" + +msgid "Database error" +msgstr "Zmylk datoweje banki" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s je so wuspěšnje změnił." +msgstr[1] "%(count)s %(name)s stej so wuspěšnje změniłoj." +msgstr[2] "%(count)s %(name)s su so wuspěšnje změnili." +msgstr[3] "%(count)s %(name)s je so wuspěšnje změniło." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s wubrany" +msgstr[1] "%(total_count)s wubranej" +msgstr[2] "%(total_count)s wubrane" +msgstr[3] "%(total_count)s wubranych" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 z %(cnt)s wubranych" + +#, python-format +msgid "Change history: %s" +msgstr "Změnowa historija: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Zo bychu so %(class_name)s %(instance)s zhašeli, dyrbja so slědowace škitane " +"přisłušne objekty zhašeć: %(related_objects)s" + +msgid "Django site admin" +msgstr "Administrator sydła Django" + +msgid "Django administration" +msgstr "Administracija Django" + +msgid "Site administration" +msgstr "Sydłowa administracija" + +msgid "Log in" +msgstr "Přizjewić" + +#, python-format +msgid "%(app)s administration" +msgstr "Administracija %(app)s" + +msgid "Page not found" +msgstr "Strona njeje so namakała" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Je nam žel, ale požadana strona njeda so namakać." + +msgid "Home" +msgstr "Startowa strona" + +msgid "Server error" +msgstr "Serwerowy zmylk" + +msgid "Server error (500)" +msgstr "Serwerowy zmylk (500)" + +msgid "Server Error (500)" +msgstr "Serwerowy zmylk (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Zmylk je wustupił. Je so sydłowym administratoram přez e-mejl zdźělił a " +"dyrbjał so bórze wotstronić. Dźakujemy so za wašu sćerpliwosć." + +msgid "Run the selected action" +msgstr "Wubranu akciju wuwjesć" + +msgid "Go" +msgstr "Start" + +msgid "Click here to select the objects across all pages" +msgstr "Klikńće tu, zo byšće objekty wšěch stronow wubrać" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Wubjerće wšě %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "Wuběr wotstronić" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Modele w nałoženju %(name)s" + +msgid "Add" +msgstr "Přidać" + +msgid "View" +msgstr "Pokazać" + +msgid "You don’t have permission to view or edit anything." +msgstr "Nimaće prawo něšto pokazać abo wobdźěłać." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Zapodajće najprjedy wužiwarske mjeno a hesło. Potom móžeće dalše wužiwarske " +"nastajenja wobdźěłować." + +msgid "Enter a username and password." +msgstr "Zapodajće wužiwarske mjeno a hesło." + +msgid "Change password" +msgstr "Hesło změnić" + +msgid "Please correct the error below." +msgid_plural "Please correct the errors below." +msgstr[0] "Prošu porjedźće slědowacy zmylk." +msgstr[1] "Prošu porjedźće slědowacej zmylkaj." +msgstr[2] "Prošu porjedźće slědowace zmylki." +msgstr[3] "Prošu porjedźće slědowace zmylki." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "Zapodajće nowe hesło za %(username)s." + +msgid "Skip to main content" +msgstr "Dale k hłownemu wobsahej" + +msgid "Welcome," +msgstr "Witajće," + +msgid "View site" +msgstr "Sydło pokazać" + +msgid "Documentation" +msgstr "Dokumentacija" + +msgid "Log out" +msgstr "Wotzjewić" + +msgid "Breadcrumbs" +msgstr "Chlěbowe srjódki" + +#, python-format +msgid "Add %(name)s" +msgstr "%(name)s přidać" + +msgid "History" +msgstr "Historija" + +msgid "View on site" +msgstr "Na sydle pokazać" + +msgid "Filter" +msgstr "Filtrować" + +msgid "Clear all filters" +msgstr "Wšě filtry zhašeć" + +msgid "Remove from sorting" +msgstr "Ze sortěrowanja wotstronić" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Sortěrowanski porjad: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Sortěrowanje přepinać" + +msgid "Toggle theme (current theme: auto)" +msgstr "Drastu změnić (aktualna drasta: auto)" + +msgid "Toggle theme (current theme: light)" +msgstr "Drastu změnić (aktualna drasta: swětła)" + +msgid "Toggle theme (current theme: dark)" +msgstr "Drastu změnić (aktualna drasta: ćmowa)" + +msgid "Delete" +msgstr "Zhašeć" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Hdyž so %(object_name)s '%(escaped_object)s' zhašeja, so tež přisłušne " +"objekty zhašeja, ale waše konto nima prawo slědowace typy objektow zhašeć:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Zo by so %(object_name)s '%(escaped_object)s' zhašało, dyrbja so slědowace " +"přisłušne objekty zhašeć:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Chceće woprawdźe %(object_name)s \"%(escaped_object)s\" zhašeć? Wšě " +"slědowace přisłušne zapiski so zhašeja:" + +msgid "Objects" +msgstr "Objekty" + +msgid "Yes, I’m sure" +msgstr "Haj, sym sej wěsty" + +msgid "No, take me back" +msgstr "Ně, prošu wróćo" + +msgid "Delete multiple objects" +msgstr "Wjacore objekty zhašeć" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Hdyž so wubrany %(objects_name)s zhaša, so přisłušne objekty zhašeja, ale " +"waše konto nima prawo slědowace typy objektow zhašeć: " + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Hdyž so wubrany %(objects_name)s zhaša, so slědowace škitane přisłušne " +"objekty zhašeja:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Chceće woprawdźe wubrane %(objects_name)s zhašeć? Wšě slědowace objekty a " +"jich přisłušne zapiski so zhašeja:" + +msgid "Delete?" +msgstr "Zhašeć?" + +#, python-format +msgid " By %(filter_title)s " +msgstr "Po %(filter_title)s " + +msgid "Summary" +msgstr "Zjeće" + +msgid "Recent actions" +msgstr "Najnowše akcije" + +msgid "My actions" +msgstr "Moje akcije" + +msgid "None available" +msgstr "Žadyn k dispoziciji" + +msgid "Unknown content" +msgstr "Njeznaty wobsah" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Něšto je so z instalaciju datoweje banki nimokuliło. Zawěsćće, zo wotpowědne " +"tabele datoweje banki su so wutworili, a, zo datowa banka da so wot " +"wotpowědneho wužiwarja čitać." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Sće jako %(username)s awtentifikowany, ale nimaće přistup na tutu stronu. " +"Chceće so pola druheho konta přizjewić?" + +msgid "Forgotten your password or username?" +msgstr "Sće swoje hesło abo wužiwarske mjeno zabył?" + +msgid "Toggle navigation" +msgstr "Nawigaciju přepinać" + +msgid "Sidebar" +msgstr "Bóčnica" + +msgid "Start typing to filter…" +msgstr "Pisajće, zo byšće filtrował …" + +msgid "Filter navigation items" +msgstr "Nawigaciske zapiski fitrować" + +msgid "Date/time" +msgstr "Datum/čas" + +msgid "User" +msgstr "Wužiwar" + +msgid "Action" +msgstr "Akcija" + +msgid "entry" +msgid_plural "entries" +msgstr[0] "zapisk" +msgstr[1] "zapiskaj" +msgstr[2] "zapiski" +msgstr[3] "zapiskow" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"Tutón objekt nima změnowu historiju. Njeje so najskerje přez tute " +"administratorowe sydło přidał." + +msgid "Show all" +msgstr "Wšě pokazać" + +msgid "Save" +msgstr "Składować" + +msgid "Popup closing…" +msgstr "Wuskakowace wokno so začinja…" + +msgid "Search" +msgstr "Pytać" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s wuslědk" +msgstr[1] "%(counter)s wuslědkaj" +msgstr[2] "%(counter)s wuslědki" +msgstr[3] "%(counter)s wuslědkow" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s dohromady" + +msgid "Save as new" +msgstr "Jako nowy składować" + +msgid "Save and add another" +msgstr "Skłaodwac a druhi přidać" + +msgid "Save and continue editing" +msgstr "Składować a dale wobdźěłować" + +msgid "Save and view" +msgstr "Składować a pokazać" + +msgid "Close" +msgstr "Začinić" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Wubrane %(model)s změnić" + +#, python-format +msgid "Add another %(model)s" +msgstr "Druhi %(model)s přidać" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Wubrane %(model)s zhašeć" + +#, python-format +msgid "View selected %(model)s" +msgstr "Wubrany %(model)s pokazać" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "" +"Wulki dźak, zo sće sej čas brał, zo byšće kwalitu websydła dźensa " +"přepruwował." + +msgid "Log in again" +msgstr "Znowa přizjewić" + +msgid "Password change" +msgstr "Hesło změnić" + +msgid "Your password was changed." +msgstr "Waše hesło je so změniło." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Prošu zapodajće swoje stare hesło k swojemu škitej a potom swoje nowe hesło " +"dwójce, zo bychmy móhli přepruwować, hač sće jo korektnje zapodał." + +msgid "Change my password" +msgstr "Moje hesło změnić" + +msgid "Password reset" +msgstr "Hesło wróćo stajić" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Waše hesło je so nastajiło. Móžeće pokročować a so nětko přizjewić." + +msgid "Password reset confirmation" +msgstr "Wobkrućenje wróćostajenja hesła" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Prošu zapodajće swoje hesło dwójce, zo bychmy móhli přepruwować, hač sće jo " +"korektnje zapodał." + +msgid "New password:" +msgstr "Nowe hesło:" + +msgid "Confirm password:" +msgstr "Hesło wobkrućić:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Wotkaz za wróćostajenje hesła bě njepłaćiwy, snano dokelž je so hižo wužił. " +"Prošu prošće wo nowe wróćostajenje hesła." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Smy wam e-mejlku z instrukcijemi wo nastajenju wašeho hesła pósłali, jeli " +"konto ze zapodatej e-mejlowej adresu eksistuje. Wy dyrbjał ju bórze dóstać." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Jeli e-mejlku njedóstawaće, přepruwujće prošu adresu, z kotrejž sće so " +"zregistrował a hladajće do swojeho spamoweho rjadowaka." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Dóstawaće tutu e-mejlku, dokelž sće wo wróćostajenje hesła za swoje " +"wužiwarske konto na at %(site_name)s prosył." + +msgid "Please go to the following page and choose a new password:" +msgstr "Prošu dźiće k slědowacej stronje a wubjerće nowe hesło:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "Waše wužiwarske mjeno, jeli sće jo zabył:" + +msgid "Thanks for using our site!" +msgstr "Wulki dźak za wužiwanje našeho sydła!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "Team %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Sće swoje hesło zabył? Zapodajće deleka swoju e-mejlowu adresu a pósćelemy " +"wam instrukcije za postajenje noweho hesła přez e-mejl." + +msgid "Email address:" +msgstr "E-mejlowa adresa:" + +msgid "Reset my password" +msgstr "Moje hesło wróćo stajić" + +msgid "All dates" +msgstr "Wšě daty" + +#, python-format +msgid "Select %s" +msgstr "%s wubrać" + +#, python-format +msgid "Select %s to change" +msgstr "%s wubrać, zo by so změniło" + +#, python-format +msgid "Select %s to view" +msgstr "%s wubrać, kotryž ma so pokazać" + +msgid "Date:" +msgstr "Datum:" + +msgid "Time:" +msgstr "Čas:" + +msgid "Lookup" +msgstr "Pytanje" + +msgid "Currently:" +msgstr "Tuchylu:" + +msgid "Change:" +msgstr "Změnić:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/hu/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/hu/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..006c4a05 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/hu/LC_MESSAGES/django 3.po @@ -0,0 +1,731 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Ádám Krizsány , 2015 +# Akos Zsolt Hochrein , 2018 +# András Veres-Szentkirályi, 2016,2018-2020 +# Istvan Farkas , 2019 +# Jannis Leidel , 2011 +# János R, 2017 +# János R, 2014 +# Kristóf Gruber <>, 2012 +# slink , 2011 +# Szilveszter Farkas , 2011 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-07-14 19:53+0200\n" +"PO-Revision-Date: 2020-07-20 07:29+0000\n" +"Last-Translator: András Veres-Szentkirályi\n" +"Language-Team: Hungarian (http://www.transifex.com/django/django/language/" +"hu/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: hu\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d %(items)s sikeresen törölve lett." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "%(name)s törlése nem sikerült" + +msgid "Are you sure?" +msgstr "Biztos benne?" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Kiválasztott %(verbose_name_plural)s törlése" + +msgid "Administration" +msgstr "Adminisztráció" + +msgid "All" +msgstr "Mind" + +msgid "Yes" +msgstr "Igen" + +msgid "No" +msgstr "Nem" + +msgid "Unknown" +msgstr "Ismeretlen" + +msgid "Any date" +msgstr "Bármely dátum" + +msgid "Today" +msgstr "Ma" + +msgid "Past 7 days" +msgstr "Utolsó 7 nap" + +msgid "This month" +msgstr "Ez a hónap" + +msgid "This year" +msgstr "Ez az év" + +msgid "No date" +msgstr "Nincs dátuma" + +msgid "Has date" +msgstr "Van dátuma" + +msgid "Empty" +msgstr "Üres" + +msgid "Not empty" +msgstr "Nem üres" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Adja meg egy adminisztrációra jogosult %(username)s és jelszavát. Vegye " +"figyelembe, hogy mindkét mező megkülönböztetheti a kis- és nagybetűket." + +msgid "Action:" +msgstr "Művelet:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Újabb %(verbose_name)s hozzáadása" + +msgid "Remove" +msgstr "Törlés" + +msgid "Addition" +msgstr "Hozzáadás" + +msgid "Change" +msgstr "Módosítás" + +msgid "Deletion" +msgstr "Törlés" + +msgid "action time" +msgstr "művelet időpontja" + +msgid "user" +msgstr "felhasználó" + +msgid "content type" +msgstr "tartalom típusa" + +msgid "object id" +msgstr "objektum id" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "objektum repr" + +msgid "action flag" +msgstr "művelet jelölés" + +msgid "change message" +msgstr "üzenet módosítása" + +msgid "log entry" +msgstr "naplóbejegyzés" + +msgid "log entries" +msgstr "naplóbejegyzések" + +#, python-format +msgid "Added “%(object)s”." +msgstr "\"%(object)s\" hozzáadva." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "\"%(object)s\" módosítva — %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "\"%(object)s\" törölve." + +msgid "LogEntry Object" +msgstr "Naplóbejegyzés objektum" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "\"{object}\" {name} hozzáadva." + +msgid "Added." +msgstr "Hozzáadva." + +msgid "and" +msgstr "és" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "\"{object}\" {name} {fields} módosítva." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "{fields} módosítva." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "\"{object}\" {name} törölve." + +msgid "No fields changed." +msgstr "Egy mező sem változott." + +msgid "None" +msgstr "Egyik sem" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" +"Több elem kiválasztásához tartsa nyomva a \"Control\" gombot, vagy Mac " +"gépeken a \"Command\" gombot." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "A(z) \"{obj}\" {name} sikeresen hozzáadva." + +msgid "You may edit it again below." +msgstr "Alább ismét szerkesztheti." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"A(z) \"{obj}\" {name} sikeresen hozzáadva. Alább hozzadhat egy új {name} " +"rekordot." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "A(z) \"{obj}\" {name} sikeresen módosítva. Alább újra szerkesztheti." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "A(z) \"{obj}\" {name} sikeresen hozzáadva. Alább újra szerkesztheti." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" +"A(z) \"{obj}\" {name} sikeresen módosítva. Alább hozzáadhat egy új {name} " +"rekordot." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "A(z) \"{obj}\" {name} sikeresen módosítva." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"A műveletek végrehajtásához ki kell választani legalább egy elemet. Semmi " +"sem lett módosítva." + +msgid "No action selected." +msgstr "Nem választott ki műveletet." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "A(z) \"%(obj)s\" %(name)s törölve lett." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "" +"A(z) \"%(key)s\" azonosítójú %(name)s nem létezik. Esetleg törölve lett?" + +#, python-format +msgid "Add %s" +msgstr "Új %s" + +#, python-format +msgid "Change %s" +msgstr "%s módosítása" + +#, python-format +msgid "View %s" +msgstr "%s megtekintése" + +msgid "Database error" +msgstr "Adatbázishiba" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s sikeresen módosítva lett." +msgstr[1] "%(count)s %(name)s sikeresen módosítva lett." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s kiválasztva" +msgstr[1] "%(total_count)s kiválasztva" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 kiválasztva ennyiből: %(cnt)s" + +#, python-format +msgid "Change history: %s" +msgstr "Változások története: %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"%(instance)s %(class_name)s törlése az alábbi kapcsolódó védett objektumok " +"törlését is magával vonná: %(related_objects)s" + +msgid "Django site admin" +msgstr "Django honlapadminisztráció" + +msgid "Django administration" +msgstr "Django adminisztráció" + +msgid "Site administration" +msgstr "Honlap karbantartása" + +msgid "Log in" +msgstr "Bejelentkezés" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s adminisztráció" + +msgid "Page not found" +msgstr "Nincs ilyen oldal" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Sajnáljuk, de a keresett oldal nem található." + +msgid "Home" +msgstr "Kezdőlap" + +msgid "Server error" +msgstr "Szerverhiba" + +msgid "Server error (500)" +msgstr "Szerverhiba (500)" + +msgid "Server Error (500)" +msgstr "Szerverhiba (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Hiba történt. Az oldal kezelőjét e-mailben értesítettük, a hiba rövidesen " +"javítva lesz. Köszönjük a türelmet." + +msgid "Run the selected action" +msgstr "Kiválasztott művelet futtatása" + +msgid "Go" +msgstr "Mehet" + +msgid "Click here to select the objects across all pages" +msgstr "Kattintson ide több oldalnyi objektum kiválasztásához" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Az összes %(module_name)s kiválasztása, összesen %(total_count)s db" + +msgid "Clear selection" +msgstr "Kiválasztás törlése" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "%(name)s alkalmazásban elérhető modellek." + +msgid "Add" +msgstr "Új" + +msgid "View" +msgstr "Megtekintés" + +msgid "You don’t have permission to view or edit anything." +msgstr "Jelenleg nincs jogosultsága bármit megtekinteni vagy szerkeszteni." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Először adjon meg egy felhasználónevet és jelszót. A mentés után a többi " +"felhasználói adat is szerkeszthető lesz." + +msgid "Enter a username and password." +msgstr "Írjon be egy felhasználónevet és jelszót." + +msgid "Change password" +msgstr "Jelszó megváltoztatása" + +msgid "Please correct the error below." +msgstr "Kérem javítsa a hibát alább." + +msgid "Please correct the errors below." +msgstr "Kérem javítsa ki a lenti hibákat." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "" +"Adjon meg egy új jelszót %(username)s nevű felhasználónak." + +msgid "Welcome," +msgstr "Üdvözlöm," + +msgid "View site" +msgstr "Honlap megtekintése" + +msgid "Documentation" +msgstr "Dokumentáció" + +msgid "Log out" +msgstr "Kijelentkezés" + +#, python-format +msgid "Add %(name)s" +msgstr "Új %(name)s" + +msgid "History" +msgstr "Történet" + +msgid "View on site" +msgstr "Megtekintés a honlapon" + +msgid "Filter" +msgstr "Szűrő" + +msgid "Clear all filters" +msgstr "Összes szűrő törlése" + +msgid "Remove from sorting" +msgstr "Eltávolítás a rendezésből" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Prioritás rendezésnél: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Rendezés megfordítása" + +msgid "Delete" +msgstr "Törlés" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"\"%(escaped_object)s\" %(object_name)s törlése a kapcsolódó objektumok " +"törlését is eredményezi, de a hozzáférése nem engedi a következő típusú " +"objektumok törlését:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"\"%(escaped_object)s\" %(object_name)s törlése az alábbi kapcsolódó " +"objektumok törlését is maga után vonja:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Biztos hogy törli a következőt: \"%(escaped_object)s\" (típus: " +"%(object_name)s)? A összes további kapcsolódó elem is törlődik:" + +msgid "Objects" +msgstr "Objektumok" + +msgid "Yes, I’m sure" +msgstr "Igen, biztos vagyok benne" + +msgid "No, take me back" +msgstr "Nem, forduljunk vissza" + +msgid "Delete multiple objects" +msgstr "Több elem törlése" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"A kiválasztott %(objects_name)s törlése kapcsolódó objektumok törlését vonja " +"maga után, de az alábbi objektumtípusok törléséhez nincs megfelelő " +"jogosultsága:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"A kiválasztott %(objects_name)s törlése az alábbi védett kapcsolódó " +"objektumok törlését is maga után vonja:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Biztosan törölni akarja a kiválasztott %(objects_name)s objektumokat? Minden " +"alábbi objektum, és a hozzájuk kapcsolódóak is törlésre kerülnek:" + +msgid "Delete?" +msgstr "Törli?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " %(filter_title)s szerint " + +msgid "Summary" +msgstr "Összegzés" + +msgid "Recent actions" +msgstr "Legutóbbi műveletek" + +msgid "My actions" +msgstr "Az én műveleteim" + +msgid "None available" +msgstr "Nincs elérhető" + +msgid "Unknown content" +msgstr "Ismeretlen tartalom" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Valami probléma van az adatbázissal. Kérjük győződjön meg róla, hogy a " +"megfelelő táblák létre lettek hozva, és hogy a megfelelő felhasználónak van " +"rájuk olvasási joga." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Jelenleg be vagy lépve mint %(username)s, de nincs jogod elérni ezt az " +"oldalt. Szeretnél belépni egy másik fiókkal?" + +msgid "Forgotten your password or username?" +msgstr "Elfelejtette jelszavát vagy felhasználónevét?" + +msgid "Toggle navigation" +msgstr "Navigáció megjelenítése/elrejtése" + +msgid "Date/time" +msgstr "Dátum/idő" + +msgid "User" +msgstr "Felhasználó" + +msgid "Action" +msgstr "Művelet" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"Ennek az objektumnak nincs változás naplója. Valószínűleg nem az admin " +"felületen keresztül lett rögzítve." + +msgid "Show all" +msgstr "Mutassa mindet" + +msgid "Save" +msgstr "Mentés" + +msgid "Popup closing…" +msgstr "A popup bezáródik…" + +msgid "Search" +msgstr "Keresés" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s találat" +msgstr[1] "%(counter)s találat" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s összesen" + +msgid "Save as new" +msgstr "Mentés újként" + +msgid "Save and add another" +msgstr "Mentés és másik hozzáadása" + +msgid "Save and continue editing" +msgstr "Mentés és a szerkesztés folytatása" + +msgid "Save and view" +msgstr "Mentés és megtekintés" + +msgid "Close" +msgstr "Bezárás" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Kiválasztott %(model)s szerkesztése" + +#, python-format +msgid "Add another %(model)s" +msgstr "Újabb %(model)s hozzáadása" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Kiválasztott %(model)s törlése" + +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Köszönjük hogy egy kis időt eltöltött ma a honlapunkon." + +msgid "Log in again" +msgstr "Jelentkezzen be újra" + +msgid "Password change" +msgstr "Jelszó megváltoztatása" + +msgid "Your password was changed." +msgstr "Megváltozott a jelszava." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Kérjük a biztoság kedvéért adja meg a jelenlegi jelszavát, majd az újat, " +"kétszer, hogy biztosak lehessünk abban, hogy megfelelően gépelte be." + +msgid "Change my password" +msgstr "Jelszavam megváltoztatása" + +msgid "Password reset" +msgstr "Jelszó beállítása" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Jelszava beállításra került. Most már bejelentkezhet." + +msgid "Password reset confirmation" +msgstr "Jelszó beállítás megerősítése" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Írja be az új jelszavát kétszer, hogy megbizonyosodhassunk annak " +"helyességéről." + +msgid "New password:" +msgstr "Új jelszó:" + +msgid "Confirm password:" +msgstr "Jelszó megerősítése:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"A jelszóbeállító link érvénytelen. Ennek egyik oka az lehet, hogy már " +"felhasználták. Kérem indítson új jelszóbeállítást." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Amennyiben a megadott e-mail címhez tartozik fiók, elküldtük e-mailben a " +"leírást, hogy hogyan tudja megváltoztatni a jelszavát. Hamarosan meg kell " +"érkeznie." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Ha nem kapja meg a levelet, kérjük ellenőrizze, hogy a megfelelő e-mail " +"címet adta-e meg, illetve nézze meg a levélszemét mappában is." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Azért kapja ezt az e-mailt, mert jelszavának visszaállítását kérte ezen a " +"weboldalon: %(site_name)s." + +msgid "Please go to the following page and choose a new password:" +msgstr "Kérjük látogassa meg a következő oldalt, és válasszon egy új jelszót:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "A felhasználóneve, amennyiben nem emlékezne rá:" + +msgid "Thanks for using our site!" +msgstr "Köszönjük, hogy használta honlapunkat!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "%(site_name)s csapat" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Elfelejtette jelszavát? Adja meg az e-mail-címet, amellyel regisztrált " +"oldalunkon, és e-mailben elküldjük a leírását, hogy hogyan tud újat " +"beállítani." + +msgid "Email address:" +msgstr "E-mail cím:" + +msgid "Reset my password" +msgstr "Jelszavam törlése" + +msgid "All dates" +msgstr "Minden dátum" + +#, python-format +msgid "Select %s" +msgstr "%s kiválasztása" + +#, python-format +msgid "Select %s to change" +msgstr "Válasszon ki egyet a módosításhoz (%s)" + +#, python-format +msgid "Select %s to view" +msgstr "Válasszon ki egyet a megtekintéshez (%s)" + +msgid "Date:" +msgstr "Dátum:" + +msgid "Time:" +msgstr "Idő:" + +msgid "Lookup" +msgstr "Keresés" + +msgid "Currently:" +msgstr "Jelenleg:" + +msgid "Change:" +msgstr "Módosítás:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/hy/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/hy/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..966302d7 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/hy/LC_MESSAGES/django 3.po @@ -0,0 +1,708 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Սմբատ Պետրոսյան , 2014 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-05-21 14:16-0300\n" +"PO-Revision-Date: 2018-11-01 20:23+0000\n" +"Last-Translator: Ruben Harutyunov \n" +"Language-Team: Armenian (http://www.transifex.com/django/django/language/" +"hy/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: hy\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Հաջողությամբ հեռացվել է %(count)d %(items)s։" + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Հնարավոր չէ հեռացնել %(name)s" + +msgid "Are you sure?" +msgstr "Համոզված ե՞ք" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Հեռացնել նշված %(verbose_name_plural)sը" + +msgid "Administration" +msgstr "Ադմինիստրավորում" + +msgid "All" +msgstr "Բոլորը" + +msgid "Yes" +msgstr "Այո" + +msgid "No" +msgstr "Ոչ" + +msgid "Unknown" +msgstr "Անհայտ" + +msgid "Any date" +msgstr "Ցանկացած ամսաթիվ" + +msgid "Today" +msgstr "Այսօր" + +msgid "Past 7 days" +msgstr "Անցած 7 օրերին" + +msgid "This month" +msgstr "Այս ամիս" + +msgid "This year" +msgstr "Այս տարի" + +msgid "No date" +msgstr "" + +msgid "Has date" +msgstr "" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "Մուտքագրեք անձնակազմի պրոֆիլի ճիշտ %(username)s և գաղտնաբառ։" + +msgid "Action:" +msgstr "Գործողություն" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Ավելացնել այլ %(verbose_name)s" + +msgid "Remove" +msgstr "Հեռացնել" + +msgid "Addition" +msgstr "" + +msgid "Change" +msgstr "Փոփոխել" + +msgid "Deletion" +msgstr "" + +msgid "action time" +msgstr "գործողության ժամանակ" + +msgid "user" +msgstr "օգտագործող" + +msgid "content type" +msgstr "կոնտենտի տիպ" + +msgid "object id" +msgstr "օբյեկտի id" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/3/library/functions.html#repr) +msgid "object repr" +msgstr "օբյեկտի repr" + +msgid "action flag" +msgstr "գործողության դրոշ" + +msgid "change message" +msgstr "փոփոխել հաղորդագրությունը" + +msgid "log entry" +msgstr "log գրառում" + +msgid "log entries" +msgstr "log գրառումներ" + +#, python-format +msgid "Added \"%(object)s\"." +msgstr "%(object)s֊ը ավելացվեց " + +#, python-format +msgid "Changed \"%(object)s\" - %(changes)s" +msgstr "%(object)s֊ը փոփոխվեց ֊ %(changes)s" + +#, python-format +msgid "Deleted \"%(object)s.\"" +msgstr "%(object)s-ը հեռացվեց" + +msgid "LogEntry Object" +msgstr "LogEntry օբյեկտ" + +#, python-brace-format +msgid "Added {name} \"{object}\"." +msgstr "" + +msgid "Added." +msgstr "Ավելացվեց։" + +msgid "and" +msgstr "և" + +#, python-brace-format +msgid "Changed {fields} for {name} \"{object}\"." +msgstr "" + +#, python-brace-format +msgid "Changed {fields}." +msgstr "" + +#, python-brace-format +msgid "Deleted {name} \"{object}\"." +msgstr "" + +msgid "No fields changed." +msgstr "Ոչ մի դաշտ չփոփոխվեց։" + +msgid "None" +msgstr "Ոչինչ" + +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "" +"Սեղմեք \"Control\", կամ \"Command\" Mac֊ի մրա, մեկից ավելին ընտրելու համար։" + +#, python-brace-format +msgid "The {name} \"{obj}\" was added successfully." +msgstr "" + +msgid "You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." +msgstr "" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Օբյեկտների հետ գործողություն կատարելու համար նրանք պետք է ընտրվեն․ Ոչ մի " +"օբյեկտ չի փոփոխվել։" + +msgid "No action selected." +msgstr "Գործողությունը ընտրված չէ։" + +#, python-format +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "%(name)s %(obj)s֊ը հաջողությամբ հեռացվեց։" + +#, python-format +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" +msgstr "" + +#, python-format +msgid "Add %s" +msgstr "Ավելացնել %s" + +#, python-format +msgid "Change %s" +msgstr "Փոփոխել %s" + +#, python-format +msgid "View %s" +msgstr "" + +msgid "Database error" +msgstr "Տվյալների բազաի սխալ" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s հաջողությամբ փոփոխվեց։" +msgstr[1] "%(count)s %(name)s հաջողությամբ փոփոխվեցին։" + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "Ընտրված են %(total_count)s" +msgstr[1] "Բոլոր %(total_count)s֊ը ընտրված են " + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "%(cnt)s֊ից 0֊ն ընտրված է" + +#, python-format +msgid "Change history: %s" +msgstr "Փոփոխությունների պատմություն %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(instance)s %(class_name)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"%(instance)s %(class_name)s֊ը հեռացնելու համար անհրաժեշտ է հեռացնել նրա հետ " +"կապված պաշտպանված օբյեկտները՝ %(related_objects)s" + +msgid "Django site admin" +msgstr "Django կայքի ադմինիստրավորման էջ" + +msgid "Django administration" +msgstr "Django ադմինիստրավորում" + +msgid "Site administration" +msgstr "Կայքի ադմինիստրավորում" + +msgid "Log in" +msgstr "Մուտք" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s ադմինիստրավորում" + +msgid "Page not found" +msgstr "Էջը գտնված չէ" + +msgid "We're sorry, but the requested page could not be found." +msgstr "Ներողություն ենք հայցում, բայց հարցվող Էջը գտնված չէ" + +msgid "Home" +msgstr "Գլխավոր" + +msgid "Server error" +msgstr "Սերվերի սխալ" + +msgid "Server error (500)" +msgstr "Սերվերի սխալ (500)" + +msgid "Server Error (500)" +msgstr "Սերվերի սխալ (500)" + +msgid "" +"There's been an error. It's been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Առաջացել է սխալ։ Ադմինիստրատորները տեղեկացվել են դրա մասին էլեկտրոնային " +"փոստի միջոցով և այն կուղղվի կարճ ժամանակահատվածի ընդացքում․ Շնորհակալ ենք " +"ձեր համբերության համար։" + +msgid "Run the selected action" +msgstr "Կատարել ընտրված գործողությունը" + +msgid "Go" +msgstr "Կատարել" + +msgid "Click here to select the objects across all pages" +msgstr "Սեղմեք այստեղ բոլոր էջերից օբյեկտներ ընտրելու համար" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Ընտրել բոլոր %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "Չեղարկել ընտրությունը" + +msgid "" +"First, enter a username and password. Then, you'll be able to edit more user " +"options." +msgstr "" +"Սկզբում մուտքագրեք օգտագործողի անունը և գաղտնաբառը․ Հետո դուք " +"հնարավորություն կունենաք խմբագրել ավելին։" + +msgid "Enter a username and password." +msgstr "Մուտքագրեք օգտագործողի անունը և գաղտնաբառը։" + +msgid "Change password" +msgstr "Փոխել գաղտնաբառը" + +msgid "Please correct the error below." +msgstr "Ուղղեք ստորև նշված սխալը։" + +msgid "Please correct the errors below." +msgstr "Ուղղեք ստորև նշված սխալները․" + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "" +"Մուտքագրեք նոր գաղտնաբառ %(username)s օգտագործողի համար։" + +msgid "Welcome," +msgstr "Բարի գալուստ, " + +msgid "View site" +msgstr "Դիտել կայքը" + +msgid "Documentation" +msgstr "Դոկումենտացիա" + +msgid "Log out" +msgstr "Դուրս գալ" + +#, python-format +msgid "Add %(name)s" +msgstr "Ավելացնել %(name)s" + +msgid "History" +msgstr "Պատմություն" + +msgid "View on site" +msgstr "Դիտել կայքում" + +msgid "Filter" +msgstr "Ֆիլտրել" + +msgid "Remove from sorting" +msgstr "Հեռացնել դասակարգումից" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Դասակարգման առաջնություն՝ %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Toggle sorting" + +msgid "Delete" +msgstr "Հեռացնել" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"%(object_name)s '%(escaped_object)s'֊ի հեռացումը կարող է հանգեցնել նրա հետ " +"կապված օբյեկտների հեռացմանը, բայց դուք չունեք իրավունք հեռացնել այդ տիպի " +"օբյեկտներ․" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"%(object_name)s '%(escaped_object)s'֊ը հեռացնելու համար կարող է անհրաժեշտ " +"լինել հեռացնել նրա հետ կապված պաշտպանված օբյեկտները։" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Համոզված ե՞ք, որ ուզում եք հեռացնել %(object_name)s \"%(escaped_object)s\"֊" +"ը։ նրա հետ կապված այս բոլոր օբյեկտները կհեռացվեն․" + +msgid "Objects" +msgstr "Օբյեկտներ" + +msgid "Yes, I'm sure" +msgstr "Այո, ես համոզված եմ" + +msgid "No, take me back" +msgstr "Ոչ, տարեք ենձ ետ" + +msgid "Delete multiple objects" +msgstr "Հեռացնել մի քանի օբյեկտ" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"%(objects_name)s֊ների հեռացումը կարող է հանգեցնել նրա հետ կապված օբյեկտների " +"հեռացմանը, բայց դուք չունեք իրավունք հեռացնել այդ տիպի օբյեկտներ․" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"%(objects_name)s֊ը հեռացնելու համար կարող է անհրաժեշտ լինել հեռացնել նրա հետ " +"կապված պաշտպանված օբյեկտները։" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Համոզված ե՞ք, որ ուզում եք հեռացնել նշված %(objects_name)s֊ները։ Այս բոլոր " +"օբյեկտները, ինչպես նաև նրանց հետ կապված օբյեկտները կհեռացվեն․" + +msgid "View" +msgstr "" + +msgid "Delete?" +msgstr "Հեռացնե՞լ" + +#, python-format +msgid " By %(filter_title)s " +msgstr "%(filter_title)s " + +msgid "Summary" +msgstr "Ամփոփում" + +#, python-format +msgid "Models in the %(name)s application" +msgstr " %(name)s հավելվածի մոդել" + +msgid "Add" +msgstr "Ավելացնել" + +msgid "You don't have permission to view or edit anything." +msgstr "" + +msgid "Recent actions" +msgstr "" + +msgid "My actions" +msgstr "" + +msgid "None available" +msgstr "Ոչինք չկա" + +msgid "Unknown content" +msgstr "Անհայտ կոնտենտ" + +msgid "" +"Something's wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Ինչ֊որ բան այն չէ ձեր տվյալների բազայի հետ։ Համոզվեք, որ համապատասխան " +"աղյուսակները ստեղծվել են և համոզվեք, որ համապատասխան օգտագործողը կարող է " +"կարդալ բազան։" + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Դուք մուտք եք գործել որպես %(username)s, բայց իրավունք չունեք դիտելու այս " +"էջը։ Ցանկանում ե՞ք մուտք գործել որպես այլ օգտագործող" + +msgid "Forgotten your password or username?" +msgstr "Մոռացել ե՞ք օգտագործողի անունը կամ գաղտնաբառը" + +msgid "Date/time" +msgstr "Ամսաթիվ/Ժամանակ" + +msgid "User" +msgstr "Օգտագործող" + +msgid "Action" +msgstr "Գործողություն" + +msgid "" +"This object doesn't have a change history. It probably wasn't added via this " +"admin site." +msgstr "" +"Այս օբյեկտը չունի փոփոխման պատմություն։ Այն հավանաբար ավելացված չէ " +"ադմինիստրավորման էջից։" + +msgid "Show all" +msgstr "Ցույց տալ բոլորը" + +msgid "Save" +msgstr "Պահպանել" + +msgid "Popup closing..." +msgstr "Ելնող պատուհանը փակվում է" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Փոփոխել ընտրված %(model)s տիպի օբյեկտը" + +#, python-format +msgid "View selected %(model)s" +msgstr "" + +#, python-format +msgid "Add another %(model)s" +msgstr "Ավելացնել այլ %(model)s տիպի օբյեկտ" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Հեռացնել ընտրված %(model)s տիպի օբյեկտը" + +msgid "Search" +msgstr "Փնտրել" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s արդյունք" +msgstr[1] "%(counter)s արդյունքներ" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s ընդհանուր" + +msgid "Save as new" +msgstr "Պահպանել որպես նոր" + +msgid "Save and add another" +msgstr "Պահպանել և ավելացնել նորը" + +msgid "Save and continue editing" +msgstr "Պահպանել և շարունակել խմբագրել" + +msgid "Save and view" +msgstr "" + +msgid "Close" +msgstr "" + +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Շնորհակալություն մեր կայքում ինչ֊որ ժամանակ ծախսելու համար։" + +msgid "Log in again" +msgstr "Մուտք գործել նորից" + +msgid "Password change" +msgstr "Փոխել գաղտնաբառը" + +msgid "Your password was changed." +msgstr "Ձեր գաղտնաբառը փոխվել է" + +msgid "" +"Please enter your old password, for security's sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Մուտքագրեք ձեր հին գաղտնաբառը։ Անվտանգության նկատառումներով մուտքագրեք ձեր " +"նոր գաղտնաբառը երկու անգամ, որպեսզի մենք համոզված լինենք, որ այն ճիշտ է " +"հավաքված։" + +msgid "Change my password" +msgstr "Փոխել իմ գաղտնաբառը" + +msgid "Password reset" +msgstr "Գաղտնաբառի փոփոխում" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Ձեր գաղտնաբառը պահպանված է․ Կարող եք մուտք գործել։" + +msgid "Password reset confirmation" +msgstr "Գաղտնաբառի փոփոխման հաստատում" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Մուտքագրեք ձեր նոր գաղտնաբառը երկու անգամ, որպեսզի մենք համոզված լինենք, որ " +"այն ճիշտ է հավաքված։" + +msgid "New password:" +msgstr "Նոր գաղտնաբառ․" + +msgid "Confirm password:" +msgstr "Նոր գաղտնաբառը նորից․" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Գաղտնաբառի փոփոխման հղում է սխալ է, հավանաբար այն արդեն օգտագործվել է․ Դուք " +"կարող եք ստանալ նոր հղում։" + +msgid "" +"We've emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Մենք ուղարկեցինք ձեր էլեկտրոնային փոստի հասցեին գաղտնաբառը փոփոխելու " +"հրահանգներ․ Դուք շուտով կստանաք դրանք։" + +msgid "" +"If you don't receive an email, please make sure you've entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Եթե դուք չեք ստացել էլեկտրոնային նամակ, համոզվեք, որ հավաքել եք այն հասցեն, " +"որով գրանցվել եք և ստուգեք ձեր սպամի թղթապանակը։" + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Դուք ստացել եք այս նամակը, քանի որ ցանկացել եք փոխել ձեր գաղտնաբառը " +"%(site_name)s կայքում։" + +msgid "Please go to the following page and choose a new password:" +msgstr "Բացեք հետևյալ էջը և ընտրեք նոր գաղտնաբառ։" + +msgid "Your username, in case you've forgotten:" +msgstr "Եթե դուք մոռացել եք ձեր օգտագործողի անունը․" + +msgid "Thanks for using our site!" +msgstr "Շնորհակալություն մեր կայքից օգտվելու համար։" + +#, python-format +msgid "The %(site_name)s team" +msgstr "%(site_name)s կայքի թիմ" + +msgid "" +"Forgotten your password? Enter your email address below, and we'll email " +"instructions for setting a new one." +msgstr "" +"Մոռացել ե՞ք ձեր գաղտնաբառը Մուտքագրեք ձեր էլեկտրոնային փոստի հասցեն և մենք " +"կուղարկենք ձեզ հրահանգներ նորը ստանալու համար։" + +msgid "Email address:" +msgstr "Email հասցե․" + +msgid "Reset my password" +msgstr "Փոխել գաղտնաբառը" + +msgid "All dates" +msgstr "Բոլոր ամսաթվերը" + +#, python-format +msgid "Select %s" +msgstr "Ընտրեք %s" + +#, python-format +msgid "Select %s to change" +msgstr "Ընտրեք %s փոխելու համար" + +#, python-format +msgid "Select %s to view" +msgstr "" + +msgid "Date:" +msgstr "Ամսաթիվ․" + +msgid "Time:" +msgstr "Ժամանակ․" + +msgid "Lookup" +msgstr "Որոնում" + +msgid "Currently:" +msgstr "Հիմա․" + +msgid "Change:" +msgstr "Փոփոխել" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..0e58ee9e --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ja/LC_MESSAGES/django 3.po @@ -0,0 +1,751 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# akiyoko , 2020 +# Claude Paroz , 2016 +# Goto Hayato , 2019 +# Hiroki Sawano, 2022 +# Jannis Leidel , 2011 +# Masaya, 2023 +# Shinichi Katsumata , 2019 +# Shinya Okano , 2012-2018,2021,2023 +# Taichi Taniguchi, 2022 +# Takuro Onoue , 2020 +# Takuya N , 2020 +# Tetsuya Morimoto , 2011 +# 上田慶祐 , 2015 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 07:05+0000\n" +"Last-Translator: Shinya Okano , 2012-2018,2021,2023\n" +"Language-Team: Japanese (http://www.transifex.com/django/django/language/" +"ja/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ja\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "選択された %(verbose_name_plural)s の削除" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d 個の %(items)s を削除しました。" + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "%(name)s が削除できません" + +msgid "Are you sure?" +msgstr "よろしいですか?" + +msgid "Administration" +msgstr "管理" + +msgid "All" +msgstr "全て" + +msgid "Yes" +msgstr "はい" + +msgid "No" +msgstr "いいえ" + +msgid "Unknown" +msgstr "不明" + +msgid "Any date" +msgstr "いつでも" + +msgid "Today" +msgstr "今日" + +msgid "Past 7 days" +msgstr "過去 7 日間" + +msgid "This month" +msgstr "今月" + +msgid "This year" +msgstr "今年" + +msgid "No date" +msgstr "日付なし" + +msgid "Has date" +msgstr "日付あり" + +msgid "Empty" +msgstr "空" + +msgid "Not empty" +msgstr "空でない" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"スタッフアカウントの正しい%(username)sとパスワードを入力してください。どちら" +"のフィールドも大文字と小文字は区別されます。" + +msgid "Action:" +msgstr "操作:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "%(verbose_name)s の追加" + +msgid "Remove" +msgstr "削除" + +msgid "Addition" +msgstr "追加" + +msgid "Change" +msgstr "変更" + +msgid "Deletion" +msgstr "削除" + +msgid "action time" +msgstr "操作時刻" + +msgid "user" +msgstr "ユーザー" + +msgid "content type" +msgstr "コンテンツタイプ" + +msgid "object id" +msgstr "オブジェクト ID" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "オブジェクトの文字列表現" + +msgid "action flag" +msgstr "操作種別" + +msgid "change message" +msgstr "変更メッセージ" + +msgid "log entry" +msgstr "ログエントリー" + +msgid "log entries" +msgstr "ログエントリー" + +#, python-format +msgid "Added “%(object)s”." +msgstr "“%(object)s” を追加しました。" + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "“%(object)s” を変更しました — %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "“%(object)s” を削除しました。" + +msgid "LogEntry Object" +msgstr "ログエントリー オブジェクト" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "{name} “{object}” を追加しました。" + +msgid "Added." +msgstr "追加されました。" + +msgid "and" +msgstr "と" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "{name} “{object}” の {fields} を変更しました。" + +#, python-brace-format +msgid "Changed {fields}." +msgstr "{fields} を変更しました。" + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "{name} “{object}” を削除しました。" + +msgid "No fields changed." +msgstr "変更はありませんでした。" + +msgid "None" +msgstr "None" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" +"複数選択するときには Control キーを押したまま選択してください。Mac は " +"Command キーを使ってください" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "{name} “{obj}” を追加しました。" + +msgid "You may edit it again below." +msgstr "以下で再度編集できます。" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "{name} “{obj}” を追加しました。別の {name} を以下から追加できます。" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "{name} “{obj}” を変更しました。以下から再度編集できます。" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "{name} “{obj}” を追加しました。続けて編集できます。" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "{name} “{obj}” を変更しました。 別の {name} を以下から追加できます。" + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "{name} “{obj}” を変更しました。" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"操作を実行するには、対象を選択する必要があります。何も変更されませんでした。" + +msgid "No action selected." +msgstr "操作が選択されていません。" + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "%(name)s “%(obj)s” を削除しました。" + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "" +"ID “%(key)s” の%(name)sは見つかりませんでした。削除された可能性があります。" + +#, python-format +msgid "Add %s" +msgstr "%s を追加" + +#, python-format +msgid "Change %s" +msgstr "%s を変更" + +#, python-format +msgid "View %s" +msgstr "%sを表示" + +msgid "Database error" +msgstr "データベースエラー" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s 個の %(name)s を変更しました。" + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s 個選択されました" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "%(cnt)s個の内ひとつも選択されていません" + +#, python-format +msgid "Change history: %s" +msgstr "変更履歴: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"%(class_name)s %(instance)s を削除するには以下の保護された関連オブジェクトを" +"削除することになります: %(related_objects)s" + +msgid "Django site admin" +msgstr "Django サイト管理" + +msgid "Django administration" +msgstr "Django 管理サイト" + +msgid "Site administration" +msgstr "サイト管理" + +msgid "Log in" +msgstr "ログイン" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s 管理" + +msgid "Page not found" +msgstr "ページが見つかりません" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "申し訳ありませんが、お探しのページは見つかりませんでした。" + +msgid "Home" +msgstr "ホーム" + +msgid "Server error" +msgstr "サーバーエラー" + +msgid "Server error (500)" +msgstr "サーバーエラー (500)" + +msgid "Server Error (500)" +msgstr "サーバーエラー (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"エラーが発生しました。サイト管理者にメールで報告されたので、修正されるまでし" +"ばらくお待ちください。" + +msgid "Run the selected action" +msgstr "選択された操作を実行" + +msgid "Go" +msgstr "実行" + +msgid "Click here to select the objects across all pages" +msgstr "全ページの項目を選択するにはここをクリック" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "%(total_count)s個ある%(module_name)s を全て選択" + +msgid "Clear selection" +msgstr "選択を解除" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "%(name)s アプリケーション内のモデル" + +msgid "Add" +msgstr "追加" + +msgid "View" +msgstr "表示" + +msgid "You don’t have permission to view or edit anything." +msgstr "表示または変更のためのパーミッションがありません。" + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"まずユーザー名とパスワードを登録してください。その後詳細情報が編集可能になり" +"ます。" + +msgid "Enter a username and password." +msgstr "ユーザー名とパスワードを入力してください。" + +msgid "Change password" +msgstr "パスワードの変更" + +msgid "Please correct the error below." +msgid_plural "Please correct the errors below." +msgstr[0] "下記のエラーを修正してください。" + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "" +"%(username)sさんの新しいパスワードを入力してください。" + +msgid "Skip to main content" +msgstr "スキップしてメインコンテンツへ" + +msgid "Welcome," +msgstr "ようこそ" + +msgid "View site" +msgstr "サイトを表示" + +msgid "Documentation" +msgstr "ドキュメント" + +msgid "Log out" +msgstr "ログアウト" + +msgid "Breadcrumbs" +msgstr "パンくずリスト" + +#, python-format +msgid "Add %(name)s" +msgstr "%(name)s を追加" + +msgid "History" +msgstr "履歴" + +msgid "View on site" +msgstr "サイト上で表示" + +msgid "Filter" +msgstr "フィルター" + +msgid "Clear all filters" +msgstr "全てのフィルターを解除" + +msgid "Remove from sorting" +msgstr "ソート条件から外します" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "ソート優先順位: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "昇順降順を切り替えます" + +msgid "Toggle theme (current theme: auto)" +msgstr "テーマを切り替え (現在のテーマ: 自動)" + +msgid "Toggle theme (current theme: light)" +msgstr "テーマを切り替え (現在のテーマ: ライト)" + +msgid "Toggle theme (current theme: dark)" +msgstr "テーマを切り替え (現在のテーマ: ダーク)" + +msgid "Delete" +msgstr "削除" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"%(object_name)s '%(escaped_object)s' の削除時に関連づけられたオブジェクトも削" +"除しようとしましたが、あなたのアカウントには以下のタイプのオブジェクトを削除" +"するパーミッションがありません:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"%(object_name)s '%(escaped_object)s' を削除するには以下の保護された関連オブ" +"ジェクトを削除することになります:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"%(object_name)s \"%(escaped_object)s\"を削除しますか? 関連づけられている以下" +"のオブジェクトも全て削除されます:" + +msgid "Objects" +msgstr "オブジェクト" + +msgid "Yes, I’m sure" +msgstr "はい、大丈夫です" + +msgid "No, take me back" +msgstr "戻る" + +msgid "Delete multiple objects" +msgstr "複数のオブジェクトを削除します" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"選択した %(objects_name)s を削除すると関連するオブジェクトも削除しますが、あ" +"なたのアカウントは以下のオブジェクト型を削除する権限がありません:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"選択した %(objects_name)s を削除すると以下の保護された関連オブジェクトを削除" +"することになります:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"本当に選択した %(objects_name)s を削除しますか? 以下の全てのオブジェクトと関" +"連する要素が削除されます:" + +msgid "Delete?" +msgstr "削除しますか?" + +#, python-format +msgid " By %(filter_title)s " +msgstr "%(filter_title)s で絞り込む" + +msgid "Summary" +msgstr "概要" + +msgid "Recent actions" +msgstr "最近行った操作" + +msgid "My actions" +msgstr "自分の操作" + +msgid "None available" +msgstr "利用不可" + +msgid "Unknown content" +msgstr "不明なコンテント" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"データベースのインストールに問題があります。適切なデータベーステーブルが作ら" +"れているか、適切なユーザーがデータベースを読み込み可能かを確認してください。" + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"あなたは %(username)s として認証されましたが、このページへのアクセス許可があ" +"りません。他のアカウントでログインしますか?" + +msgid "Forgotten your password or username?" +msgstr "パスワードまたはユーザー名を忘れましたか?" + +msgid "Toggle navigation" +msgstr "ナビゲーションを切り替えます" + +msgid "Sidebar" +msgstr "サイドバー" + +msgid "Start typing to filter…" +msgstr "絞り込みの入力..." + +msgid "Filter navigation items" +msgstr "ナビゲーション項目の絞り込み" + +msgid "Date/time" +msgstr "日付/時刻" + +msgid "User" +msgstr "ユーザー" + +msgid "Action" +msgstr "操作" + +msgid "entry" +msgid_plural "entries" +msgstr[0] "エントリー" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"このオブジェクトには変更履歴がありません。おそらくこの管理サイトで追加したも" +"のではありません。" + +msgid "Show all" +msgstr "全件表示" + +msgid "Save" +msgstr "保存" + +msgid "Popup closing…" +msgstr "ポップアップを閉じています..." + +msgid "Search" +msgstr "検索" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "結果 %(counter)s" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "全 %(full_result_count)s 件" + +msgid "Save as new" +msgstr "新規保存" + +msgid "Save and add another" +msgstr "保存してもう一つ追加" + +msgid "Save and continue editing" +msgstr "保存して編集を続ける" + +msgid "Save and view" +msgstr "保存して表示" + +msgid "Close" +msgstr "閉じる" + +#, python-format +msgid "Change selected %(model)s" +msgstr "選択された %(model)s の変更" + +#, python-format +msgid "Add another %(model)s" +msgstr "%(model)s の追加" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "選択された %(model)s を削除" + +#, python-format +msgid "View selected %(model)s" +msgstr "選択された %(model)s を表示" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "ご利用ありがとうございました。" + +msgid "Log in again" +msgstr "もう一度ログイン" + +msgid "Password change" +msgstr "パスワードの変更" + +msgid "Your password was changed." +msgstr "あなたのパスワードは変更されました" + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"セキュリティ上の理由から元のパスワードの入力が必要です。新しいパスワードは正" +"しく入力したか確認できるように二度入力してください。" + +msgid "Change my password" +msgstr "パスワードの変更" + +msgid "Password reset" +msgstr "パスワードをリセット" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "パスワードがセットされました。ログインしてください。" + +msgid "Password reset confirmation" +msgstr "パスワードリセットの確認" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "確認のために、新しいパスワードを二回入力してください。" + +msgid "New password:" +msgstr "新しいパスワード:" + +msgid "Confirm password:" +msgstr "新しいパスワード (確認用) :" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"パスワードリセットのリンクが不正です。おそらくこのリンクは既に使われていま" +"す。もう一度パスワードリセットしてください。" + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"入力されたメールアドレスを持つアカウントが存在する場合、パスワードを設定する" +"ためのメールを送信しました。すぐに届くはずです。" + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"メールが届かない場合は、登録したメールアドレスを入力したか確認し、スパムフォ" +"ルダに入っていないか確認してください。" + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"このメールは %(site_name)s で、あなたのアカウントのパスワードリセットが要求さ" +"れたため、送信されました。" + +msgid "Please go to the following page and choose a new password:" +msgstr "次のページで新しいパスワードを選んでください:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "あなたのユーザー名 (もし忘れていたら):" + +msgid "Thanks for using our site!" +msgstr "ご利用ありがとうございました!" + +#, python-format +msgid "The %(site_name)s team" +msgstr " %(site_name)s チーム" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"パスワードを忘れましたか? メールアドレスを以下に入力すると、新しいパスワード" +"の設定方法をお知らせします。" + +msgid "Email address:" +msgstr "メールアドレス:" + +msgid "Reset my password" +msgstr "パスワードをリセット" + +msgid "All dates" +msgstr "いつでも" + +#, python-format +msgid "Select %s" +msgstr "%s を選択" + +#, python-format +msgid "Select %s to change" +msgstr "変更する %s を選択" + +#, python-format +msgid "Select %s to view" +msgstr "表示する%sを選択" + +msgid "Date:" +msgstr "日付:" + +msgid "Time:" +msgstr "時刻:" + +msgid "Lookup" +msgstr "検索" + +msgid "Currently:" +msgstr "現在の値:" + +msgid "Change:" +msgstr "変更後:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/kk/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/kk/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..ba1062eb --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/kk/LC_MESSAGES/django 3.po @@ -0,0 +1,695 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Baurzhan Muftakhidinov , 2015 +# Leo Trubach , 2017 +# Nurlan Rakhimzhanov , 2011 +# yun_man_ger , 2011 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2019-01-18 00:36+0000\n" +"Last-Translator: Ramiro Morales\n" +"Language-Team: Kazakh (http://www.transifex.com/django/django/language/kk/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: kk\n" +"Plural-Forms: nplurals=2; plural=(n!=1);\n" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Таңдалған %(count)d %(items)s элемент өшірілді." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "%(name)s өшіру мүмкін емес" + +msgid "Are you sure?" +msgstr "Осыған сенімдісіз бе?" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Таңдалған %(verbose_name_plural)s өшірілді" + +msgid "Administration" +msgstr "" + +msgid "All" +msgstr "Барлығы" + +msgid "Yes" +msgstr "Иә" + +msgid "No" +msgstr "Жоқ" + +msgid "Unknown" +msgstr "Белгісіз" + +msgid "Any date" +msgstr "Кез келген күн" + +msgid "Today" +msgstr "Бүгін" + +msgid "Past 7 days" +msgstr "Өткен 7 күн" + +msgid "This month" +msgstr "Осы ай" + +msgid "This year" +msgstr "Осы жыл" + +msgid "No date" +msgstr "Күні жоқ" + +msgid "Has date" +msgstr "Күні бар" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" + +msgid "Action:" +msgstr "Әрекет:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Тағы басқа %(verbose_name)s кос" + +msgid "Remove" +msgstr "Өшіру" + +msgid "Addition" +msgstr "" + +msgid "Change" +msgstr "Өзгетру" + +msgid "Deletion" +msgstr "" + +msgid "action time" +msgstr "әрекет уақыты" + +msgid "user" +msgstr "" + +msgid "content type" +msgstr "" + +msgid "object id" +msgstr "объекттің id-i" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "объекттің repr-i" + +msgid "action flag" +msgstr "әрекет белгісі" + +msgid "change message" +msgstr "хабарламаны өзгерту" + +msgid "log entry" +msgstr "Жорнал жазуы" + +msgid "log entries" +msgstr "Жорнал жазулары" + +#, python-format +msgid "Added \"%(object)s\"." +msgstr "" + +#, python-format +msgid "Changed \"%(object)s\" - %(changes)s" +msgstr "" + +#, python-format +msgid "Deleted \"%(object)s.\"" +msgstr "" + +msgid "LogEntry Object" +msgstr "" + +#, python-brace-format +msgid "Added {name} \"{object}\"." +msgstr "" + +msgid "Added." +msgstr "" + +msgid "and" +msgstr "және" + +#, python-brace-format +msgid "Changed {fields} for {name} \"{object}\"." +msgstr "" + +#, python-brace-format +msgid "Changed {fields}." +msgstr "" + +#, python-brace-format +msgid "Deleted {name} \"{object}\"." +msgstr "" + +msgid "No fields changed." +msgstr "Ешқандай толтырма өзгермеді." + +msgid "None" +msgstr "Ешнәрсе" + +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was added successfully." +msgstr "" + +msgid "You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." +msgstr "" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Бірнәрсені өзгерту үшін бірінші оларды таңдау керек. Ешнәрсе өзгертілмеді." + +msgid "No action selected." +msgstr "Ешқандай әрекет таңдалмады." + +#, python-format +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "%(name)s \"%(obj)s\" сәтті өшірілді." + +#, python-format +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" +msgstr "" + +#, python-format +msgid "Add %s" +msgstr "%s қосу" + +#, python-format +msgid "Change %s" +msgstr "%s өзгету" + +#, python-format +msgid "View %s" +msgstr "" + +msgid "Database error" +msgstr "Мәліметтер базасының қатесі" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "" +"one: %(count)s %(name)s өзгертілді.\n" +"\n" +"other: %(count)s %(name)s таңдалғандарының барі өзгертілді." +msgstr[1] "" +"one: %(count)s %(name)s өзгертілді.\n" +"\n" +"other: %(count)s %(name)s таңдалғандарының барі өзгертілді." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "" +"one: %(total_count)s таңдалды\n" +"\n" +"other: Барлығы %(total_count)s таңдалды" +msgstr[1] "" +"one: %(total_count)s таңдалды\n" +"\n" +"other: Барлығы %(total_count)s таңдалды" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 of %(cnt)s-ден 0 таңдалды" + +#, python-format +msgid "Change history: %s" +msgstr "Өзгерес тарихы: %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" + +msgid "Django site admin" +msgstr "Даңғо сайтының әкімі" + +msgid "Django administration" +msgstr "Даңғо әкімшілігі" + +msgid "Site administration" +msgstr "Сайт әкімшілігі" + +msgid "Log in" +msgstr "Кіру" + +#, python-format +msgid "%(app)s administration" +msgstr "" + +msgid "Page not found" +msgstr "Бет табылмады" + +msgid "We're sorry, but the requested page could not be found." +msgstr "Кешірім сұраймыз, сіздің сұраған бетіңіз табылмады." + +msgid "Home" +msgstr "Негізгі" + +msgid "Server error" +msgstr "Сервердің қатесі" + +msgid "Server error (500)" +msgstr "Сервердің қатесі (500)" + +msgid "Server Error (500)" +msgstr "Сервердің қатесі (500)" + +msgid "" +"There's been an error. It's been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" + +msgid "Run the selected action" +msgstr "Таңдалған әрәкетті іске қосу" + +msgid "Go" +msgstr "Алға" + +msgid "Click here to select the objects across all pages" +msgstr "Осы беттегі барлық объекттерді таңдау үшін осы жерді шертіңіз" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Осылардың %(total_count)s %(module_name)s барлығын таңдау" + +msgid "Clear selection" +msgstr "Белгілерді өшіру" + +msgid "" +"First, enter a username and password. Then, you'll be able to edit more user " +"options." +msgstr "" +"Алдымен, пайдаланушының атын және құпия сөзді енгізіңіз. Содан соң, тағы " +"басқа пайдаланушы параметрлерін енгізе аласыз." + +msgid "Enter a username and password." +msgstr "Пайдаланушының атын және құпия сөзді енгізіңіз." + +msgid "Change password" +msgstr "Құпия сөзді өзгерту" + +msgid "Please correct the error below." +msgstr "" + +msgid "Please correct the errors below." +msgstr "" + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "" +"%(username)s пайдаланушы үшін жаңа құпия сөзді енгізіңіз." + +msgid "Welcome," +msgstr "Қош келдіңіз," + +msgid "View site" +msgstr "" + +msgid "Documentation" +msgstr "Құжаттама" + +msgid "Log out" +msgstr "Шығу" + +#, python-format +msgid "Add %(name)s" +msgstr "%(name)s қосу" + +msgid "History" +msgstr "Тарих" + +msgid "View on site" +msgstr "Сайтта көру" + +msgid "Filter" +msgstr "Сүзгіз" + +msgid "Remove from sorting" +msgstr "" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "" + +msgid "Toggle sorting" +msgstr "" + +msgid "Delete" +msgstr "Өшіру" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"%(object_name)s '%(escaped_object)s' объектты өшіруы байланысты объекттерін " +"өшіруді қажет етеді, бырақ сізде осындай объектерді өшіру рұқсаты жоқ:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"%(object_name)s '%(escaped_object)s' объектті өшіру осындай байлансты " +"объекттерды өшіруді қажет етеді:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"%(object_name)s \"%(escaped_object)s\" объекттерді өшіруге сенімдісіз бе? " +"Бұл байланысты элементтер де өшіріледі:" + +msgid "Objects" +msgstr "" + +msgid "Yes, I'm sure" +msgstr "Иә, сенімдімін" + +msgid "No, take me back" +msgstr "" + +msgid "Delete multiple objects" +msgstr "Бірнеше объекттерді өшіру" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"%(objects_name)s объектты өшіруы байланысты объекттерін өшіруді қажет етеді, " +"бырақ сізде осындай объектерді өшіру рұқсаты жоқ:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Таңдалған %(objects_name)s-ді(ы) өшіру, онымен байланыстағы қорғалған " +"объектілердің барлығын жояды:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Таңдаған %(objects_name)s объектіңізді өшіруге сенімдісіз бе? Себебі, " +"таңдағын объектіліріңіз және онымен байланыстағы барлық элементтер жойылады:" + +msgid "View" +msgstr "" + +msgid "Delete?" +msgstr "Өшіру?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " %(filter_title)s " + +msgid "Summary" +msgstr "" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "" + +msgid "Add" +msgstr "Қосу" + +msgid "You don't have permission to view or edit anything." +msgstr "" + +msgid "Recent actions" +msgstr "" + +msgid "My actions" +msgstr "" + +msgid "None available" +msgstr "Қол жетімдісі жоқ" + +msgid "Unknown content" +msgstr "Белгісіз мазмұн" + +msgid "" +"Something's wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Дерекқор орнатуыңызда бір қате бар. Дерекқор кестелері дұрыс құрылғаның және " +"дерекқор көрсетілген дерекқор пайдаланушыда оқұ рұқсаты бар." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" + +msgid "Forgotten your password or username?" +msgstr "" + +msgid "Date/time" +msgstr "Өшіру/Уақыт" + +msgid "User" +msgstr "Қолданушы" + +msgid "Action" +msgstr "Әрекет" + +msgid "" +"This object doesn't have a change history. It probably wasn't added via this " +"admin site." +msgstr "" +"Бұл объекттың өзгерту тарихы жоқ. Мүмкін ол бұл сайт арқылы енгізілген жоқ." + +msgid "Show all" +msgstr "Барлығын көрсету" + +msgid "Save" +msgstr "Сақтау" + +msgid "Popup closing…" +msgstr "" + +msgid "Search" +msgstr "Іздеу" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s нәтиже" +msgstr[1] "%(counter)s нәтиже" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "Барлығы %(full_result_count)s" + +msgid "Save as new" +msgstr "Жаңадан сақтау" + +msgid "Save and add another" +msgstr "Сақта және жаңасын қос" + +msgid "Save and continue editing" +msgstr "Сақта және өзгертуді жалғастыр" + +msgid "Save and view" +msgstr "" + +msgid "Close" +msgstr "" + +#, python-format +msgid "Change selected %(model)s" +msgstr "" + +#, python-format +msgid "Add another %(model)s" +msgstr "" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "" + +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Бүгін Веб-торапқа уақыт бөлгеніңіз үшін рахмет." + +msgid "Log in again" +msgstr "Қайтадан кіріңіз" + +msgid "Password change" +msgstr "Құпия сөзді өзгерту" + +msgid "Your password was changed." +msgstr "Құпия сөзіңіз өзгертілді." + +msgid "" +"Please enter your old password, for security's sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Ескі құпия сөзіңізді енгізіңіз, содан сон сенімді болу үшін жаңа құпия " +"сөзіңізді екі рет енгізіңіз." + +msgid "Change my password" +msgstr "Құпия сөзімді өзгерту" + +msgid "Password reset" +msgstr "Құпия сөзді өзгерту" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Сіздің құпия сөзіңіз енгізілді. Жүйеге кіруіңізге болады." + +msgid "Password reset confirmation" +msgstr "Құпия сөзді өзгерту растау" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "Сенімді болу үшін жаңа құпия сөзіңізді екі рет енгізіңіз." + +msgid "New password:" +msgstr "Жаңа құпия сөз:" + +msgid "Confirm password:" +msgstr "Құпия сөз (растау):" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Құпия сөзді өзгерту байланыс дұрыс емес, мүмкін ол осыған дейін " +"пайдаланылды. Жаңа құпия сөзді өзгерту сұрау жіберіңіз." + +msgid "" +"We've emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" + +msgid "" +"If you don't receive an email, please make sure you've entered the address " +"you registered with, and check your spam folder." +msgstr "" + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" + +msgid "Please go to the following page and choose a new password:" +msgstr "Жаңа құпия сөзді тандау үшін мынау бетке кіріңіз:" + +msgid "Your username, in case you've forgotten:" +msgstr "Егер ұмытып қалған болсаңыз, пайдалануш атыңыз:" + +msgid "Thanks for using our site!" +msgstr "Біздің веб-торабын қолданғаныңыз үшін рахмет!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "%(site_name)s тобы" + +msgid "" +"Forgotten your password? Enter your email address below, and we'll email " +"instructions for setting a new one." +msgstr "" + +msgid "Email address:" +msgstr "" + +msgid "Reset my password" +msgstr "Құпия сөзді жаңала" + +msgid "All dates" +msgstr "Барлық мерзімдер" + +#, python-format +msgid "Select %s" +msgstr "%s таңда" + +#, python-format +msgid "Select %s to change" +msgstr "%s өзгерту үщін таңда" + +#, python-format +msgid "Select %s to view" +msgstr "" + +msgid "Date:" +msgstr "Күнтізбелік күн:" + +msgid "Time:" +msgstr "Уақыт:" + +msgid "Lookup" +msgstr "Іздеу" + +msgid "Currently:" +msgstr "" + +msgid "Change:" +msgstr "" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..ad9d4a0e --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ko/LC_MESSAGES/django 3.po @@ -0,0 +1,768 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Jiyoon, Ha , 2016 +# DONGHO JEONG , 2020 +# 코딩 영, 2021 +# Geonho Kim / Leo Kim , 2019 +# Gihun Ham , 2018 +# Hang Park , 2019 +# Hoseok Lee , 2016 +# Ian Y. Choi , 2015,2019 +# Jaehong Kim , 2011 +# Jannis Leidel , 2011 +# Jay Oh , 2020 +# Le Tartuffe , 2014,2016 +# LEE Hwanyong , 2023 +# Seho Noh , 2018 +# Seacbyul Lee , 2017 +# Taesik Yoon , 2015 +# 정훈 이, 2021 +# 박태진, 2021 +# Yang Chan Woo , 2019 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 07:05+0000\n" +"Last-Translator: LEE Hwanyong , 2023\n" +"Language-Team: Korean (http://www.transifex.com/django/django/language/ko/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ko\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "선택된 %(verbose_name_plural)s 을/를 삭제합니다." + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d개의 %(items)s 을/를 성공적으로 삭제하였습니다." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "%(name)s를 삭제할 수 없습니다." + +msgid "Are you sure?" +msgstr "확실합니까?" + +msgid "Administration" +msgstr "관리" + +msgid "All" +msgstr "모두" + +msgid "Yes" +msgstr "예" + +msgid "No" +msgstr "아니오" + +msgid "Unknown" +msgstr "알 수 없습니다." + +msgid "Any date" +msgstr "언제나" + +msgid "Today" +msgstr "오늘" + +msgid "Past 7 days" +msgstr "지난 7일" + +msgid "This month" +msgstr "이번 달" + +msgid "This year" +msgstr "이번 해" + +msgid "No date" +msgstr "날짜 없음" + +msgid "Has date" +msgstr "날짜 있음" + +msgid "Empty" +msgstr "비어 있음" + +msgid "Not empty" +msgstr "비어 있지 않음" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"관리자 계정의 %(username)s 와 비밀번호를 입력해주세요. 대소문자를 구분해서 입" +"력해주세요." + +msgid "Action:" +msgstr "액션:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "%(verbose_name)s 더 추가하기" + +msgid "Remove" +msgstr "삭제하기" + +msgid "Addition" +msgstr "추가" + +msgid "Change" +msgstr "변경" + +msgid "Deletion" +msgstr "삭제" + +msgid "action time" +msgstr "액션 타임" + +msgid "user" +msgstr "사용자" + +msgid "content type" +msgstr "콘텐츠 타입" + +msgid "object id" +msgstr "오브젝트 아이디" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "오브젝트 표현" + +msgid "action flag" +msgstr "액션 플래그" + +msgid "change message" +msgstr "메시지 변경" + +msgid "log entry" +msgstr "로그 엔트리" + +msgid "log entries" +msgstr "로그 엔트리" + +#, python-format +msgid "Added “%(object)s”." +msgstr "\"%(object)s\"이/가 추가되었습니다." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "\"%(object)s\"이/가 \"%(changes)s\"(으)로 변경되었습니다." + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "%(object)s를 삭제했습니다." + +msgid "LogEntry Object" +msgstr "로그 엔트리 객체" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "{name} “{object}개체”를 추가했습니다." + +msgid "Added." +msgstr "추가되었습니다." + +msgid "and" +msgstr "또한" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "{name} “{object}개체”의 {fields}필드를 변경했습니다." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "{fields}가 변경되었습니다." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "{name} “{object}개체”를 삭제했습니다." + +msgid "No fields changed." +msgstr "변경된 필드가 없습니다." + +msgid "None" +msgstr "없음" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" +"하나 이상을 선택하려면 \"Control\" 키를 누른 채로 선택해주세요. Mac의 경우에" +"는 \"Command\" 키를 눌러주세요." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "{name} \"{obj}\"가 성공적으로 추가되었습니다." + +msgid "You may edit it again below." +msgstr "아래 내용을 수정해야 합니다." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"{name} \"{obj}\"가 성공적으로 추가되었습니다. 아래에서 다른 {name}을 추가할 " +"수 있습니다." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" +"{name} \"{obj}\"가 성공적으로 변경되었습니다. 아래에서 다시 수정할 수 있습니" +"다." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" +"{name} \"{obj}\"가 성공적으로 추가되었습니다. 아래에서 다시 수정할 수 있습니" +"다." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" +"{name} \"{obj}\"가 성공적으로 변경되었습니다. 아래에서 다른 {name}을 추가할 " +"수 있습니다." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "{name} \"{obj}\"가 성공적으로 변경되었습니다." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"항목들에 액션을 적용하기 위해선 먼저 항목들이 선택되어 있어야 합니다. 아무 항" +"목도 변경되지 않았습니다." + +msgid "No action selected." +msgstr "액션이 선택되지 않았습니다." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "%(name)s \"%(obj)s\"이/가 성공적으로 삭제되었습니다." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "" +"ID \"%(key)s\"을/를 지닌%(name)s이/가 존재하지 않습니다. 삭제된 값이 아닌지 " +"확인해주세요." + +#, python-format +msgid "Add %s" +msgstr "%s 추가" + +#, python-format +msgid "Change %s" +msgstr "%s 변경" + +#, python-format +msgid "View %s" +msgstr "뷰 %s" + +msgid "Database error" +msgstr "데이터베이스 오류" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s개의 %(name)s이/가 변경되었습니다." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "총 %(total_count)s개가 선택되었습니다." + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "%(cnt)s 중 아무것도 선택되지 않았습니다." + +#, python-format +msgid "Change history: %s" +msgstr "변경 히스토리: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"%(class_name)s %(instance)s 을/를 삭제하려면 다음 보호상태의 연관된 오브젝트" +"들을 삭제해야 합니다: %(related_objects)s" + +msgid "Django site admin" +msgstr "Django 사이트 관리" + +msgid "Django administration" +msgstr "Django 관리" + +msgid "Site administration" +msgstr "사이트 관리" + +msgid "Log in" +msgstr "로그인" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s 관리" + +msgid "Page not found" +msgstr "페이지를 찾을 수 없습니다." + +msgid "We’re sorry, but the requested page could not be found." +msgstr "죄송합니다, 요청한 페이지를 찾을 수 없습니다." + +msgid "Home" +msgstr "홈" + +msgid "Server error" +msgstr "서버 오류" + +msgid "Server error (500)" +msgstr "서버 오류 (500)" + +msgid "Server Error (500)" +msgstr "서버 오류 (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"오류가 발생했습니다. 사이트 관리자들에게 이메일로 보고되었고 단시일 내에 수정" +"될 것입니다. 기다려주셔서 감사합니다." + +msgid "Run the selected action" +msgstr "선택한 액션을 실행합니다." + +msgid "Go" +msgstr "실행" + +msgid "Click here to select the objects across all pages" +msgstr "모든 페이지의 항목들을 선택하려면 여기를 클릭하세요." + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "%(total_count)s개의 %(module_name)s 모두를 선택합니다." + +msgid "Clear selection" +msgstr "선택 해제" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "%(name)s 애플리케이션의 모델" + +msgid "Add" +msgstr "추가" + +msgid "View" +msgstr "보기" + +msgid "You don’t have permission to view or edit anything." +msgstr "독자는 뷰 및 수정 권한이 없습니다." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"첫 번째로, 사용자명과 비밀번호를 입력하세요. 그 후, 독자는 더 많은 사용자 옵" +"션을 수정할 수 있습니다. " + +msgid "Enter a username and password." +msgstr "사용자 이름과 비밀번호를 입력하세요." + +msgid "Change password" +msgstr "비밀번호 변경" + +msgid "Please correct the error below." +msgid_plural "Please correct the errors below." +msgstr[0] "아래 오류를 수정하기 바랍니다. " + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "%(username)s 새로운 비밀번호를 입력하세요." + +msgid "Skip to main content" +msgstr "메인 콘텐츠로 이동" + +msgid "Welcome," +msgstr "환영합니다," + +msgid "View site" +msgstr "사이트 보기" + +msgid "Documentation" +msgstr "문서" + +msgid "Log out" +msgstr "로그아웃" + +msgid "Breadcrumbs" +msgstr "사용자 위치" + +#, python-format +msgid "Add %(name)s" +msgstr "%(name)s 추가" + +msgid "History" +msgstr "히스토리" + +msgid "View on site" +msgstr "사이트에서 보기" + +msgid "Filter" +msgstr "필터" + +msgid "Clear all filters" +msgstr "모든 필터 삭제" + +msgid "Remove from sorting" +msgstr "정렬에서 " + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "정렬 조건 : %(priority_number)s" + +msgid "Toggle sorting" +msgstr "정렬 " + +msgid "Toggle theme (current theme: auto)" +msgstr "테마 토글 (현재 테마:자동)" + +msgid "Toggle theme (current theme: light)" +msgstr "테마 토글 (현재 테마: 밝음)" + +msgid "Toggle theme (current theme: dark)" +msgstr "테마 토글 (현재 테마: 어두움)" + +msgid "Delete" +msgstr "삭제" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"%(object_name)s \"%(escaped_object)s\" 을/를 삭제하면서관련 오브젝트를 제거하" +"고자 했으나, 지금 사용하시는 계정은 다음 타입의 오브젝트를 제거할 권한이 없습" +"니다. :" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"%(object_name)s '%(escaped_object)s'를 삭제하려면 다음 보호상태의 연관된 오브" +"젝트들을 삭제해야 합니다." + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"정말로 %(object_name)s \"%(escaped_object)s\"을/를 삭제하시겠습니까? 다음의 " +"관련 항목들이 모두 삭제됩니다. :" + +msgid "Objects" +msgstr "오브젝트" + +msgid "Yes, I’m sure" +msgstr "네, 확신합니다. " + +msgid "No, take me back" +msgstr "아뇨, 돌려주세요." + +msgid "Delete multiple objects" +msgstr "여러 개의 오브젝트 삭제" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"연관 오브젝트 삭제로 선택한 %(objects_name)s의 삭제 중, 그러나 당신의 계정은 " +"다음 오브젝트의 삭제 권한이 없습니다. " + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"%(objects_name)s를 삭제하려면 다음 보호상태의 연관된 오브젝트들을 삭제해야 합" +"니다." + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"선택한 %(objects_name)s를 정말 삭제하시겠습니까? 다음의 오브젝트와 연관 아이" +"템들이 모두 삭제됩니다:" + +msgid "Delete?" +msgstr "삭제" + +#, python-format +msgid " By %(filter_title)s " +msgstr "%(filter_title)s (으)로" + +msgid "Summary" +msgstr "개요" + +msgid "Recent actions" +msgstr "최근 활동" + +msgid "My actions" +msgstr "나의 활동" + +msgid "None available" +msgstr "이용할 수 없습니다." + +msgid "Unknown content" +msgstr "알 수 없는 형식입니다." + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"당신의 데이터베이스 설치, 설치본에 오류가 있습니다. \n" +"적합한 데이터베이스 테이블이 생성되었는지 확인하고, 데이터베이스가 적합한 사" +"용자가 열람할 수 있는 지 확인하십시오. " + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"%(username)s 로 인증되어 있지만, 이 페이지에 접근 가능한 권한이 없습니다. 다" +"른 계정으로 로그인하시겠습니까?" + +msgid "Forgotten your password or username?" +msgstr "아이디 또는 비밀번호를 분실하였습니까?" + +msgid "Toggle navigation" +msgstr "토글 메뉴" + +msgid "Sidebar" +msgstr "사이드바" + +msgid "Start typing to filter…" +msgstr "필터에 타이핑 시작..." + +msgid "Filter navigation items" +msgstr "탐색 항목 필터링" + +msgid "Date/time" +msgstr "날짜/시간" + +msgid "User" +msgstr "사용자" + +msgid "Action" +msgstr "액션" + +msgid "entry" +msgid_plural "entries" +msgstr[0] "항목" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"이 개체는 변경 기록이 없습니다. 아마도 이 관리자 사이트를 통해 추가되지 않았" +"을 것입니다. " + +msgid "Show all" +msgstr "모두 표시" + +msgid "Save" +msgstr "저장" + +msgid "Popup closing…" +msgstr "팝업 닫는중..." + +msgid "Search" +msgstr "검색" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "결과 %(counter)s개 나옴" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "총 %(full_result_count)s건" + +msgid "Save as new" +msgstr "새로 저장" + +msgid "Save and add another" +msgstr "저장 및 다른 이름으로 추가" + +msgid "Save and continue editing" +msgstr "저장 및 편집 계속" + +msgid "Save and view" +msgstr "저장하고 조회하기" + +msgid "Close" +msgstr "닫기" + +#, python-format +msgid "Change selected %(model)s" +msgstr "선택된 %(model)s 변경" + +#, python-format +msgid "Add another %(model)s" +msgstr "%(model)s 추가" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "선택된 %(model)s 제거" + +#, python-format +msgid "View selected %(model)s" +msgstr "선택된 %(model)s 보기" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "사이트를 이용해 주셔서 고맙습니다." + +msgid "Log in again" +msgstr "다시 로그인하기" + +msgid "Password change" +msgstr "비밀번호 변경" + +msgid "Your password was changed." +msgstr "비밀번호가 변경되었습니다." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"독자의 과거 비밀번호를 입력한 후, 보안을 위해 새로운 비밀번호을 두 번 입력하" +"여 옳은 입력인 지 확인할 수 있도록 하십시오." + +msgid "Change my password" +msgstr "비밀번호 변경" + +msgid "Password reset" +msgstr "비밀번호 초기화" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "비밀번호가 설정되었습니다. 이제 로그인하세요." + +msgid "Password reset confirmation" +msgstr "비밀번호 초기화 확인" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"새로운 비밀번호를 정확히 입력했는지 확인할 수 있도록 두 번 입력하시기 바랍니" +"다." + +msgid "New password:" +msgstr "새로운 비밀번호:" + +msgid "Confirm password:" +msgstr "새로운 비밀번호 (확인):" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"비밀번호 초기화 링크가 이미 사용되어 올바르지 않습니다. 비밀번호 초기화를 다" +"시 해주세요." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"계정이 존재한다면, 독자가 입력한 이메일로 비밀번호 설정 안내문을 발송했습니" +"다. 곧 수신할 수 있을 것입니다. " + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"만약 이메일을 받지 못하였다면, 등록하신 이메일을 다시 확인하시거나 스팸 메일" +"함을 확인해주세요." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"%(site_name)s의 계정 비밀번호를 초기화하기 위한 요청으로 이 이메일이 전송되었" +"습니다." + +msgid "Please go to the following page and choose a new password:" +msgstr "다음 페이지에서 새 비밀번호를 선택하세요." + +msgid "Your username, in case you’ve forgotten:" +msgstr "사용자명:" + +msgid "Thanks for using our site!" +msgstr "사이트를 이용해 주셔서 고맙습니다." + +#, python-format +msgid "The %(site_name)s team" +msgstr "%(site_name)s 팀" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"비밀번호를 잊어버렸나요? 이메일 주소를 아래에 입력하시면 새로운 비밀번호를 설" +"정하는 절차를 이메일로 보내드리겠습니다." + +msgid "Email address:" +msgstr "이메일 주소:" + +msgid "Reset my password" +msgstr "비밀번호 초기화" + +msgid "All dates" +msgstr "언제나" + +#, python-format +msgid "Select %s" +msgstr "%s 선택" + +#, python-format +msgid "Select %s to change" +msgstr "변경할 %s 선택" + +#, python-format +msgid "Select %s to view" +msgstr "보기위한 1%s 를(을) 선택" + +msgid "Date:" +msgstr "날짜:" + +msgid "Time:" +msgstr "시각:" + +msgid "Lookup" +msgstr "찾아보기" + +msgid "Currently:" +msgstr "현재:" + +msgid "Change:" +msgstr "변경:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..abf31377 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/mk/LC_MESSAGES/django 3.po @@ -0,0 +1,726 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Dimce Grozdanoski , 2021 +# dekomote , 2015 +# Jannis Leidel , 2011 +# Martino Nikolovski, 2022 +# Vasil Vangelovski , 2016-2017,2019,2021 +# Vasil Vangelovski , 2013-2015 +# Vasil Vangelovski , 2011-2013 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:10-0500\n" +"PO-Revision-Date: 2022-05-25 07:05+0000\n" +"Last-Translator: Martino Nikolovski, 2022\n" +"Language-Team: Macedonian (http://www.transifex.com/django/django/language/" +"mk/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: mk\n" +"Plural-Forms: nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Избриши ги избраните %(verbose_name_plural)s" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Успешно беа избришани %(count)d %(items)s." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Не може да се избрише %(name)s" + +msgid "Are you sure?" +msgstr "Сигурни сте?" + +msgid "Administration" +msgstr "Администрација" + +msgid "All" +msgstr "Сите" + +msgid "Yes" +msgstr "Да" + +msgid "No" +msgstr "Не" + +msgid "Unknown" +msgstr "Непознато" + +msgid "Any date" +msgstr "Било кој датум" + +msgid "Today" +msgstr "Денеска" + +msgid "Past 7 days" +msgstr "Последните 7 дена" + +msgid "This month" +msgstr "Овој месец" + +msgid "This year" +msgstr "Оваа година" + +msgid "No date" +msgstr "Нема датум" + +msgid "Has date" +msgstr "Има датум" + +msgid "Empty" +msgstr "Празно" + +msgid "Not empty" +msgstr "" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Ве молиме внесете ги точните %(username)s и лозинка за член на сајтот. " +"Внимавајте, двете полиња се осетливи на големи и мали букви." + +msgid "Action:" +msgstr "Акција:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Додади уште %(verbose_name)s" + +msgid "Remove" +msgstr "Отстрани" + +msgid "Addition" +msgstr "Додавање" + +msgid "Change" +msgstr "Измени" + +msgid "Deletion" +msgstr "Бришење" + +msgid "action time" +msgstr "време на акција" + +msgid "user" +msgstr "корисник" + +msgid "content type" +msgstr "тип на содржина" + +msgid "object id" +msgstr "идентификационен број на објект" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "репрезентација на објект" + +msgid "action flag" +msgstr "знакче за акција" + +msgid "change message" +msgstr "измени ја пораката" + +msgid "log entry" +msgstr "ставка во записникот" + +msgid "log entries" +msgstr "ставки во записникот" + +#, python-format +msgid "Added “%(object)s”." +msgstr "Додадено “%(object)s”." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "Избришано “%(object)s.”" + +msgid "LogEntry Object" +msgstr "Запис во дневник" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "" + +msgid "Added." +msgstr "Додадено." + +msgid "and" +msgstr "и" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "" + +#, python-brace-format +msgid "Changed {fields}." +msgstr "Изменети {fields}." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "Избришан {name} “{object}”." + +msgid "No fields changed." +msgstr "Не е изменето ниедно поле." + +msgid "None" +msgstr "Ништо" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "Држете “Control” или “Command” на Mac за да изберете повеќе." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "Успешно беше додадено {name} “{obj}”." + +msgid "You may edit it again below." +msgstr "Можете повторно да го промените подолу." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Мора да се одберат предмети за да се изврши акција врз нив. Ниеден предмет " +"не беше променет." + +msgid "No action selected." +msgstr "Ниедна акција не е одбрана." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "" + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "" + +#, python-format +msgid "Add %s" +msgstr "Додади %s" + +#, python-format +msgid "Change %s" +msgstr "Измени %s" + +#, python-format +msgid "View %s" +msgstr "Погледни %s" + +msgid "Database error" +msgstr "Грешка во базата на податоци" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s ставка %(name)s беше успешно изменета." +msgstr[1] "%(count)s ставки %(name)s беа успешно изменети." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s одбран" +msgstr[1] "Сите %(total_count)s одбрани" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 од %(cnt)s избрани" + +#, python-format +msgid "Change history: %s" +msgstr "Историја на измени: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Бришењето на %(class_name)s %(instance)s бара бришење на следните заштитени " +"поврзани објекти: %(related_objects)s" + +msgid "Django site admin" +msgstr "Администрација на Џанго сајт" + +msgid "Django administration" +msgstr "Џанго администрација" + +msgid "Site administration" +msgstr "Администрација на сајт" + +msgid "Log in" +msgstr "Најава" + +#, python-format +msgid "%(app)s administration" +msgstr "Администрација на %(app)s" + +msgid "Page not found" +msgstr "Страницата не е најдена" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Се извинуваме, страница која ја побаравте не е пронајдена" + +msgid "Home" +msgstr "Дома" + +msgid "Server error" +msgstr "Грешка со серверот" + +msgid "Server error (500)" +msgstr "Грешка со серверот (500)" + +msgid "Server Error (500)" +msgstr "Грешка со серверот (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Наидовте на грешка. Известени се администраторите на страницата преку имејл " +"и би требало наскоро да биде поправена. Ви благодариме на трпението." + +msgid "Run the selected action" +msgstr "Изврши ја избраната акција" + +msgid "Go" +msgstr "Оди" + +msgid "Click here to select the objects across all pages" +msgstr "Кликнете тука за да изберете објекти низ сите страници" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Избери ги сите %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "Откажи го изборот" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Модели во %(name)s апликација" + +msgid "Add" +msgstr "Додади" + +msgid "View" +msgstr "Погледни" + +msgid "You don’t have permission to view or edit anything." +msgstr "Немате дозвола да прегледате или промените ништо" + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Прво внесете корисничко име и лозинка па потоа ќе можете да уредувате повеќе " +"опции за корисникот" + +msgid "Enter a username and password." +msgstr "Внесете корисничко име и лозинка." + +msgid "Change password" +msgstr "Промени лозинка" + +msgid "Please correct the error below." +msgstr "Ве молиме поправете ја грешката подолу." + +msgid "Please correct the errors below." +msgstr "Ве молам поправете ги грешките подолу." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "Внесете нова лозинка за корисникот %(username)s." + +msgid "Welcome," +msgstr "Добредојдовте," + +msgid "View site" +msgstr "Посети го сајтот" + +msgid "Documentation" +msgstr "Документација" + +msgid "Log out" +msgstr "Одјава" + +#, python-format +msgid "Add %(name)s" +msgstr "Додади %(name)s" + +msgid "History" +msgstr "Историја" + +msgid "View on site" +msgstr "Погледни на сајтот" + +msgid "Filter" +msgstr "Филтер" + +msgid "Clear all filters" +msgstr "Ресетирај ги сите филтри" + +msgid "Remove from sorting" +msgstr "Отстрани од сортирање" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Приоритет на сортирање: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Вклучи/исклучи сортирање" + +msgid "Delete" +msgstr "Избриши" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Бришење на %(object_name)s '%(escaped_object)s' ќе резултира со бришење на " +"поврзаните објекти, но со вашата сметка немате доволно привилегии да ги " +"бришете следните типови на објекти:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Бришење на %(object_name)s '%(escaped_object)s' ќе резултира со бришење на " +"следниве заштитени објекти:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Сигурне сте дека сакате да ги бришете %(object_name)s „%(escaped_object)s“? " +"Сите овие ставки ќе бидат избришани:" + +msgid "Objects" +msgstr "Предмети" + +msgid "Yes, I’m sure" +msgstr "Да, сигурен сум" + +msgid "No, take me back" +msgstr "Не, врати ме назад" + +msgid "Delete multiple objects" +msgstr "Избриши повеќе ставки" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Бришење на избраните %(objects_name)s ќе резултира со бришење на поврзани " +"објекти, но немате одобрување да ги избришете следниве типови објекти:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Бришење на избраните %(objects_name)s бара бришење на следните поврзани " +"објекти кои се заштитени:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Дали сте сигурни дека сакате да го избришете избраниот %(objects_name)s? " +"Сите овие објекти и оние поврзани со нив ќе бидат избришани:" + +msgid "Delete?" +msgstr "Избриши?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " Според %(filter_title)s " + +msgid "Summary" +msgstr "Резиме" + +msgid "Recent actions" +msgstr "Последни акции" + +msgid "My actions" +msgstr "Мои акции" + +msgid "None available" +msgstr "Ништо не е достапно" + +msgid "Unknown content" +msgstr "Непозната содржина" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Нешто не е во ред со инсталацијата на базата на податоци. Уверете се дека " +"соодветните табели се создадени, и дека базата на податоци е пристапна до " +"соодветниот корисник." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Најавени сте како %(username)s, но не сте авторизирани да пристапите до " +"оваа страна. Сакате ли да се најавите како друг корисник?" + +msgid "Forgotten your password or username?" +msgstr "Ја заборавивте вашата лозинка или корисничко име?" + +msgid "Toggle navigation" +msgstr "" + +msgid "Start typing to filter…" +msgstr "Започнете со пишување за да филтрирате..." + +msgid "Filter navigation items" +msgstr "" + +msgid "Date/time" +msgstr "Датум/час" + +msgid "User" +msgstr "Корисник" + +msgid "Action" +msgstr "Акција" + +msgid "entry" +msgstr "" + +msgid "entries" +msgstr "" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" + +msgid "Show all" +msgstr "Прикажи ги сите" + +msgid "Save" +msgstr "Сними" + +msgid "Popup closing…" +msgstr "Попапот се затвара..." + +msgid "Search" +msgstr "Барај" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s резултат" +msgstr[1] "%(counter)s резултати" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "вкупно %(full_result_count)s" + +msgid "Save as new" +msgstr "Сними како нова" + +msgid "Save and add another" +msgstr "Сними и додади уште" + +msgid "Save and continue editing" +msgstr "Сними и продолжи со уредување" + +msgid "Save and view" +msgstr "Сними и прегледај" + +msgid "Close" +msgstr "Затвори" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Промени ги избраните %(model)s" + +#, python-format +msgid "Add another %(model)s" +msgstr "Додади уште %(model)s" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Избриши ги избраните %(model)s" + +#, python-format +msgid "View selected %(model)s" +msgstr "" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "" + +msgid "Log in again" +msgstr "Најавете се повторно" + +msgid "Password change" +msgstr "Измена на лозинка" + +msgid "Your password was changed." +msgstr "Вашата лозинка беше сменета." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" + +msgid "Change my password" +msgstr "Промени ја мојата лозинка" + +msgid "Password reset" +msgstr "Ресетирање на лозинка" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Вашата лозинка беше поставена. Сега можете да се најавите." + +msgid "Password reset confirmation" +msgstr "Одобрување за ресетирање на лозинка" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Ве молам внесете ја вашата нова лозинка двапати за да може да бидете сигурни " +"дека правилно сте ја внеле." + +msgid "New password:" +msgstr "Нова лозинка:" + +msgid "Confirm password:" +msgstr "Потврди лозинка:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Врската за ресетирање на лозинката беше невалидна, најверојатно бидејќи веќе " +"била искористена. Ве молам повторно побарајте ресетирање на вашата лозинката." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Го примате овој email бидејќи побаравте ресетирање на лозинка како корисник " +"на %(site_name)s." + +msgid "Please go to the following page and choose a new password:" +msgstr "Ве молам одете на следната страница и внесете нова лозинка:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "Вашето корисничко име, во случај да сте заборавиле:" + +msgid "Thanks for using our site!" +msgstr "Ви благодариме што го користите овој сајт!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "Тимот на %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Ја заборавивте вашата лозинка? Внесете го вашиот имејл и ќе ви пратиме " +"инструкции да подесите нова лозинка. " + +msgid "Email address:" +msgstr "Email адреса:" + +msgid "Reset my password" +msgstr "Ресетирај ја мојата лозинка" + +msgid "All dates" +msgstr "Сите датуми" + +#, python-format +msgid "Select %s" +msgstr "Изберете %s" + +#, python-format +msgid "Select %s to change" +msgstr "Изберете %s за измена" + +#, python-format +msgid "Select %s to view" +msgstr "Изберете %s за прегледување" + +msgid "Date:" +msgstr "Датум:" + +msgid "Time:" +msgstr "Време:" + +msgid "Lookup" +msgstr "Побарај" + +msgid "Currently:" +msgstr "Моментално:" + +msgid "Change:" +msgstr "Измени:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/nb/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/nb/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..9f312043 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/nb/LC_MESSAGES/django 3.po @@ -0,0 +1,720 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Jannis Leidel , 2011 +# jensadne , 2013-2014 +# Jon , 2015-2016 +# Jon , 2017-2020 +# Jon , 2013 +# Jon , 2011,2013 +# Sigurd Gartmann , 2012 +# Tommy Strand , 2013 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-07-14 19:53+0200\n" +"PO-Revision-Date: 2020-09-04 13:37+0000\n" +"Last-Translator: Jon \n" +"Language-Team: Norwegian Bokmål (http://www.transifex.com/django/django/" +"language/nb/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: nb\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Slettet %(count)d %(items)s." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Kan ikke slette %(name)s" + +msgid "Are you sure?" +msgstr "Er du sikker?" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Slett valgte %(verbose_name_plural)s" + +msgid "Administration" +msgstr "Administrasjon" + +msgid "All" +msgstr "Alle" + +msgid "Yes" +msgstr "Ja" + +msgid "No" +msgstr "Nei" + +msgid "Unknown" +msgstr "Ukjent" + +msgid "Any date" +msgstr "Når som helst" + +msgid "Today" +msgstr "I dag" + +msgid "Past 7 days" +msgstr "Siste syv dager" + +msgid "This month" +msgstr "Denne måneden" + +msgid "This year" +msgstr "I år" + +msgid "No date" +msgstr "Ingen dato" + +msgid "Has date" +msgstr "Har dato" + +msgid "Empty" +msgstr "Tom" + +msgid "Not empty" +msgstr "Ikke tom" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Vennligst oppgi gyldig %(username)s og passord til en " +"administrasjonsbrukerkonto. Merk at det er forskjell på små og store " +"bokstaver." + +msgid "Action:" +msgstr "Handling:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Legg til ny %(verbose_name)s" + +msgid "Remove" +msgstr "Fjern" + +msgid "Addition" +msgstr "Tillegg" + +msgid "Change" +msgstr "Endre" + +msgid "Deletion" +msgstr "Sletting" + +msgid "action time" +msgstr "tid for handling" + +msgid "user" +msgstr "bruker" + +msgid "content type" +msgstr "innholdstype" + +msgid "object id" +msgstr "objekt-ID" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "objekt-repr" + +msgid "action flag" +msgstr "handlingsflagg" + +msgid "change message" +msgstr "endre melding" + +msgid "log entry" +msgstr "logginnlegg" + +msgid "log entries" +msgstr "logginnlegg" + +#, python-format +msgid "Added “%(object)s”." +msgstr "La til \"%(object)s\"." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "Endret \"%(object)s\" — %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "Slettet \"%(object)s\"." + +msgid "LogEntry Object" +msgstr "LogEntry-objekt" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "La til {name} \"{object}\"." + +msgid "Added." +msgstr "Lagt til." + +msgid "and" +msgstr "og" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "Endret {fields} for {name} \"{object}\"." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "Endret {fields}." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "Slettet {name} \"{object}\"." + +msgid "No fields changed." +msgstr "Ingen felt endret." + +msgid "None" +msgstr "Ingen" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" +"Hold nede «Control», eller «Command» på en Mac, for å velge mer enn én." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "{name} \"{obj}\" ble lagt til." + +msgid "You may edit it again below." +msgstr "Du kan endre det igjen nedenfor." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "{name} \"{obj}\" ble lagt til. Du kan legge til en ny {name} nedenfor." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "{name} \"{obj}\" ble endret. Du kan redigere videre nedenfor." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "{name} \"{obj}\" ble lagt til. Du kan redigere videre nedenfor." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "{name} \"{obj}\" ble endret. Du kan legge til en ny {name} nedenfor." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "{name} \"{obj}\" ble endret." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Du må velge objekter for å utføre handlinger på dem. Ingen objekter har " +"blitt endret." + +msgid "No action selected." +msgstr "Ingen handling valgt." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "%(name)s \"%(obj)s\" ble slettet." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "%(name)s med ID \"%(key)s\" eksisterer ikke. Kanskje det ble slettet?" + +#, python-format +msgid "Add %s" +msgstr "Legg til ny %s" + +#, python-format +msgid "Change %s" +msgstr "Endre %s" + +#, python-format +msgid "View %s" +msgstr "Se %s" + +msgid "Database error" +msgstr "Databasefeil" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s ble endret." +msgstr[1] "%(count)s %(name)s ble endret." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s valgt" +msgstr[1] "Alle %(total_count)s valgt" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 av %(cnt)s valgt" + +#, python-format +msgid "Change history: %s" +msgstr "Endringshistorikk: %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Sletting av %(class_name)s «%(instance)s» krever sletting av følgende " +"beskyttede relaterte objekter: %(related_objects)s" + +msgid "Django site admin" +msgstr "Django administrasjonsside" + +msgid "Django administration" +msgstr "Django-administrasjon" + +msgid "Site administration" +msgstr "Nettstedsadministrasjon" + +msgid "Log in" +msgstr "Logg inn" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s-administrasjon" + +msgid "Page not found" +msgstr "Fant ikke siden" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Beklager, men siden du spør etter finnes ikke." + +msgid "Home" +msgstr "Hjem" + +msgid "Server error" +msgstr "Tjenerfeil" + +msgid "Server error (500)" +msgstr "Tjenerfeil (500)" + +msgid "Server Error (500)" +msgstr "Tjenerfeil (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Det har oppstått en feil. Feilen er blitt rapportert til administrator via e-" +"post, og vil bli fikset snart. Takk for din tålmodighet." + +msgid "Run the selected action" +msgstr "Utfør den valgte handlingen" + +msgid "Go" +msgstr "Gå" + +msgid "Click here to select the objects across all pages" +msgstr "Trykk her for å velge samtlige objekter fra alle sider" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Velg alle %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "Nullstill valg" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Modeller i %(name)s-applikasjonen" + +msgid "Add" +msgstr "Legg til" + +msgid "View" +msgstr "Se" + +msgid "You don’t have permission to view or edit anything." +msgstr "Du har ikke tillatelse til å vise eller endre noe." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Skriv først inn brukernavn og passord. Deretter vil du få mulighet til å " +"endre flere brukerinnstillinger." + +msgid "Enter a username and password." +msgstr "Skriv inn brukernavn og passord." + +msgid "Change password" +msgstr "Endre passord" + +msgid "Please correct the error below." +msgstr "Vennligst korriger feilen under." + +msgid "Please correct the errors below." +msgstr "Vennligst korriger feilene under." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "Skriv inn et nytt passord for brukeren %(username)s." + +msgid "Welcome," +msgstr "Velkommen," + +msgid "View site" +msgstr "Vis nettsted" + +msgid "Documentation" +msgstr "Dokumentasjon" + +msgid "Log out" +msgstr "Logg ut" + +#, python-format +msgid "Add %(name)s" +msgstr "Legg til ny %(name)s" + +msgid "History" +msgstr "Historikk" + +msgid "View on site" +msgstr "Vis på nettsted" + +msgid "Filter" +msgstr "Filtrering" + +msgid "Clear all filters" +msgstr "Fjern alle filtre" + +msgid "Remove from sorting" +msgstr "Fjern fra sortering" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Sorteringsprioritet: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Slå av og på sortering" + +msgid "Delete" +msgstr "Slett" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Om du sletter %(object_name)s «%(escaped_object)s», vil også relaterte " +"objekter slettes, men du har ikke tillatelse til å slette følgende " +"objekttyper:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Sletting av %(object_name)s «%(escaped_object)s» krever sletting av følgende " +"beskyttede relaterte objekter:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Er du sikker på at du vil slette %(object_name)s «%(escaped_object)s»? Alle " +"de følgende relaterte objektene vil bli slettet:" + +msgid "Objects" +msgstr "Objekter" + +msgid "Yes, I’m sure" +msgstr "Ja, jeg er sikker" + +msgid "No, take me back" +msgstr "Nei, ta meg tilbake" + +msgid "Delete multiple objects" +msgstr "Slett flere objekter" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Sletting av det valgte %(objects_name)s ville resultere i sletting av " +"relaterte objekter, men kontoen din har ikke tillatelse til å slette " +"følgende objekttyper:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Sletting av det valgte %(objects_name)s ville kreve sletting av følgende " +"beskyttede relaterte objekter:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Er du sikker på vil slette det valgte %(objects_name)s? De følgende " +"objektene og deres relaterte objekter vil bli slettet:" + +msgid "Delete?" +msgstr "Slette?" + +#, python-format +msgid " By %(filter_title)s " +msgstr "Etter %(filter_title)s " + +msgid "Summary" +msgstr "Oppsummering" + +msgid "Recent actions" +msgstr "Siste handlinger" + +msgid "My actions" +msgstr "Mine handlinger" + +msgid "None available" +msgstr "Ingen tilgjengelige" + +msgid "Unknown content" +msgstr "Ukjent innhold" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Noe er galt med databaseinstallasjonen din. Sørg for at databasetabellene er " +"opprettet og at brukeren har de nødvendige rettighetene." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Du er logget inn som %(username)s, men er ikke autorisert til å få tilgang " +"til denne siden. Ønsker du å logge inn med en annen konto?" + +msgid "Forgotten your password or username?" +msgstr "Glemt brukernavnet eller passordet ditt?" + +msgid "Toggle navigation" +msgstr "Veksle navigasjon" + +msgid "Date/time" +msgstr "Dato/tid" + +msgid "User" +msgstr "Bruker" + +msgid "Action" +msgstr "Handling" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"Dette objektet har ingen endringshistorikk. Det ble sannsynligvis ikke lagt " +"til på denne administrasjonssiden." + +msgid "Show all" +msgstr "Vis alle" + +msgid "Save" +msgstr "Lagre" + +msgid "Popup closing…" +msgstr "Lukker popup..." + +msgid "Search" +msgstr "Søk" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s resultat" +msgstr[1] "%(counter)s resultater" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s totalt" + +msgid "Save as new" +msgstr "Lagre som ny" + +msgid "Save and add another" +msgstr "Lagre og legg til ny" + +msgid "Save and continue editing" +msgstr "Lagre og fortsett å redigere" + +msgid "Save and view" +msgstr "Lagre og se" + +msgid "Close" +msgstr "Lukk" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Endre valgt %(model)s" + +#, python-format +msgid "Add another %(model)s" +msgstr "Legg til ny %(model)s" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Slett valgte %(model)s" + +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Takk for i dag." + +msgid "Log in again" +msgstr "Logg inn igjen" + +msgid "Password change" +msgstr "Endre passord" + +msgid "Your password was changed." +msgstr "Ditt passord ble endret." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Av sikkerhetsgrunner må du oppgi ditt gamle passord. Deretter oppgir du det " +"nye passordet ditt to ganger, slik at vi kan kontrollere at det er korrekt." + +msgid "Change my password" +msgstr "Endre passord" + +msgid "Password reset" +msgstr "Nullstill passord" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Passordet ditt er satt. Du kan nå logge inn." + +msgid "Password reset confirmation" +msgstr "Bekreftelse på nullstilt passord" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Oppgi det nye passordet to ganger, for å sikre at det er skrevet korrekt." + +msgid "New password:" +msgstr "Nytt passord:" + +msgid "Confirm password:" +msgstr "Gjenta nytt passord:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Nullstillingslenken er ugyldig, kanskje fordi den allerede har vært brukt. " +"Vennligst nullstill passordet ditt på nytt." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Vi har sendt deg en e-post med instruksjoner for nullstilling av passord, " +"hvis en konto finnes på den e-postadressen du oppga. Du bør motta den om " +"kort tid." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Hvis du ikke mottar en e-post, sjekk igjen at du har oppgitt den adressen du " +"er registrert med og sjekk spam-mappen din." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Du mottar denne e-posten fordi du har bedt om nullstilling av passordet ditt " +"på %(site_name)s." + +msgid "Please go to the following page and choose a new password:" +msgstr "Vennligst gå til følgende side og velg et nytt passord:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "Brukernavnet ditt, i tilfelle du har glemt det:" + +msgid "Thanks for using our site!" +msgstr "Takk for at du bruker siden vår!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "Hilsen %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Glemt passordet ditt? Oppgi e-postadressen din under, så sender vi deg en e-" +"post med instruksjoner for nullstilling av passord." + +msgid "Email address:" +msgstr "E-postadresse:" + +msgid "Reset my password" +msgstr "Nullstill mitt passord" + +msgid "All dates" +msgstr "Alle datoer" + +#, python-format +msgid "Select %s" +msgstr "Velg %s" + +#, python-format +msgid "Select %s to change" +msgstr "Velg %s du ønsker å endre" + +#, python-format +msgid "Select %s to view" +msgstr "Velg %s å se" + +msgid "Date:" +msgstr "Dato:" + +msgid "Time:" +msgstr "Tid:" + +msgid "Lookup" +msgstr "Oppslag" + +msgid "Currently:" +msgstr "Nåværende:" + +msgid "Change:" +msgstr "Endre:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ne/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ne/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..7a29d0ee --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ne/LC_MESSAGES/django 3.po @@ -0,0 +1,688 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Sagar Chalise , 2011 +# Santosh Purbey , 2020 +# Shrawan Poudel , 2021 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-09-21 10:22+0200\n" +"PO-Revision-Date: 2021-11-22 06:50+0000\n" +"Last-Translator: Shrawan Poudel \n" +"Language-Team: Nepali (http://www.transifex.com/django/django/language/ne/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ne\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "%(verbose_name_plural)s छानिएको मेट्नुहोस" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "सफलतापूर्वक मेटियो %(count)d %(items)s ।" + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "%(name)s मेट्न सकिएन " + +msgid "Are you sure?" +msgstr "के तपाई पक्का हुनुहुन्छ ?" + +msgid "Administration" +msgstr "प्रशासन " + +msgid "All" +msgstr "सबै" + +msgid "Yes" +msgstr "हो" + +msgid "No" +msgstr "होइन" + +msgid "Unknown" +msgstr "अज्ञात" + +msgid "Any date" +msgstr "कुनै मिति" + +msgid "Today" +msgstr "आज" + +msgid "Past 7 days" +msgstr "पूर्व ७ दिन" + +msgid "This month" +msgstr "यो महिना" + +msgid "This year" +msgstr "यो साल" + +msgid "No date" +msgstr "मिति छैन" + +msgid "Has date" +msgstr "मिति छ" + +msgid "Empty" +msgstr "खाली" + +msgid "Not empty" +msgstr "खाली छैन" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"कृपया स्टाफ खाताको लागि सही %(username)s र पासवर्ड राख्नु होस । दुवै खाली ठाउँ केस " +"सेन्सिटिव हुन सक्छन् ।" + +msgid "Action:" +msgstr "कार्य:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "अर्को %(verbose_name)s थप्नुहोस ।" + +msgid "Remove" +msgstr "हटाउनुहोस" + +msgid "Addition" +msgstr "थप" + +msgid "Change" +msgstr "फेर्नुहोस" + +msgid "Deletion" +msgstr "हटाइयो" + +msgid "action time" +msgstr "कार्य समय" + +msgid "user" +msgstr "प्रयोग कर्ता" + +msgid "content type" +msgstr "" + +msgid "object id" +msgstr "वस्तु परिचय" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "" + +msgid "action flag" +msgstr "एक्सन फ्ल्याग" + +msgid "change message" +msgstr "सन्देश परिवर्तन गर्नुहोस" + +msgid "log entry" +msgstr "लग" + +msgid "log entries" +msgstr "लगहरु" + +#, python-format +msgid "Added “%(object)s”." +msgstr "थपियो “%(object)s”." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "बदलियो “%(object)s” — %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "हटाईयो “%(object)s.”" + +msgid "LogEntry Object" +msgstr "लग ईन्ट्री वस्तु" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "थपियो  {name} “{object}”." + +msgid "Added." +msgstr "थपिएको छ ।" + +msgid "and" +msgstr "र" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "" + +#, python-brace-format +msgid "Changed {fields}." +msgstr "" + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "" + +msgid "No fields changed." +msgstr "कुनै फाँट फेरिएन ।" + +msgid "None" +msgstr "शुन्य" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "" + +msgid "You may edit it again below." +msgstr "तपाईं तल फेरि सम्पादन गर्न सक्नुहुन्छ।" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "कार्य गर्नका निम्ति वस्तु छान्नु पर्दछ । कुनैपनि छस्तु छानिएको छैन । " + +msgid "No action selected." +msgstr "कार्य छानिएको छैन ।" + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "" + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "" + +#, python-format +msgid "Add %s" +msgstr "%s थप्नुहोस" + +#, python-format +msgid "Change %s" +msgstr "%s परिवर्तित ।" + +#, python-format +msgid "View %s" +msgstr "" + +msgid "Database error" +msgstr "डाटाबेस त्रुटि" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s सफलतापूर्वक परिवर्तन भयो ।" +msgstr[1] "%(count)s %(name)sहरु सफलतापूर्वक परिवर्तन भयो ।" + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s चयन भयो" +msgstr[1] "सबै %(total_count)s चयन भयो" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "%(cnt)s को ० चयन गरियो" + +#, python-format +msgid "Change history: %s" +msgstr "इतिहास फेर्नुहोस : %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" + +msgid "Django site admin" +msgstr "ज्याङ्गो साइट प्रशासन" + +msgid "Django administration" +msgstr "ज्याङ्गो प्रशासन" + +msgid "Site administration" +msgstr "साइट प्रशासन" + +msgid "Log in" +msgstr "लगिन" + +#, python-format +msgid "%(app)s administration" +msgstr "" + +msgid "Page not found" +msgstr "पृष्ठ भेटिएन" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "हामी क्षमाप्रार्थी छौं, तर अनुरोध गरिएको पृष्ठ फेला पार्न सकिएन।" + +msgid "Home" +msgstr "गृह" + +msgid "Server error" +msgstr "सर्भर त्रुटि" + +msgid "Server error (500)" +msgstr "सर्भर त्रुटि (५००)" + +msgid "Server Error (500)" +msgstr "सर्भर त्रुटि (५००)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"त्यहाँ त्रुटि रहेको छ। यो ईमेल मार्फत साइट प्रशासकहरूलाई सूचित गरिएको छ र तुरुन्तै ठिक " +"गर्नुपर्नेछ। तपाईको धैर्यताको लागि धन्यबाद।" + +msgid "Run the selected action" +msgstr "छानिएको कार्य गर्नुहोस ।" + +msgid "Go" +msgstr "बढ्नुहोस" + +msgid "Click here to select the objects across all pages" +msgstr "सबै पृष्ठभरमा वस्तु छान्न यहाँ थिच्नुहोस ।" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "%(total_count)s %(module_name)s सबै छान्नुहोस " + +msgid "Clear selection" +msgstr "चुनेको कुरा हटाउनुहोस ।" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "%(name)s एप्लिकेसनमा भएको मोडेलहरु" + +msgid "Add" +msgstr "थप्नुहोस " + +msgid "View" +msgstr "" + +msgid "You don’t have permission to view or edit anything." +msgstr "तपाईंसँग केहि पनि हेर्न वा सम्पादन गर्न अनुमति छैन।" + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"पहिले, प्रयोगकर्ता नाम र पासवर्ड प्रविष्ट गर्नुहोस्। त्यसो भए, तपाई बढि उपयोगकर्ता " +"विकल्पहरू सम्पादन गर्न सक्षम हुनुहुनेछ।" + +msgid "Enter a username and password." +msgstr "प्रयोगकर्ता नाम र पासवर्ड राख्नुहोस।" + +msgid "Change password" +msgstr "पासवर्ड फेर्नुहोस " + +msgid "Please correct the error below." +msgstr "कृपया तल त्रुटि सुधार गर्नुहोस्।" + +msgid "Please correct the errors below." +msgstr "कृपया तलका त्रुटी सुधार्नु होस ।" + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "प्रयोगकर्ता %(username)s को लागि नयाँ पासवर्ड राख्नुहोस ।" + +msgid "Welcome," +msgstr "स्वागतम्" + +msgid "View site" +msgstr "साइट हेर्नु होस ।" + +msgid "Documentation" +msgstr "विस्तृत विवरण" + +msgid "Log out" +msgstr "लग आउट" + +#, python-format +msgid "Add %(name)s" +msgstr "%(name)s थप्नुहोस" + +msgid "History" +msgstr "इतिहास" + +msgid "View on site" +msgstr "साइटमा हेर्नुहोस" + +msgid "Filter" +msgstr "छान्नुहोस" + +msgid "Clear all filters" +msgstr "" + +msgid "Remove from sorting" +msgstr "" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "" + +msgid "Toggle sorting" +msgstr "" + +msgid "Delete" +msgstr "मेट्नुहोस" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" + +msgid "Objects" +msgstr "" + +msgid "Yes, I’m sure" +msgstr "" + +msgid "No, take me back" +msgstr "" + +msgid "Delete multiple objects" +msgstr "वहु वस्तुहरु मेट्नुहोस ।" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "%(objects_name)s " + +msgid "Delete?" +msgstr "मेट्नुहुन्छ ?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " %(filter_title)s द्वारा" + +msgid "Summary" +msgstr "" + +msgid "Recent actions" +msgstr "भर्खरका कार्यहरू" + +msgid "My actions" +msgstr "मेरो कार्यहरू" + +msgid "None available" +msgstr "कुनै पनि उपलब्ध छैन ।" + +msgid "Unknown content" +msgstr "अज्ञात सामग्री" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"तपाईंको डाटाबेस स्थापनामा केहि गलत छ। निश्चित गर्नुहोस् कि उपयुक्त डाटाबेस टेबलहरू सिर्जना " +"गरिएको छ, र यो सुनिश्चित गर्नुहोस् कि उपयुक्त डाटाबेस उपयुक्त प्रयोगकर्ताद्वारा पढ्न योग्य " +"छ।" + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"तपाईं यस %(username)s रूपमा प्रमाणिकरण हुनुहुन्छ, तर यस पृष्ठ पहुँच गर्न अधिकृत हुनुहुन्न। के " +"तपाइँ बिभिन्न खातामा लगईन गर्न चाहानुहुन्छ?" + +msgid "Forgotten your password or username?" +msgstr "पासवर्ड अथवा प्रयोगकर्ता नाम भुल्नुभयो ।" + +msgid "Toggle navigation" +msgstr "" + +msgid "Start typing to filter…" +msgstr "" + +msgid "Filter navigation items" +msgstr "" + +msgid "Date/time" +msgstr "मिति/समय" + +msgid "User" +msgstr "प्रयोगकर्ता" + +msgid "Action" +msgstr "कार्य:" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" + +msgid "Show all" +msgstr "सबै देखाउनुहोस" + +msgid "Save" +msgstr "बचत गर्नुहोस" + +msgid "Popup closing…" +msgstr "" + +msgid "Search" +msgstr "खोज्नुहोस" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s नतिजा" +msgstr[1] "%(counter)s नतिजाहरु" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "जम्मा %(full_result_count)s" + +msgid "Save as new" +msgstr "नयाँ रुपमा बचत गर्नुहोस" + +msgid "Save and add another" +msgstr "बचत गरेर अर्को थप्नुहोस" + +msgid "Save and continue editing" +msgstr "बचत गरेर संशोधन जारी राख्नुहोस" + +msgid "Save and view" +msgstr "" + +msgid "Close" +msgstr "" + +#, python-format +msgid "Change selected %(model)s" +msgstr "" + +#, python-format +msgid "Add another %(model)s" +msgstr "" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "" + +msgid "Log in again" +msgstr "पुन: लगिन गर्नुहोस" + +msgid "Password change" +msgstr "पासवर्ड फेरबदल" + +msgid "Your password was changed." +msgstr "तपाइको पासवर्ड फेरिएको छ ।" + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" + +msgid "Change my password" +msgstr "मेरो पासवर्ड फेर्नुहोस " + +msgid "Password reset" +msgstr "पासवर्डपून: राख्नुहोस । " + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "तपाइको पासवर्ड राखियो । कृपया लगिन गर्नुहोस ।" + +msgid "Password reset confirmation" +msgstr "पासवर्ड पुनर्स्थापना पुष्टि" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "ठीक तरिकाले राखिएको पुष्टि गर्न कृपया नयाँ पासवर्ड दोहोर्याएर राख्नुहोस ।" + +msgid "New password:" +msgstr "नयाँ पासवर्ड :" + +msgid "Confirm password:" +msgstr "पासवर्ड पुष्टि:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "पासवर्ड पुनर्स्थापना प्रयोग भइसकेको छ । कृपया नयाँ पासवर्ड रिसेट माग्नुहोस ।" + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +" %(site_name)s को लागि तपाइले पासवर्ड पुन: राख्न आग्रह गरेको हुनाले ई-मेल पाउनुहुदैंछ । " + +msgid "Please go to the following page and choose a new password:" +msgstr "कृपया उक्त पृष्ठमा जानुहोस र नयाँ पासवर्ड राख्नुहोस :" + +msgid "Your username, in case you’ve forgotten:" +msgstr "तपाईंको प्रयोगकर्ता नाम, यदि तपाईंले बिर्सनुभयो भने:" + +msgid "Thanks for using our site!" +msgstr "हाम्रो साइट प्रयोग गरेकोमा धन्यवाद" + +#, python-format +msgid "The %(site_name)s team" +msgstr "%(site_name)s टोली" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"तपाईँको पासवर्ड बिर्सनुभयो? तल तपाईंको ईमेल ठेगाना राख्नुहोस् र हामी नयाँ सेट गर्न ईमेल " +"निर्देशनहरू दिनेछौं।" + +msgid "Email address:" +msgstr "ई-मेल ठेगाना :" + +msgid "Reset my password" +msgstr "मेरो पासवर्ड पुन: राख्नुहोस ।" + +msgid "All dates" +msgstr "सबै मिति" + +#, python-format +msgid "Select %s" +msgstr "%s छान्नुहोस" + +#, python-format +msgid "Select %s to change" +msgstr "%s परिवर्तन गर्न छान्नुहोस ।" + +#, python-format +msgid "Select %s to view" +msgstr "" + +msgid "Date:" +msgstr "मिति:" + +msgid "Time:" +msgstr "समय:" + +msgid "Lookup" +msgstr "खोज तलास" + +msgid "Currently:" +msgstr "अहिले :" + +msgid "Change:" +msgstr "फेर्नु होस :" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/nl/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/nl/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..2a0b6e14 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/nl/LC_MESSAGES/django 3.po @@ -0,0 +1,752 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Bas Peschier , 2013 +# Claude Paroz , 2017 +# Evelijn Saaltink , 2016 +# Harro van der Klauw , 2012 +# Ilja Maas , 2015 +# Jannis Leidel , 2011 +# 6a27f10aef159701c7a5ff07f0fb0a78_05545ed , 2011-2012 +# dokterbob , 2015 +# Meteor0id, 2019-2020 +# 8de006b1b0894aab6aef71979dcd8bd6_5c6b207 , 2014-2015 +# Tino de Bruijn , 2011 +# Tonnes , 2017,2019-2020,2022 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:10-0500\n" +"PO-Revision-Date: 2022-07-25 07:05+0000\n" +"Last-Translator: Tonnes \n" +"Language-Team: Dutch (http://www.transifex.com/django/django/language/nl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: nl\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Geselecteerde %(verbose_name_plural)s verwijderen" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d %(items)s met succes verwijderd." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "%(name)s kan niet worden verwijderd " + +msgid "Are you sure?" +msgstr "Weet u het zeker?" + +msgid "Administration" +msgstr "Beheer" + +msgid "All" +msgstr "Alle" + +msgid "Yes" +msgstr "Ja" + +msgid "No" +msgstr "Nee" + +msgid "Unknown" +msgstr "Onbekend" + +msgid "Any date" +msgstr "Elke datum" + +msgid "Today" +msgstr "Vandaag" + +msgid "Past 7 days" +msgstr "Afgelopen zeven dagen" + +msgid "This month" +msgstr "Deze maand" + +msgid "This year" +msgstr "Dit jaar" + +msgid "No date" +msgstr "Geen datum" + +msgid "Has date" +msgstr "Heeft datum" + +msgid "Empty" +msgstr "Leeg" + +msgid "Not empty" +msgstr "Niet leeg" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Voer de correcte %(username)s en wachtwoord voor een stafaccount in. Let op " +"dat beide velden hoofdlettergevoelig zijn." + +msgid "Action:" +msgstr "Actie:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Nog een %(verbose_name)s toevoegen" + +msgid "Remove" +msgstr "Verwijderen" + +msgid "Addition" +msgstr "Toevoeging" + +msgid "Change" +msgstr "Wijzigen" + +msgid "Deletion" +msgstr "Verwijdering" + +msgid "action time" +msgstr "actietijd" + +msgid "user" +msgstr "gebruiker" + +msgid "content type" +msgstr "inhoudstype" + +msgid "object id" +msgstr "object-id" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "object-repr" + +msgid "action flag" +msgstr "actievlag" + +msgid "change message" +msgstr "wijzigingsbericht" + +msgid "log entry" +msgstr "logboekvermelding" + +msgid "log entries" +msgstr "logboekvermeldingen" + +#, python-format +msgid "Added “%(object)s”." +msgstr "‘%(object)s’ toegevoegd." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "‘%(object)s’ gewijzigd - %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "‘%(object)s’ verwijderd." + +msgid "LogEntry Object" +msgstr "LogEntry-object" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "{name} ‘{object}’ toegevoegd." + +msgid "Added." +msgstr "Toegevoegd." + +msgid "and" +msgstr "en" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "{fields} voor {name} ‘{object}’ gewijzigd." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "{fields} gewijzigd." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "{name} ‘{object}’ verwijderd." + +msgid "No fields changed." +msgstr "Geen velden gewijzigd." + +msgid "None" +msgstr "Geen" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" +"Houd ‘Control’, of ‘Command’ op een Mac, ingedrukt om meerdere items te " +"selecteren." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "De {name} ‘{obj}’ is met succes toegevoegd." + +msgid "You may edit it again below." +msgstr "U kunt deze hieronder weer bewerken." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"De {name} ‘{obj}’ is met succes toegevoegd. U kunt hieronder nog een {name} " +"toevoegen." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" +"De {name} ‘{obj}’ is met succes gewijzigd. U kunt deze hieronder nogmaals " +"bewerken." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" +"De {name} ‘{obj}’ is met succes toegevoegd. U kunt deze hieronder nogmaals " +"bewerken." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" +"De {name} ‘{obj}’ is met succes gewijzigd. U kunt hieronder nog een {name} " +"toevoegen." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "De {name} ‘{obj}’ is met succes gewijzigd." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Er moeten items worden geselecteerd om acties op uit te voeren. Er zijn geen " +"items gewijzigd." + +msgid "No action selected." +msgstr "Geen actie geselecteerd." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "De %(name)s ‘%(obj)s’ is met succes verwijderd." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "%(name)s met ID ‘%(key)s’ bestaat niet. Misschien is deze verwijderd?" + +#, python-format +msgid "Add %s" +msgstr "%s toevoegen" + +#, python-format +msgid "Change %s" +msgstr "%s wijzigen" + +#, python-format +msgid "View %s" +msgstr "%s weergeven" + +msgid "Database error" +msgstr "Databasefout" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s is met succes gewijzigd." +msgstr[1] "%(count)s %(name)s zijn met succes gewijzigd." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s geselecteerd" +msgstr[1] "Alle %(total_count)s geselecteerd" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 van de %(cnt)s geselecteerd" + +#, python-format +msgid "Change history: %s" +msgstr "Wijzigingsgeschiedenis: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Het verwijderen van %(class_name)s %(instance)s vereist het verwijderen van " +"de volgende beschermde gerelateerde objecten: %(related_objects)s" + +msgid "Django site admin" +msgstr "Django-websitebeheer" + +msgid "Django administration" +msgstr "Django-beheer" + +msgid "Site administration" +msgstr "Websitebeheer" + +msgid "Log in" +msgstr "Aanmelden" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s-beheer" + +msgid "Page not found" +msgstr "Pagina niet gevonden" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Het spijt ons, maar de opgevraagde pagina kon niet worden gevonden." + +msgid "Home" +msgstr "Voorpagina" + +msgid "Server error" +msgstr "Serverfout" + +msgid "Server error (500)" +msgstr "Serverfout (500)" + +msgid "Server Error (500)" +msgstr "Serverfout (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Er heeft zich een fout voorgedaan. Dit is via e-mail bij de " +"websitebeheerders gemeld en zou snel verholpen moeten zijn. Bedankt voor uw " +"geduld." + +msgid "Run the selected action" +msgstr "De geselecteerde actie uitvoeren" + +msgid "Go" +msgstr "Uitvoeren" + +msgid "Click here to select the objects across all pages" +msgstr "Klik hier om alle objecten op alle pagina's te selecteren" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Alle %(total_count)s %(module_name)s selecteren" + +msgid "Clear selection" +msgstr "Selectie wissen" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Modellen in de %(name)s-toepassing" + +msgid "Add" +msgstr "Toevoegen" + +msgid "View" +msgstr "Weergeven" + +msgid "You don’t have permission to view or edit anything." +msgstr "U hebt geen rechten om iets te bekijken of te bewerken." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Vul allereerst een gebruikersnaam en wachtwoord in. Daarna kunt u meer " +"gebruikersopties bewerken." + +msgid "Enter a username and password." +msgstr "Voer een gebruikersnaam en wachtwoord in." + +msgid "Change password" +msgstr "Wachtwoord wijzigen" + +msgid "Please correct the error below." +msgstr "Corrigeer de fout hieronder." + +msgid "Please correct the errors below." +msgstr "Corrigeer de fouten hieronder." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "" +"Voer een nieuw wachtwoord in voor de gebruiker %(username)s." + +msgid "Welcome," +msgstr "Welkom," + +msgid "View site" +msgstr "Website bekijken" + +msgid "Documentation" +msgstr "Documentatie" + +msgid "Log out" +msgstr "Afmelden" + +#, python-format +msgid "Add %(name)s" +msgstr "%(name)s toevoegen" + +msgid "History" +msgstr "Geschiedenis" + +msgid "View on site" +msgstr "Weergeven op website" + +msgid "Filter" +msgstr "Filter" + +msgid "Clear all filters" +msgstr "Alle filters wissen" + +msgid "Remove from sorting" +msgstr "Verwijderen uit sortering" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Sorteerprioriteit: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Sortering aan/uit" + +msgid "Delete" +msgstr "Verwijderen" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Het verwijderen van %(object_name)s '%(escaped_object)s' zou ook " +"gerelateerde objecten verwijderen, maar uw account heeft geen rechten om de " +"volgende typen objecten te verwijderen:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Het verwijderen van %(object_name)s '%(escaped_object)s' vereist het " +"verwijderen van de volgende gerelateerde objecten:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Weet u zeker dat u %(object_name)s '%(escaped_object)s' wilt verwijderen? " +"Alle volgende gerelateerde objecten worden verwijderd:" + +msgid "Objects" +msgstr "Objecten" + +msgid "Yes, I’m sure" +msgstr "Ja, ik weet het zeker" + +msgid "No, take me back" +msgstr "Nee, teruggaan" + +msgid "Delete multiple objects" +msgstr "Meerdere objecten verwijderen" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Het verwijderen van de geselecteerde %(objects_name)s zou ook gerelateerde " +"objecten verwijderen, maar uw account heeft geen rechten om de volgende " +"typen objecten te verwijderen:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Het verwijderen van de geselecteerde %(objects_name)s vereist het " +"verwijderen van de volgende beschermde gerelateerde objecten:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Weet u zeker dat u de geselecteerde %(objects_name)s wilt verwijderen? Alle " +"volgende objecten en hun aanverwante items zullen worden verwijderd:" + +msgid "Delete?" +msgstr "Verwijderen?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " Op %(filter_title)s " + +msgid "Summary" +msgstr "Samenvatting" + +msgid "Recent actions" +msgstr "Recente acties" + +msgid "My actions" +msgstr "Mijn acties" + +msgid "None available" +msgstr "Geen beschikbaar" + +msgid "Unknown content" +msgstr "Onbekende inhoud" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Er is iets mis met de installatie van uw database. Zorg ervoor dat de juiste " +"databasetabellen zijn aangemaakt en dat de database voor de juiste gebruiker " +"leesbaar is." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"U bent geverifieerd als %(username)s, maar niet bevoegd om deze pagina te " +"bekijken. Wilt u zich aanmelden bij een andere account?" + +msgid "Forgotten your password or username?" +msgstr "Wachtwoord of gebruikersnaam vergeten?" + +msgid "Toggle navigation" +msgstr "Navigatie aan/uit" + +msgid "Start typing to filter…" +msgstr "Begin met typen om te filteren…" + +msgid "Filter navigation items" +msgstr "Navigatie-items filteren" + +msgid "Date/time" +msgstr "Datum/tijd" + +msgid "User" +msgstr "Gebruiker" + +msgid "Action" +msgstr "Actie" + +msgid "entry" +msgstr "vermelding" + +msgid "entries" +msgstr "vermeldingen" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"Dit object heeft geen wijzigingsgeschiedenis. Het is mogelijk niet via de " +"beheerwebsite toegevoegd." + +msgid "Show all" +msgstr "Alles tonen" + +msgid "Save" +msgstr "Opslaan" + +msgid "Popup closing…" +msgstr "Pop-up sluiten…" + +msgid "Search" +msgstr "Zoeken" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s resultaat" +msgstr[1] "%(counter)s resultaten" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s totaal" + +msgid "Save as new" +msgstr "Opslaan als nieuw item" + +msgid "Save and add another" +msgstr "Opslaan en nieuwe toevoegen" + +msgid "Save and continue editing" +msgstr "Opslaan en opnieuw bewerken" + +msgid "Save and view" +msgstr "Opslaan en weergeven" + +msgid "Close" +msgstr "Sluiten" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Geselecteerde %(model)s wijzigen" + +#, python-format +msgid "Add another %(model)s" +msgstr "Nog een %(model)s toevoegen" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Geselecteerde %(model)s verwijderen" + +#, python-format +msgid "View selected %(model)s" +msgstr "Geselecteerde %(model)s weergeven" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "Bedankt voor het vandaag wat tijd besteden aan de website." + +msgid "Log in again" +msgstr "Opnieuw aanmelden" + +msgid "Password change" +msgstr "Wachtwoordwijziging" + +msgid "Your password was changed." +msgstr "Uw wachtwoord is gewijzigd." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Voer omwille van beveiliging uw oude en twee keer uw nieuwe wachtwoord in, " +"zodat we kunnen controleren of u geen typefouten hebt gemaakt." + +msgid "Change my password" +msgstr "Mijn wachtwoord wijzigen" + +msgid "Password reset" +msgstr "Wachtwoord hersteld" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Uw wachtwoord is ingesteld. U kunt nu verdergaan en zich aanmelden." + +msgid "Password reset confirmation" +msgstr "Bevestiging wachtwoord herstellen" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Voer het nieuwe wachtwoord twee keer in, zodat we kunnen controleren of er " +"geen typefouten zijn gemaakt." + +msgid "New password:" +msgstr "Nieuw wachtwoord:" + +msgid "Confirm password:" +msgstr "Bevestig wachtwoord:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"De link voor het herstellen van het wachtwoord is ongeldig, waarschijnlijk " +"omdat de link al eens is gebruikt. Vraag opnieuw een wachtwoord aan." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"We hebben u instructies gestuurd voor het instellen van uw wachtwoord, als " +"er een account bestaat met het door u ingevoerde e-mailadres. U zou deze " +"straks moeten ontvangen." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Als u geen e-mail ontvangt, controleer dan of u het e-mailadres hebt " +"ingevoerd waarmee u zich hebt geregistreerd, en controleer uw spam-map." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"U ontvangt deze e-mail, omdat u een aanvraag voor opnieuw instellen van het " +"wachtwoord voor uw account op %(site_name)s hebt gedaan." + +msgid "Please go to the following page and choose a new password:" +msgstr "Ga naar de volgende pagina en kies een nieuw wachtwoord:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "Uw gebruikersnaam, mocht u deze vergeten zijn:" + +msgid "Thanks for using our site!" +msgstr "Bedankt voor het gebruik van onze website!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "Het %(site_name)s-team" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Wachtwoord vergeten? Vul hieronder uw e-mailadres in, en we sturen " +"instructies voor het instellen van een nieuw wachtwoord." + +msgid "Email address:" +msgstr "E-mailadres:" + +msgid "Reset my password" +msgstr "Mijn wachtwoord opnieuw instellen" + +msgid "All dates" +msgstr "Alle datums" + +#, python-format +msgid "Select %s" +msgstr "Selecteer %s" + +#, python-format +msgid "Select %s to change" +msgstr "Selecteer %s om te wijzigen" + +#, python-format +msgid "Select %s to view" +msgstr "Selecteer %s om te bekijken" + +msgid "Date:" +msgstr "Datum:" + +msgid "Time:" +msgstr "Tijd:" + +msgid "Lookup" +msgstr "Opzoeken" + +msgid "Currently:" +msgstr "Huidig:" + +msgid "Change:" +msgstr "Wijzigen:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/pl/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/pl/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..af0d64c4 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/pl/LC_MESSAGES/django 3.po @@ -0,0 +1,784 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# angularcircle, 2011-2013 +# angularcircle, 2013-2014 +# Jannis Leidel , 2011 +# Janusz Harkot , 2014-2015 +# Karol , 2012 +# 0d5641585fd67fbdb97037c19ab83e4c_18c98b0 , 2011 +# 0d5641585fd67fbdb97037c19ab83e4c_18c98b0 , 2011 +# Maciej Olko , 2016-2022 +# Maciej Olko , 2023 +# Maciej Olko , 2015 +# Mariusz Felisiak , 2020,2022 +# Ola Sitarska , 2013 +# Ola Sitarska , 2013 +# Roman Barczyński, 2014 +# Tomasz Kajtoch , 2017 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 07:05+0000\n" +"Last-Translator: Maciej Olko , 2023\n" +"Language-Team: Polish (http://www.transifex.com/django/django/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && " +"(n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && " +"n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Usuń wybrane(-nych) %(verbose_name_plural)s" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Pomyślnie usunięto %(count)d %(items)s." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Nie można usunąć %(name)s" + +msgid "Are you sure?" +msgstr "Jesteś pewien?" + +msgid "Administration" +msgstr "Administracja" + +msgid "All" +msgstr "Wszystko" + +msgid "Yes" +msgstr "Tak" + +msgid "No" +msgstr "Nie" + +msgid "Unknown" +msgstr "Nieznany" + +msgid "Any date" +msgstr "Dowolna data" + +msgid "Today" +msgstr "Dzisiaj" + +msgid "Past 7 days" +msgstr "Ostatnie 7 dni" + +msgid "This month" +msgstr "Ten miesiąc" + +msgid "This year" +msgstr "Ten rok" + +msgid "No date" +msgstr "Brak daty" + +msgid "Has date" +msgstr "Posiada datę" + +msgid "Empty" +msgstr "Puste" + +msgid "Not empty" +msgstr "Niepuste" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Wprowadź poprawne dane w polach „%(username)s” i „hasło” dla konta " +"należącego do zespołu. Uwaga: wielkość liter może mieć znaczenie." + +msgid "Action:" +msgstr "Akcja:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Dodaj kolejne(go)(-ną)(-ny) %(verbose_name)s" + +msgid "Remove" +msgstr "Usuń" + +msgid "Addition" +msgstr "Dodanie" + +msgid "Change" +msgstr "Zmień" + +msgid "Deletion" +msgstr "Usunięcie" + +msgid "action time" +msgstr "czas akcji" + +msgid "user" +msgstr "użytkownik" + +msgid "content type" +msgstr "typ zawartości" + +msgid "object id" +msgstr "id obiektu" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "reprezentacja obiektu" + +msgid "action flag" +msgstr "flaga akcji" + +msgid "change message" +msgstr "zmień wiadomość" + +msgid "log entry" +msgstr "log" + +msgid "log entries" +msgstr "logi" + +#, python-format +msgid "Added “%(object)s”." +msgstr "Dodano „%(object)s”." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "Zmieniono „%(object)s” — %(changes)s " + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "Usunięto „%(object)s”." + +msgid "LogEntry Object" +msgstr "Obiekt LogEntry" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "Dodano {name} „{object}”." + +msgid "Added." +msgstr "Dodano." + +msgid "and" +msgstr "i" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "Zmodyfikowano {fields} w {name} „{object}”." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "Zmodyfikowano {fields}." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "Usunięto {name} „{object}”." + +msgid "No fields changed." +msgstr "Żadne pole nie zostało zmienione." + +msgid "None" +msgstr "Brak" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" +"Przytrzymaj wciśnięty klawisz „Ctrl” lub „Command” na Macu, aby zaznaczyć " +"więcej niż jeden wybór." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "{name} „{obj}” został(a)(-ło) dodany(-na)(-ne) pomyślnie." + +msgid "You may edit it again below." +msgstr "Poniżej możesz ponownie edytować." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"{name} „{obj}” został(a)(-ło) dodany(-na)(-ne) pomyślnie. Można dodać " +"kolejne(go)(-ną)(-ny) {name} poniżej." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" +"{name} „{obj}” został(a)(-ło) pomyślnie zmieniony(-na)(-ne). Można edytować " +"go/ją/je ponownie poniżej." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" +"{name} „{obj}” został(a)(-ło) dodany(-na)(-ne) pomyślnie. Można edytować go/" +"ją/je ponownie poniżej." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" +"{name} „{obj}” został(a)(-ło) pomyślnie zmieniony(-na)(-ne). Można dodać " +"kolejny(-nego)(-ną)(-ne) {name} poniżej." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "{name} „{obj}” został(a)(-ło) pomyślnie zmieniony(-na)(-ne)." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Wykonanie akcji wymaga wybrania obiektów. Żaden obiekt nie został zmieniony." + +msgid "No action selected." +msgstr "Nie wybrano akcji." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "%(name)s „%(obj)s” usunięty(-ta)(-te) pomyślnie." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "" +"%(name)s z ID „%(key)s” nie istnieje. Może został(a)(-ło) usunięty(-ta)(-te)?" + +#, python-format +msgid "Add %s" +msgstr "Dodaj %s" + +#, python-format +msgid "Change %s" +msgstr "Zmień %s" + +#, python-format +msgid "View %s" +msgstr "Zobacz %s" + +msgid "Database error" +msgstr "Błąd bazy danych" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s został(a)(-ło) pomyślnie zmieniony(-na)(-ne)." +msgstr[1] "%(count)s %(name)s zostały(-ło) pomyślnie zmienione(-nych)." +msgstr[2] "%(count)s %(name)s zostało pomyślnie zmienionych." +msgstr[3] "%(count)s %(name)s zostało pomyślnie zmienione." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "Wybrano %(total_count)s" +msgstr[1] "Wybrano %(total_count)s" +msgstr[2] "Wybrano %(total_count)s" +msgstr[3] "Wybrano wszystkie %(total_count)s" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "Wybrano 0 z %(cnt)s" + +#, python-format +msgid "Change history: %s" +msgstr "Historia zmian: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Usunięcie %(class_name)s %(instance)s może wiązać się z usunięciem " +"następujących chronionych obiektów pokrewnych: %(related_objects)s" + +msgid "Django site admin" +msgstr "Administracja stroną Django" + +msgid "Django administration" +msgstr "Administracja Django" + +msgid "Site administration" +msgstr "Administracja stroną" + +msgid "Log in" +msgstr "Zaloguj się" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s: administracja" + +msgid "Page not found" +msgstr "Strona nie została znaleziona" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Przykro nam, ale żądana strona nie została znaleziona." + +msgid "Home" +msgstr "Strona główna" + +msgid "Server error" +msgstr "Błąd serwera" + +msgid "Server error (500)" +msgstr "Błąd serwera (500)" + +msgid "Server Error (500)" +msgstr "Błąd Serwera (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Niestety wystąpił błąd. Zostało to zgłoszone administratorom strony poprzez " +"email i niebawem powinno zostać naprawione. Dziękujemy za cierpliwość." + +msgid "Run the selected action" +msgstr "Wykonaj wybraną akcję" + +msgid "Go" +msgstr "Wykonaj" + +msgid "Click here to select the objects across all pages" +msgstr "Kliknij by wybrać obiekty na wszystkich stronach" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Wybierz wszystkie(-kich) %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "Wyczyść wybór" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Modele w aplikacji %(name)s" + +msgid "Add" +msgstr "Dodaj" + +msgid "View" +msgstr "Zobacz" + +msgid "You don’t have permission to view or edit anything." +msgstr "Nie masz uprawnień do oglądania ani edycji niczego." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Najpierw podaj nazwę użytkownika i hasło. Następnie będziesz mógł edytować " +"więcej opcji użytkownika." + +msgid "Enter a username and password." +msgstr "Podaj nazwę użytkownika i hasło." + +msgid "Change password" +msgstr "Zmień hasło" + +msgid "Please correct the error below." +msgid_plural "Please correct the errors below." +msgstr[0] "Prosimy poprawić poniższy błąd." +msgstr[1] "Prosimy poprawić poniższe błędy." +msgstr[2] "Prosimy poprawić poniższe błędy." +msgstr[3] "Prosimy poprawić poniższe błędy." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "Podaj nowe hasło dla użytkownika %(username)s." + +msgid "Skip to main content" +msgstr "Przejdź do głównej treści" + +msgid "Welcome," +msgstr "Witaj," + +msgid "View site" +msgstr "Pokaż stronę" + +msgid "Documentation" +msgstr "Dokumentacja" + +msgid "Log out" +msgstr "Wyloguj się" + +msgid "Breadcrumbs" +msgstr "Breadcrumbs" + +#, python-format +msgid "Add %(name)s" +msgstr "Dodaj %(name)s" + +msgid "History" +msgstr "Historia" + +msgid "View on site" +msgstr "Pokaż na stronie" + +msgid "Filter" +msgstr "Filtruj" + +msgid "Clear all filters" +msgstr "Wyczyść wszystkie filtry" + +msgid "Remove from sorting" +msgstr "Usuń z sortowania" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Priorytet sortowania: %(priority_number)s " + +msgid "Toggle sorting" +msgstr "Przełącz sortowanie" + +msgid "Toggle theme (current theme: auto)" +msgstr "Przełącz motyw (bieżący motyw: auto)" + +msgid "Toggle theme (current theme: light)" +msgstr "Przełącz motyw (bieżący motyw: jasny)" + +msgid "Toggle theme (current theme: dark)" +msgstr "Przełącz motyw (bieżący motyw: ciemny)" + +msgid "Delete" +msgstr "Usuń" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Usunięcie %(object_name)s „%(escaped_object)s” wiązałoby się z usunięciem " +"obiektów z nim/nią powiązanych, ale niestety nie posiadasz uprawnień do " +"usunięcia obiektów następujących typów:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Usunięcie %(object_name)s „%(escaped_object)s” wymagałoby skasowania " +"następujących chronionych obiektów, które są z nim/nią powiązane:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Czy chcesz skasować %(object_name)s „%(escaped_object)s”? Następujące " +"obiekty powiązane zostaną usunięte:" + +msgid "Objects" +msgstr "Obiekty" + +msgid "Yes, I’m sure" +msgstr "Tak, na pewno" + +msgid "No, take me back" +msgstr "Nie, zabierz mnie stąd" + +msgid "Delete multiple objects" +msgstr "Usuwanie wielu obiektów" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Usunięcie wybranych(-nego)(-nej) %(objects_name)s spowoduje skasowanie " +"obiektów, które są z nim(i)/nią powiązane. Niestety nie posiadasz uprawnień " +"do usunięcia następujących typów obiektów:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Usunięcie wybranych(-nego)(-nej) %(objects_name)s wymaga skasowania " +"następujących chronionych obiektów, które są z nim(i)/nią powiązane:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Czy chcesz skasować zaznaczone(go)(-ną)(-ny)(-nych) %(objects_name)s? " +"Następujące obiekty oraz obiekty od nich zależne zostaną skasowane:" + +msgid "Delete?" +msgstr "Usunąć?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " Według pola %(filter_title)s " + +msgid "Summary" +msgstr "Podsumowanie" + +msgid "Recent actions" +msgstr "Ostatnie działania" + +msgid "My actions" +msgstr "Moje działania" + +msgid "None available" +msgstr "Brak dostępnych" + +msgid "Unknown content" +msgstr "Zawartość nieznana" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Instalacja Twojej bazy danych jest niepoprawna. Upewnij się, że odpowiednie " +"tabele zostały utworzone i odpowiedni użytkownik jest uprawniony do ich " +"odczytu." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Jesteś uwierzytelniony jako %(username)s, ale nie jesteś upoważniony do " +"dostępu do tej strony. Czy chciałbyś zalogować się na inne konto?" + +msgid "Forgotten your password or username?" +msgstr "Nie pamiętasz swojego hasła lub nazwy użytkownika?" + +msgid "Toggle navigation" +msgstr "Przełącz nawigację" + +msgid "Sidebar" +msgstr "Pasek boczny" + +msgid "Start typing to filter…" +msgstr "Zacznij pisać, aby odfiltrować…" + +msgid "Filter navigation items" +msgstr "Filtruj elementy nawigacji" + +msgid "Date/time" +msgstr "Data/czas" + +msgid "User" +msgstr "Użytkownik" + +msgid "Action" +msgstr "Akcja" + +msgid "entry" +msgid_plural "entries" +msgstr[0] "wpis" +msgstr[1] "wpisy" +msgstr[2] "wpisów" +msgstr[3] "wpisu" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"Ten obiekt nie ma historii zmian. Najprawdopodobniej nie został on dodany " +"poprzez panel administracyjny." + +msgid "Show all" +msgstr "Pokaż wszystko" + +msgid "Save" +msgstr "Zapisz" + +msgid "Popup closing…" +msgstr "Zamykanie okna..." + +msgid "Search" +msgstr "Szukaj" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s wynik" +msgstr[1] "%(counter)s wyniki" +msgstr[2] "%(counter)s wyników" +msgstr[3] "%(counter)s wyników" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s łącznie" + +msgid "Save as new" +msgstr "Zapisz jako nowy" + +msgid "Save and add another" +msgstr "Zapisz i dodaj nowy" + +msgid "Save and continue editing" +msgstr "Zapisz i kontynuuj edycję" + +msgid "Save and view" +msgstr "Zapisz i obejrzyj" + +msgid "Close" +msgstr "Zamknij" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Zmień wybraną(-ne)(-nego)(-ny) %(model)s" + +#, python-format +msgid "Add another %(model)s" +msgstr "Dodaj kolejne(go)(-ną)(-ny) %(model)s" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Usuń wybraną(-ne)(-nego)(-ny) %(model)s" + +#, python-format +msgid "View selected %(model)s" +msgstr "Obejrzyj wybraną(-ne)(-nego)(-ny) %(model)s" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "Dzięki za spędzenie cennego czasu ze stroną." + +msgid "Log in again" +msgstr "Zaloguj się ponownie" + +msgid "Password change" +msgstr "Zmiana hasła" + +msgid "Your password was changed." +msgstr "Twoje hasło zostało zmienione." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Podaj swoje stare hasło, ze względów bezpieczeństwa, a później wpisz " +"dwukrotnie Twoje nowe hasło, abyśmy mogli zweryfikować, że zostało wpisane " +"poprawnie." + +msgid "Change my password" +msgstr "Zmień hasło" + +msgid "Password reset" +msgstr "Zresetuj hasło" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Twoje hasło zostało ustawione. Możesz się teraz zalogować." + +msgid "Password reset confirmation" +msgstr "Potwierdzenie zresetowania hasła" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Podaj dwukrotnie nowe hasło, by można było zweryfikować, czy zostało wpisane " +"poprawnie." + +msgid "New password:" +msgstr "Nowe hasło:" + +msgid "Confirm password:" +msgstr "Potwierdź hasło:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Link pozwalający na reset hasła jest niepoprawny - być może dlatego, że " +"został już raz użyty. Możesz ponownie zażądać zresetowania hasła." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Instrukcja pozwalająca ustawić nowe hasło dla podanego adresu e-mail została " +"wysłana. Niebawem powinna się pojawić na twoim koncie pocztowym." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"W przypadku nieotrzymania wiadomości e-mail: upewnij się czy adres " +"wprowadzony jest zgodny z tym podanym podczas rejestracji i sprawdź " +"zawartość folderu SPAM na swoim koncie." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Otrzymujesz tę wiadomość, gdyż skorzystano z opcji resetu hasła dla Twojego " +"konta na stronie %(site_name)s." + +msgid "Please go to the following page and choose a new password:" +msgstr "" +"Aby wprowadzić nowe hasło, proszę przejść na stronę, której adres widnieje " +"poniżej:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "Twoja nazwa użytkownika, na wypadek, gdybyś zapomniał(a):" + +msgid "Thanks for using our site!" +msgstr "Dzięki za korzystanie z naszej strony!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "Zespół %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Nie pamiętasz swojego hasła? Wprowadź w poniższym polu swój adres e-mail, a " +"wyślemy ci instrukcję opisującą sposób ustawienia nowego hasła." + +msgid "Email address:" +msgstr "Adres e-mail:" + +msgid "Reset my password" +msgstr "Zresetuj moje hasło" + +msgid "All dates" +msgstr "Wszystkie daty" + +#, python-format +msgid "Select %s" +msgstr "Wybierz %s" + +#, python-format +msgid "Select %s to change" +msgstr "Wybierz %s do zmiany" + +#, python-format +msgid "Select %s to view" +msgstr "Wybierz %s do obejrzenia" + +msgid "Date:" +msgstr "Data:" + +msgid "Time:" +msgstr "Czas:" + +msgid "Lookup" +msgstr "Szukaj" + +msgid "Currently:" +msgstr "Aktualny:" + +msgid "Change:" +msgstr "Zmień:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/pt/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/pt/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..bdb0c615 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/pt/LC_MESSAGES/django 3.po @@ -0,0 +1,725 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Henrique Azevedo , 2018 +# Jannis Leidel , 2011 +# jorgecarleitao , 2015 +# Nuno Mariz , 2013,2015,2017-2018 +# Paulo Köch , 2011 +# Raúl Pedro Fernandes Santos, 2014 +# Rui Dinis Silva, 2017 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-01-16 20:42+0100\n" +"PO-Revision-Date: 2019-01-18 00:36+0000\n" +"Last-Translator: Ramiro Morales\n" +"Language-Team: Portuguese (http://www.transifex.com/django/django/language/" +"pt/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pt\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Foram removidos com sucesso %(count)d %(items)s." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Não é possível remover %(name)s " + +msgid "Are you sure?" +msgstr "Tem a certeza?" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Remover %(verbose_name_plural)s selecionados" + +msgid "Administration" +msgstr "Administração" + +msgid "All" +msgstr "Todos" + +msgid "Yes" +msgstr "Sim" + +msgid "No" +msgstr "Não" + +msgid "Unknown" +msgstr "Desconhecido" + +msgid "Any date" +msgstr "Qualquer data" + +msgid "Today" +msgstr "Hoje" + +msgid "Past 7 days" +msgstr "Últimos 7 dias" + +msgid "This month" +msgstr "Este mês" + +msgid "This year" +msgstr "Este ano" + +msgid "No date" +msgstr "Sem data" + +msgid "Has date" +msgstr "Tem data" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Por favor introduza o %(username)s e password corretos para a conta de " +"equipa. Tenha em atenção às maiúsculas e minúsculas." + +msgid "Action:" +msgstr "Ação:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Adicionar outro %(verbose_name)s" + +msgid "Remove" +msgstr "Remover" + +msgid "Addition" +msgstr "Adição" + +msgid "Change" +msgstr "Modificar" + +msgid "Deletion" +msgstr "Eliminação" + +msgid "action time" +msgstr "hora da ação" + +msgid "user" +msgstr "utilizador" + +msgid "content type" +msgstr "tipo de conteúdo" + +msgid "object id" +msgstr "id do objeto" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "repr do objeto" + +msgid "action flag" +msgstr "flag de ação" + +msgid "change message" +msgstr "modificar mensagem" + +msgid "log entry" +msgstr "entrada de log" + +msgid "log entries" +msgstr "entradas de log" + +#, python-format +msgid "Added \"%(object)s\"." +msgstr "Adicionado \"%(object)s\"." + +#, python-format +msgid "Changed \"%(object)s\" - %(changes)s" +msgstr "Foram modificados \"%(object)s\" - %(changes)s" + +#, python-format +msgid "Deleted \"%(object)s.\"" +msgstr "Foram removidos \"%(object)s.\"" + +msgid "LogEntry Object" +msgstr "Objeto LogEntry" + +#, python-brace-format +msgid "Added {name} \"{object}\"." +msgstr "Foi adicionado {name} \"{object}\"." + +msgid "Added." +msgstr "Adicionado." + +msgid "and" +msgstr "e" + +#, python-brace-format +msgid "Changed {fields} for {name} \"{object}\"." +msgstr "Foram modificados os {fields} para {name} \"{object}\"." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "Foi modificado {fields}." + +#, python-brace-format +msgid "Deleted {name} \"{object}\"." +msgstr "Foi removido {name} \"{object}\"." + +msgid "No fields changed." +msgstr "Nenhum campo foi modificado." + +msgid "None" +msgstr "Nenhum" + +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "" +"Mantenha pressionado o \"Control\", ou \"Command\" no Mac, para selecionar " +"mais do que um." + +#, python-brace-format +msgid "The {name} \"{obj}\" was added successfully." +msgstr "O {name} \"{obj}\" foi adicionado com sucesso." + +msgid "You may edit it again below." +msgstr "Pode editar novamente abaixo." + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may add another {name} " +"below." +msgstr "" +"O {name} \"{obj}\" foi adicionado com sucesso. Pode adicionar um novo {name} " +"abaixo." + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." +msgstr "" +"O {name} \"{obj}\" foi modificado com sucesso. Pode voltar a editar " +"novamente abaixo." + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may edit it again below." +msgstr "" +"O {name} \"{obj}\" foi adicionado com sucesso. Pode voltar a editar " +"novamente abaixo." + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." +msgstr "" +"O {name} \"{obj}\" foi modificado com sucesso. Pode adicionar um novo {name} " +"abaixo." + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." +msgstr "O {name} \"{obj}\" foi modificado com sucesso." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Os itens devem ser selecionados de forma a efectuar ações sobre eles. Nenhum " +"item foi modificado." + +msgid "No action selected." +msgstr "Nenhuma ação selecionada." + +#, python-format +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "O(A) %(name)s \"%(obj)s\" foi removido(a) com sucesso." + +#, python-format +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" +msgstr "%(name)s com ID \"%(key)s\" não existe. Talvez foi removido?" + +#, python-format +msgid "Add %s" +msgstr "Adicionar %s" + +#, python-format +msgid "Change %s" +msgstr "Modificar %s" + +#, python-format +msgid "View %s" +msgstr "View %s " + +msgid "Database error" +msgstr "Erro de base de dados" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s foi modificado com sucesso." +msgstr[1] "%(count)s %(name)s foram modificados com sucesso." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s selecionado" +msgstr[1] "Todos %(total_count)s selecionados" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 de %(cnt)s selecionados" + +#, python-format +msgid "Change history: %s" +msgstr "Histórico de modificações: %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Remover %(class_name)s %(instance)s exigiria a remoção dos seguintes objetos " +"relacionados protegidos: %(related_objects)s" + +msgid "Django site admin" +msgstr "Site de administração do Django" + +msgid "Django administration" +msgstr "Administração do Django" + +msgid "Site administration" +msgstr "Administração do site" + +msgid "Log in" +msgstr "Entrar" + +#, python-format +msgid "%(app)s administration" +msgstr "Administração de %(app)s" + +msgid "Page not found" +msgstr "Página não encontrada" + +msgid "We're sorry, but the requested page could not be found." +msgstr "Pedimos desculpa, mas a página solicitada não foi encontrada." + +msgid "Home" +msgstr "Início" + +msgid "Server error" +msgstr "Erro do servidor" + +msgid "Server error (500)" +msgstr "Erro do servidor (500)" + +msgid "Server Error (500)" +msgstr "Erro do servidor (500)" + +msgid "" +"There's been an error. It's been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Ocorreu um erro. Foi enviada uma notificação para os administradores do " +"site, devendo o mesmo ser corrigido em breve. Obrigado pela atenção." + +msgid "Run the selected action" +msgstr "Executar a acção selecionada" + +msgid "Go" +msgstr "Ir" + +msgid "Click here to select the objects across all pages" +msgstr "Clique aqui para selecionar os objetos em todas as páginas" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Selecionar todos %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "Remover seleção" + +msgid "" +"First, enter a username and password. Then, you'll be able to edit more user " +"options." +msgstr "" +"Primeiro introduza o nome do utilizador e palavra-passe. Depois poderá " +"editar mais opções do utilizador." + +msgid "Enter a username and password." +msgstr "Introduza o utilizador e palavra-passe." + +msgid "Change password" +msgstr "Modificar palavra-passe" + +msgid "Please correct the error below." +msgstr "Por favor corrija o erro abaixo." + +msgid "Please correct the errors below." +msgstr "Por favor corrija os erros abaixo." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "" +"Introduza uma nova palavra-passe para o utilizador %(username)s." + +msgid "Welcome," +msgstr "Bem-vindo," + +msgid "View site" +msgstr "Ver site" + +msgid "Documentation" +msgstr "Documentação" + +msgid "Log out" +msgstr "Sair" + +#, python-format +msgid "Add %(name)s" +msgstr "Adicionar %(name)s" + +msgid "History" +msgstr "História" + +msgid "View on site" +msgstr "Ver no site" + +msgid "Filter" +msgstr "Filtro" + +msgid "Remove from sorting" +msgstr "Remover da ordenação" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Prioridade de ordenação: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Altenar ordenação" + +msgid "Delete" +msgstr "Remover" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"A remoção de %(object_name)s '%(escaped_object)s' resultará na remoção dos " +"objetos relacionados, mas a sua conta não tem permissão de remoção dos " +"seguintes tipos de objetos:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Remover o %(object_name)s ' %(escaped_object)s ' exigiria a remoção dos " +"seguintes objetos protegidos relacionados:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Tem a certeza que deseja remover %(object_name)s \"%(escaped_object)s\"? " +"Todos os items relacionados seguintes irão ser removidos:" + +msgid "Objects" +msgstr "Objectos" + +msgid "Yes, I'm sure" +msgstr "Sim, tenho a certeza" + +msgid "No, take me back" +msgstr "Não, retrocede" + +msgid "Delete multiple objects" +msgstr "Remover múltiplos objetos." + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Remover o %(objects_name)s selecionado poderia resultar na remoção de " +"objetos relacionados, mas a sua conta não tem permissão para remover os " +"seguintes tipos de objetos:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Remover o %(objects_name)s selecionado exigiria remover os seguintes objetos " +"protegidos relacionados:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Tem certeza de que deseja remover %(objects_name)s selecionado? Todos os " +"objetos seguintes e seus itens relacionados serão removidos:" + +msgid "View" +msgstr "View" + +msgid "Delete?" +msgstr "Remover?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " Por %(filter_title)s " + +msgid "Summary" +msgstr "Sumário" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Modelos na aplicação %(name)s" + +msgid "Add" +msgstr "Adicionar" + +msgid "You don't have permission to view or edit anything." +msgstr "Não tem permissão para ver ou editar nada." + +msgid "Recent actions" +msgstr "Ações recentes" + +msgid "My actions" +msgstr "As minhas ações" + +msgid "None available" +msgstr "Nenhum disponível" + +msgid "Unknown content" +msgstr "Conteúdo desconhecido" + +msgid "" +"Something's wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Passa-se algo de errado com a instalação da sua base de dados. Verifique se " +"as tabelas da base de dados foram criadas apropriadamente e verifique se a " +"base de dados pode ser lida pelo utilizador definido." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Está autenticado como %(username)s, mas não está autorizado a aceder a esta " +"página. Deseja autenticar-se com uma conta diferente?" + +msgid "Forgotten your password or username?" +msgstr "Esqueceu-se da sua palavra-passe ou utilizador?" + +msgid "Date/time" +msgstr "Data/hora" + +msgid "User" +msgstr "Utilizador" + +msgid "Action" +msgstr "Ação" + +msgid "" +"This object doesn't have a change history. It probably wasn't added via this " +"admin site." +msgstr "" +"Este objeto não tem histórico de modificações. Provavelmente não foi " +"modificado via site de administração." + +msgid "Show all" +msgstr "Mostrar todos" + +msgid "Save" +msgstr "Gravar" + +msgid "Popup closing…" +msgstr "" + +msgid "Search" +msgstr "Pesquisar" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s resultado" +msgstr[1] "%(counter)s resultados" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s no total" + +msgid "Save as new" +msgstr "Gravar como novo" + +msgid "Save and add another" +msgstr "Gravar e adicionar outro" + +msgid "Save and continue editing" +msgstr "Gravar e continuar a editar" + +msgid "Save and view" +msgstr "Gravar e ver" + +msgid "Close" +msgstr "Fechar" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Alterar %(model)s selecionado." + +#, python-format +msgid "Add another %(model)s" +msgstr "Adicionar outro %(model)s" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Remover %(model)s seleccionado" + +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Obrigado pela sua visita." + +msgid "Log in again" +msgstr "Entrar novamente" + +msgid "Password change" +msgstr "Modificação da palavra-passe" + +msgid "Your password was changed." +msgstr "A sua palavra-passe foi modificada." + +msgid "" +"Please enter your old password, for security's sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Por razões de segurança, por favor introduza a sua palavra-passe antiga e " +"depois introduza a nova duas vezes para que possamos verificar se introduziu " +"corretamente." + +msgid "Change my password" +msgstr "Modificar a minha palavra-passe" + +msgid "Password reset" +msgstr "Palavra-passe de reinicialização" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "A sua palavra-passe foi atribuída. Pode entrar agora." + +msgid "Password reset confirmation" +msgstr "Confirmação da reinicialização da palavra-passe" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Por favor, introduza a sua nova palavra-passe duas vezes para verificarmos " +"se está correcta." + +msgid "New password:" +msgstr "Nova palavra-passe:" + +msgid "Confirm password:" +msgstr "Confirmação da palavra-passe:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"O endereço de reinicialização da palavra-passe é inválido, possivelmente " +"porque já foi usado. Por favor requisite uma nova reinicialização da palavra-" +"passe." + +msgid "" +"We've emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Foram enviadas para o email indicado as instruções de configuração da " +"palavra-passe, se existir uma conta com o email que indicou. Deverá recebê-" +"las brevemente." + +msgid "" +"If you don't receive an email, please make sure you've entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Se não receber um email, por favor assegure-se de que introduziu o endereço " +"com o qual se registou e verifique a sua pasta de correio electrónico não " +"solicitado." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Está a receber este email porque pediu para redefinir a palavra-chave para o " +"seu utilizador no site %(site_name)s." + +msgid "Please go to the following page and choose a new password:" +msgstr "Por favor siga a seguinte página e escolha a sua nova palavra-passe:" + +msgid "Your username, in case you've forgotten:" +msgstr "O seu nome de utilizador, no caso de se ter esquecido:" + +msgid "Thanks for using our site!" +msgstr "Obrigado pela sua visita ao nosso site!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "A equipa do %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we'll email " +"instructions for setting a new one." +msgstr "" +"Esqueceu-se da sua palavra-chave? Introduza o seu endereço de email e enviar-" +"lhe-emos instruções para definir uma nova." + +msgid "Email address:" +msgstr "Endereço de email:" + +msgid "Reset my password" +msgstr "Reinicializar a minha palavra-passe" + +msgid "All dates" +msgstr "Todas as datas" + +#, python-format +msgid "Select %s" +msgstr "Selecionar %s" + +#, python-format +msgid "Select %s to change" +msgstr "Selecione %s para modificar" + +#, python-format +msgid "Select %s to view" +msgstr "Selecione %s para ver" + +msgid "Date:" +msgstr "Data:" + +msgid "Time:" +msgstr "Hora:" + +msgid "Lookup" +msgstr "Procurar" + +msgid "Currently:" +msgstr "Atualmente:" + +msgid "Change:" +msgstr "Modificar:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/pt_BR/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/pt_BR/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..99f16767 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/pt_BR/LC_MESSAGES/django 3.po @@ -0,0 +1,788 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Allisson Azevedo , 2014 +# Bruce de Sá , 2019 +# bruno.devpod , 2014 +# Carlos C. Leite , 2019 +# Carlos C. Leite , 2019 +# Filipe Cifali , 2016 +# dudanogueira , 2012 +# Elyézer Rezende , 2013 +# Fábio C. Barrionuevo da Luz , 2015 +# Fabio Cerqueira , 2019 +# Francisco Petry Rauber , 2016 +# Gladson , 2013 +# Guilherme Ferreira , 2017 +# semente, 2012-2013 +# Jannis Leidel , 2011 +# João Paulo Andrade , 2018 +# Jonas Rodrigues, 2023 +# Lucas Infante , 2015 +# Luiz Boaretto , 2017 +# Marssal Jr. , 2022 +# Marcelo Moro Brondani , 2018 +# Marco Rougeth , 2015 +# Otávio Reis , 2018 +# Raysa Dutra, 2016 +# R.J Lelis , 2019 +# Samuel Nogueira Bacelar , 2020 +# Sergio Garcia , 2015 +# Tomaz Marcelino Cunha Neto , 2022 +# Vinícius Damaceno , 2019 +# Vinícius Muniz de Melo , 2019 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 07:05+0000\n" +"Last-Translator: Jonas Rodrigues, 2023\n" +"Language-Team: Portuguese (Brazil) (http://www.transifex.com/django/django/" +"language/pt_BR/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pt_BR\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Remover %(verbose_name_plural)s selecionados" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Removido %(count)d %(items)s com sucesso." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Não é possível excluir %(name)s " + +msgid "Are you sure?" +msgstr "Tem certeza?" + +msgid "Administration" +msgstr "Administração" + +msgid "All" +msgstr "Todos" + +msgid "Yes" +msgstr "Sim" + +msgid "No" +msgstr "Não" + +msgid "Unknown" +msgstr "Desconhecido" + +msgid "Any date" +msgstr "Qualquer data" + +msgid "Today" +msgstr "Hoje" + +msgid "Past 7 days" +msgstr "Últimos 7 dias" + +msgid "This month" +msgstr "Este mês" + +msgid "This year" +msgstr "Este ano" + +msgid "No date" +msgstr "Sem data" + +msgid "Has date" +msgstr "Tem data" + +msgid "Empty" +msgstr "Vazio" + +msgid "Not empty" +msgstr "Não está vazio" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Por favor, insira um %(username)s e senha corretos para uma conta de equipe. " +"Note que ambos campos são sensíveis a maiúsculas e minúsculas." + +msgid "Action:" +msgstr "Ação:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Adicionar outro(a) %(verbose_name)s" + +msgid "Remove" +msgstr "Remover" + +msgid "Addition" +msgstr "Adição" + +msgid "Change" +msgstr "Modificar" + +msgid "Deletion" +msgstr "Eliminação" + +msgid "action time" +msgstr "hora da ação" + +msgid "user" +msgstr "usuário" + +msgid "content type" +msgstr "tipo de conteúdo" + +msgid "object id" +msgstr "id do objeto" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "repr do objeto" + +msgid "action flag" +msgstr "flag de ação" + +msgid "change message" +msgstr "modificar mensagem" + +msgid "log entry" +msgstr "entrada de log" + +msgid "log entries" +msgstr "entradas de log" + +#, python-format +msgid "Added “%(object)s”." +msgstr "Adicionado “%(object)s”." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "Alterado “%(object)s” — %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "Deletado “%(object)s.”" + +msgid "LogEntry Object" +msgstr "Objeto LogEntry" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "Adicionado {name} “{object}”." + +msgid "Added." +msgstr "Adicionado." + +msgid "and" +msgstr "e" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "Alterado {fields} para {name} “{object}”." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "Alterado {fields}." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "Deletado {name} “{object}”." + +msgid "No fields changed." +msgstr "Nenhum campo modificado." + +msgid "None" +msgstr "Nenhum" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "Pressione “Control”, ou “Command” no Mac, para selecionar mais de um." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "O {name} “{obj}” foi adicionado com sucesso." + +msgid "You may edit it again below." +msgstr "Você pode editá-lo novamente abaixo." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"O {name} “{obj}” foi adicionado com sucesso. Você pode adicionar outro " +"{name} abaixo." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" +"O {name} “{obj}” foi alterado com sucesso. Você pode alterá-lo novamente " +"abaixo." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" +"O {name} “{obj}” foi adicionado com sucesso. Você pode editá-lo novamente " +"abaixo." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" +"O {name} “{obj}” foi alterado com sucesso. Você talvez adicione outro " +"{name} abaixo." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "O {name} “{obj}” foi alterado com sucesso." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Os itens devem ser selecionados em ordem a fim de executar ações sobre eles. " +"Nenhum item foi modificado." + +msgid "No action selected." +msgstr "Nenhuma ação selecionada." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "O %(name)s “%(obj)s” foi deletado com sucesso." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "O %(name)s com ID “%(key)s” não existe. Talvez tenha sido deletado." + +#, python-format +msgid "Add %s" +msgstr "Adicionar %s" + +#, python-format +msgid "Change %s" +msgstr "Modificar %s" + +#, python-format +msgid "View %s" +msgstr "Visualizar %s" + +msgid "Database error" +msgstr "Erro no banco de dados" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s modificado com sucesso." +msgstr[1] "%(count)s %(name)s modificados com sucesso." +msgstr[2] "%(count)s %(name)s modificados com sucesso." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s selecionado" +msgstr[1] "Todos %(total_count)s selecionados" +msgstr[2] "Todos %(total_count)s selecionados" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 de %(cnt)s selecionados" + +#, python-format +msgid "Change history: %s" +msgstr "Histórico de modificações: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Excluir o %(class_name)s %(instance)s exigiria excluir os seguintes objetos " +"protegidos relacionados: %(related_objects)s" + +msgid "Django site admin" +msgstr "Site de administração do Django" + +msgid "Django administration" +msgstr "Administração do Django" + +msgid "Site administration" +msgstr "Administração do Site" + +msgid "Log in" +msgstr "Acessar" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s administração" + +msgid "Page not found" +msgstr "Página não encontrada" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Lamentamos, mas a página requisitada não pode ser encontrada." + +msgid "Home" +msgstr "Início" + +msgid "Server error" +msgstr "Erro no servidor" + +msgid "Server error (500)" +msgstr "Erro no servidor (500)" + +msgid "Server Error (500)" +msgstr "Erro no Servidor (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Ocorreu um erro. Este foi reportado para os administradores do site via " +"email e deve ser corrigido logo. Obirgado por sua paciência." + +msgid "Run the selected action" +msgstr "Executar ação selecionada" + +msgid "Go" +msgstr "Ir" + +msgid "Click here to select the objects across all pages" +msgstr "Clique aqui para selecionar os objetos de todas as páginas" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Selecionar todos %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "Limpar seleção" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Modelos na aplicação %(name)s" + +msgid "Add" +msgstr "Adicionar" + +msgid "View" +msgstr "Visualizar" + +msgid "You don’t have permission to view or edit anything." +msgstr "Você não tem permissão para ver ou editar nada." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Primeiro, informe seu nome de usuário e senha. Então, você poderá editar " +"outras opções do usuário." + +msgid "Enter a username and password." +msgstr "Digite um nome de usuário e senha." + +msgid "Change password" +msgstr "Alterar senha" + +msgid "Please correct the error below." +msgid_plural "Please correct the errors below." +msgstr[0] "Por favor corrija o erro abaixo." +msgstr[1] "Por favor corrija os erros abaixo." +msgstr[2] "Por favor corrija os erros abaixo." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "Informe uma nova senha para o usuário %(username)s." + +msgid "Skip to main content" +msgstr "Pule o conteúdo principal" + +msgid "Welcome," +msgstr "Bem-vindo(a)," + +msgid "View site" +msgstr "Ver o site" + +msgid "Documentation" +msgstr "Documentação" + +msgid "Log out" +msgstr "Encerrar sessão" + +msgid "Breadcrumbs" +msgstr "Migalhas de pão" + +#, python-format +msgid "Add %(name)s" +msgstr "Adicionar %(name)s" + +msgid "History" +msgstr "Histórico" + +msgid "View on site" +msgstr "Ver no site" + +msgid "Filter" +msgstr "Filtro" + +msgid "Clear all filters" +msgstr "Limpar todos os filtros" + +msgid "Remove from sorting" +msgstr "Remover da ordenação" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Prioridade da ordenação: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Alternar ordenção" + +msgid "Toggle theme (current theme: auto)" +msgstr "Alternar tema (tema atual: automático)" + +msgid "Toggle theme (current theme: light)" +msgstr "Alternar tema (tema atual: claro)" + +msgid "Toggle theme (current theme: dark)" +msgstr "Alternar tema (tema atual: escuro)" + +msgid "Delete" +msgstr "Apagar" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"A remoção de '%(object_name)s' %(escaped_object)s pode resultar na remoção " +"de objetos relacionados, mas sua conta não tem a permissão para remoção dos " +"seguintes tipos de objetos:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Excluir o %(object_name)s ' %(escaped_object)s ' exigiria excluir os " +"seguintes objetos protegidos relacionados:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Você tem certeza que quer remover %(object_name)s \"%(escaped_object)s\"? " +"Todos os seguintes itens relacionados serão removidos:" + +msgid "Objects" +msgstr "Objetos" + +msgid "Yes, I’m sure" +msgstr "Sim, eu tenho certeza" + +msgid "No, take me back" +msgstr "Não, me leve de volta" + +msgid "Delete multiple objects" +msgstr "Remover múltiplos objetos" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Excluir o %(objects_name)s selecionado pode resultar na remoção de objetos " +"relacionados, mas sua conta não tem permissão para excluir os seguintes " +"tipos de objetos:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Excluir o %(objects_name)s selecionado exigiria excluir os seguintes objetos " +"relacionados protegidos:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Tem certeza de que deseja apagar o %(objects_name)s selecionado? Todos os " +"seguintes objetos e seus itens relacionados serão removidos:" + +msgid "Delete?" +msgstr "Apagar?" + +#, python-format +msgid " By %(filter_title)s " +msgstr "Por %(filter_title)s " + +msgid "Summary" +msgstr "Resumo" + +msgid "Recent actions" +msgstr "Ações recentes" + +msgid "My actions" +msgstr "Minhas Ações" + +msgid "None available" +msgstr "Nenhum disponível" + +msgid "Unknown content" +msgstr "Conteúdo desconhecido" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Alguma coisa está errada com sua estalação do banco de dados. Certifique-se " +"que as tabelas apropriadas foram criadas, e certifique-se que o banco de " +"dados pode ser acessado pelo usuário apropriado." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Você está autenticado como %(username)s, mas não está autorizado a acessar " +"esta página. Você gostaria de realizar login com uma conta diferente?" + +msgid "Forgotten your password or username?" +msgstr "Esqueceu sua senha ou nome de usuário?" + +msgid "Toggle navigation" +msgstr "Alternar navegação" + +msgid "Sidebar" +msgstr "Barra Lateral" + +msgid "Start typing to filter…" +msgstr "Comece a digitar para filtrar…" + +msgid "Filter navigation items" +msgstr "Filtrar itens de navegação" + +msgid "Date/time" +msgstr "Data/hora" + +msgid "User" +msgstr "Usuário" + +msgid "Action" +msgstr "Ação" + +msgid "entry" +msgid_plural "entries" +msgstr[0] "entrada" +msgstr[1] "entradas" +msgstr[2] "entradas" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"Este objeto não tem histórico de alterações. Provavelmente não adicionado " +"por este site de administração." + +msgid "Show all" +msgstr "Mostrar tudo" + +msgid "Save" +msgstr "Salvar" + +msgid "Popup closing…" +msgstr "Popup fechando…" + +msgid "Search" +msgstr "Pesquisar" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s resultado" +msgstr[1] "%(counter)s resultados" +msgstr[2] "%(counter)s resultados" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s total" + +msgid "Save as new" +msgstr "Salvar como novo" + +msgid "Save and add another" +msgstr "Salvar e adicionar outro(a)" + +msgid "Save and continue editing" +msgstr "Salvar e continuar editando" + +msgid "Save and view" +msgstr "Salvar e visualizar" + +msgid "Close" +msgstr "Fechar" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Alterar %(model)s selecionado" + +#, python-format +msgid "Add another %(model)s" +msgstr "Adicionar outro %(model)s" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Excluir %(model)s selecionado" + +#, python-format +msgid "View selected %(model)s" +msgstr "Visualizar %(model)s selecionados" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "Obrigado por passar algum tempo de qualidade com o site hoje." + +msgid "Log in again" +msgstr "Acessar novamente" + +msgid "Password change" +msgstr "Alterar senha" + +msgid "Your password was changed." +msgstr "Sua senha foi alterada." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Informe sua senha antiga por favor, por motivos de segurança, e então " +"informe sua nova senha duas vezes para que possamos verificar se você " +"digitou tudo corretamente." + +msgid "Change my password" +msgstr "Alterar minha senha" + +msgid "Password reset" +msgstr "Recuperar senha" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Sua senha foi definida. Você pode prosseguir e se autenticar agora." + +msgid "Password reset confirmation" +msgstr "Confirmação de recuperação de senha" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Por favor, informe sua nova senha duas vezes para que possamos verificar se " +"você a digitou corretamente." + +msgid "New password:" +msgstr "Nova senha:" + +msgid "Confirm password:" +msgstr "Confirme a senha:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"O link para a recuperação de senha era inválido, possivelmente porque já foi " +"utilizado. Por favor, solicite uma nova recuperação de senha." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Nos te enviamos um email com instruções para configurar sua senha, se uma " +"conta existe com o email fornecido. Você receberá a mensagem em breve." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Se você não recebeu um email, por favor certifique-se que você forneceu o " +"endereço que você está cadastrado, e verifique sua pasta de spam." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Você está recebendo este email porque solicitou a redefinição da senha da " +"sua conta em %(site_name)s." + +msgid "Please go to the following page and choose a new password:" +msgstr "Por favor, acesse a seguinte página e escolha uma nova senha:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "Seu nome de usuário, caso tenha esquecido:" + +msgid "Thanks for using our site!" +msgstr "Obrigado por usar nosso site!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "Equipe %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Esqueceu sua senha? Forneça seu endereço de email abaixo, e nos te " +"enviaremos um email com instruções para configurar uma nova." + +msgid "Email address:" +msgstr "Endereço de email:" + +msgid "Reset my password" +msgstr "Reinicializar minha senha" + +msgid "All dates" +msgstr "Todas as datas" + +#, python-format +msgid "Select %s" +msgstr "Selecione %s" + +#, python-format +msgid "Select %s to change" +msgstr "Selecione %s para modificar" + +#, python-format +msgid "Select %s to view" +msgstr "Selecione %s para visualizar" + +msgid "Date:" +msgstr "Data:" + +msgid "Time:" +msgstr "Hora:" + +msgid "Lookup" +msgstr "Procurar" + +msgid "Currently:" +msgstr "Atualmente:" + +msgid "Change:" +msgstr "Alterar:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/sl/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/sl/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..c51cd56c --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/sl/LC_MESSAGES/django 3.po @@ -0,0 +1,690 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Jannis Leidel , 2011 +# Primož Verdnik , 2017 +# zejn , 2013,2016 +# zejn , 2011-2013 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2017-09-23 18:54+0000\n" +"Last-Translator: Primož Verdnik \n" +"Language-Team: Slovenian (http://www.transifex.com/django/django/language/" +"sl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: sl\n" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n" +"%100==4 ? 2 : 3);\n" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Uspešno izbrisano %(count)d %(items)s." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Ni mogoče izbrisati %(name)s" + +msgid "Are you sure?" +msgstr "Ste prepričani?" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Izbriši izbrano: %(verbose_name_plural)s" + +msgid "Administration" +msgstr "Administracija" + +msgid "All" +msgstr "Vse" + +msgid "Yes" +msgstr "Da" + +msgid "No" +msgstr "Ne" + +msgid "Unknown" +msgstr "Neznano" + +msgid "Any date" +msgstr "Kadarkoli" + +msgid "Today" +msgstr "Danes" + +msgid "Past 7 days" +msgstr "Zadnjih 7 dni" + +msgid "This month" +msgstr "Ta mesec" + +msgid "This year" +msgstr "Letos" + +msgid "No date" +msgstr "Brez datuma" + +msgid "Has date" +msgstr "Z datumom" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Vnesite veljavno %(username)s in geslo za račun osebja. Opomba: obe polji " +"upoštevata velikost črk." + +msgid "Action:" +msgstr "Dejanje:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Dodaj še en %(verbose_name)s" + +msgid "Remove" +msgstr "Odstrani" + +msgid "action time" +msgstr "čas dejanja" + +msgid "user" +msgstr "uporabnik" + +msgid "content type" +msgstr "vrsta vsebine" + +msgid "object id" +msgstr "id objekta" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/3/library/functions.html#repr) +msgid "object repr" +msgstr "predstavitev objekta" + +msgid "action flag" +msgstr "zastavica dejanja" + +msgid "change message" +msgstr "spremeni sporočilo" + +msgid "log entry" +msgstr "dnevniški vnos" + +msgid "log entries" +msgstr "dnevniški vnosi" + +#, python-format +msgid "Added \"%(object)s\"." +msgstr "Dodan \"%(object)s\"." + +#, python-format +msgid "Changed \"%(object)s\" - %(changes)s" +msgstr "Spremenjen \"%(object)s\" - %(changes)s" + +#, python-format +msgid "Deleted \"%(object)s.\"" +msgstr "Izbrisan \"%(object)s.\"" + +msgid "LogEntry Object" +msgstr "Dnevniški vnos" + +#, python-brace-format +msgid "Added {name} \"{object}\"." +msgstr "Dodan vnos {name} \"{object}\"." + +msgid "Added." +msgstr "Dodano." + +msgid "and" +msgstr "in" + +#, python-brace-format +msgid "Changed {fields} for {name} \"{object}\"." +msgstr "Spremenjena polja {fields} za {name} \"{object}\"." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "Spremenjena polja {fields}." + +#, python-brace-format +msgid "Deleted {name} \"{object}\"." +msgstr "Izbrisan vnos {name} \"{object}\"." + +msgid "No fields changed." +msgstr "Nobeno polje ni bilo spremenjeno." + +msgid "None" +msgstr "Brez vrednosti" + +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "Držite \"Control\" (ali \"Command\" na Mac-u) za izbiro več kot enega." + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may edit it again below." +msgstr "" +"Vnos {name} \"{obj}\" je bil uspešno dodan. Lahko ga znova uredite spodaj." + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may add another {name} " +"below." +msgstr "" +"Vnos {name} \"{obj}\" je bil uspešno dodan. Lahko dodate še en {name} spodaj." + +#, python-brace-format +msgid "The {name} \"{obj}\" was added successfully." +msgstr "Vnos {name} \"{obj}\" je bil uspešno dodan." + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." +msgstr "" +"Vnos {name} \"{obj}\" je bil uspešno spremenjen. Lahko ga znova uredite " +"spodaj." + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." +msgstr "" +"Vnos {name} \"{obj}\" je bil uspešno spremenjen. Spodaj lahko dodate nov " +"vnos {name}." + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." +msgstr "Vnos {name} \"{obj}\" je bil uspešno spremenjen." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Izbrati morate vnose, nad katerimi želite izvesti operacijo. Noben vnos ni " +"bil spremenjen." + +msgid "No action selected." +msgstr "Brez dejanja." + +#, python-format +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "%(name)s \"%(obj)s\" je bil uspešno izbrisan." + +#, python-format +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" +msgstr "%(name)s s ključem \"%(key)s\" ne obstaja. Morda je bil izbrisan?" + +#, python-format +msgid "Add %s" +msgstr "Dodaj %s" + +#, python-format +msgid "Change %s" +msgstr "Spremeni %s" + +msgid "Database error" +msgstr "Napaka v podatkovni bazi" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s je bil uspešno spremenjen." +msgstr[1] "%(count)s %(name)s sta bila uspešno spremenjena." +msgstr[2] "%(count)s %(name)s so bili uspešno spremenjeni." +msgstr[3] "%(count)s %(name)s je bilo uspešno spremenjenih." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s izbran" +msgstr[1] "%(total_count)s izbrana" +msgstr[2] "Vsi %(total_count)s izbrani" +msgstr[3] "Vseh %(total_count)s izbranih" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 od %(cnt)s izbranih" + +#, python-format +msgid "Change history: %s" +msgstr "Zgodovina sprememb: %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Brisanje %(class_name)s %(instance)s bi zahtevalo brisanje naslednjih " +"zaščitenih povezanih objektov: %(related_objects)s" + +msgid "Django site admin" +msgstr "Django administrativni vmesnik" + +msgid "Django administration" +msgstr "Django administracija" + +msgid "Site administration" +msgstr "Administracija strani" + +msgid "Log in" +msgstr "Prijavite se" + +#, python-format +msgid "%(app)s administration" +msgstr "Administracija %(app)s" + +msgid "Page not found" +msgstr "Strani ni mogoče najti" + +msgid "We're sorry, but the requested page could not be found." +msgstr "Opravičujemo se, a zahtevane strani ni mogoče najti." + +msgid "Home" +msgstr "Domov" + +msgid "Server error" +msgstr "Napaka na strežniku" + +msgid "Server error (500)" +msgstr "Napaka na strežniku (500)" + +msgid "Server Error (500)" +msgstr "Napaka na strežniku (500)" + +msgid "" +"There's been an error. It's been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Prišlo je do nepričakovane napake. Napaka je bila javljena administratorjem " +"spletne strani in naj bi jo v kratkem odpravili. Hvala za potrpljenje." + +msgid "Run the selected action" +msgstr "Izvedi izbrano dejanje" + +msgid "Go" +msgstr "Pojdi" + +msgid "Click here to select the objects across all pages" +msgstr "Kliknite tu za izbiro vseh vnosov na vseh straneh" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Izberi vse %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "Počisti izbiro" + +msgid "" +"First, enter a username and password. Then, you'll be able to edit more user " +"options." +msgstr "" +"Najprej vpišite uporabniško ime in geslo, nato boste lahko urejali druge " +"lastnosti uporabnika." + +msgid "Enter a username and password." +msgstr "Vnesite uporabniško ime in geslo." + +msgid "Change password" +msgstr "Spremeni geslo" + +msgid "Please correct the error below." +msgstr "Prosimo, odpravite sledeče napake." + +msgid "Please correct the errors below." +msgstr "Prosimo popravite spodnje napake." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "Vpišite novo geslo za uporabnika %(username)s." + +msgid "Welcome," +msgstr "Dobrodošli," + +msgid "View site" +msgstr "Poglej stran" + +msgid "Documentation" +msgstr "Dokumentacija" + +msgid "Log out" +msgstr "Odjava" + +#, python-format +msgid "Add %(name)s" +msgstr "Dodaj %(name)s" + +msgid "History" +msgstr "Zgodovina" + +msgid "View on site" +msgstr "Poglej na strani" + +msgid "Filter" +msgstr "Filter" + +msgid "Remove from sorting" +msgstr "Odstrani iz razvrščanja" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Prioriteta razvrščanja: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Preklopi razvrščanje" + +msgid "Delete" +msgstr "Izbriši" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Izbris %(object_name)s '%(escaped_object)s' bi pomenil izbris povezanih " +"objektov, vendar nimate dovoljenja za izbris naslednjih tipov objektov:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Brisanje %(object_name)s '%(escaped_object)s' bi zahtevalo brisanje " +"naslednjih zaščitenih povezanih objektov:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Ste prepričani, da želite izbrisati %(object_name)s \"%(escaped_object)s\"? " +"Vsi naslednji povezani elementi bodo izbrisani:" + +msgid "Objects" +msgstr "Objekti" + +msgid "Yes, I'm sure" +msgstr "Ja, prepričan sem" + +msgid "No, take me back" +msgstr "Ne, vrni me nazaj" + +msgid "Delete multiple objects" +msgstr "Izbriši več objektov" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Brisanje naslendjih %(objects_name)s bi imelo za posledico izbris naslednjih " +"povezanih objektov, vendar vaš račun nima pravic za izbris naslednjih tipov " +"objektov:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Brisanje izbranih %(objects_name)s zahteva brisanje naslednjih zaščitenih " +"povezanih objektov:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Ali res želite izbrisati izbrane %(objects_name)s? Vsi naslednji objekti in " +"njihovi povezani vnosi bodo izbrisani:" + +msgid "Change" +msgstr "Spremeni" + +msgid "Delete?" +msgstr "Izbrišem?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " Po %(filter_title)s " + +msgid "Summary" +msgstr "Povzetek" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Model v %(name)s aplikaciji" + +msgid "Add" +msgstr "Dodaj" + +msgid "You don't have permission to edit anything." +msgstr "Nimate dovoljenja za urejanje česarkoli." + +msgid "Recent actions" +msgstr "Nedavna dejanja" + +msgid "My actions" +msgstr "Moja dejanja" + +msgid "None available" +msgstr "Ni na voljo" + +msgid "Unknown content" +msgstr "Neznana vsebina" + +msgid "" +"Something's wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Nekaj je narobe z namestitvijo vaše podatkovne baze. Preverite, da so bile " +"ustvarjene prave tabele v podatkovni bazi in da je dostop do branja baze " +"omogočen pravemu uporabniku." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Prijavljeni ste kot %(username)s in nimate pravic za dostop do te strani. Bi " +"se želeli prijaviti z drugim računom?" + +msgid "Forgotten your password or username?" +msgstr "Ste pozabili geslo ali uporabniško ime?" + +msgid "Date/time" +msgstr "Datum/čas" + +msgid "User" +msgstr "Uporabnik" + +msgid "Action" +msgstr "Dejanje" + +msgid "" +"This object doesn't have a change history. It probably wasn't added via this " +"admin site." +msgstr "" +"Ta objekt nima zgodovine sprememb. Verjetno ni bil dodan preko te strani za " +"administracijo." + +msgid "Show all" +msgstr "Prikaži vse" + +msgid "Save" +msgstr "Shrani" + +msgid "Popup closing..." +msgstr "Zapiram pojavno okno ..." + +#, python-format +msgid "Change selected %(model)s" +msgstr "Spremeni izbran %(model)s" + +#, python-format +msgid "Add another %(model)s" +msgstr "Dodaj še en %(model)s " + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Izbriši izbran %(model)s" + +msgid "Search" +msgstr "Išči" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s zadetkov" +msgstr[1] "%(counter)s zadetek" +msgstr[2] "%(counter)s zadetka" +msgstr[3] "%(counter)s zadetki" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s skupno" + +msgid "Save as new" +msgstr "Shrani kot novo" + +msgid "Save and add another" +msgstr "Shrani in dodaj še eno" + +msgid "Save and continue editing" +msgstr "Shrani in nadaljuj z urejanjem" + +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Hvala, ker ste si danes vzeli nekaj časa za to spletno stran." + +msgid "Log in again" +msgstr "Ponovna prijava" + +msgid "Password change" +msgstr "Sprememba gesla" + +msgid "Your password was changed." +msgstr "Vaše geslo je bilo spremenjeno." + +msgid "" +"Please enter your old password, for security's sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Vnesite vaše staro geslo (zaradi varnosti) in nato še dvakrat novo, da se " +"izognete tipkarskim napakam." + +msgid "Change my password" +msgstr "Spremeni moje geslo" + +msgid "Password reset" +msgstr "Ponastavitev gesla" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Vaše geslo je bilo nastavljeno. Zdaj se lahko prijavite." + +msgid "Password reset confirmation" +msgstr "Potrdite ponastavitev gesla" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "Vnesite vaše novo geslo dvakrat, da se izognete tipkarskim napakam." + +msgid "New password:" +msgstr "Novo geslo:" + +msgid "Confirm password:" +msgstr "Potrditev gesla:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Povezava za ponastavitev gesla ni bila veljavna, morda je bila že " +"uporabljena. Prosimo zahtevajte novo ponastavitev gesla." + +msgid "" +"We've emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Če obstaja račun z navedenim e-poštnim naslovom, smo vam prek epošte poslali " +"navodila za nastavitev vašega gesla. Prejeti bi jih morali v kratkem." + +msgid "" +"If you don't receive an email, please make sure you've entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Če e-pošte niste prejeli, prosimo preverite, da ste vnesli pravilen e-poštni " +"naslov in preverite nezaželeno pošto." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"To e-pošto ste prejeli, ker je ste zahtevali ponastavitev gesla za vaš " +"uporabniški račun na %(site_name)s." + +msgid "Please go to the following page and choose a new password:" +msgstr "Prosimo pojdite na sledečo stran in izberite novo geslo:" + +msgid "Your username, in case you've forgotten:" +msgstr "Vaše uporabniško ime (za vsak primer):" + +msgid "Thanks for using our site!" +msgstr "Hvala, ker uporabljate našo stran!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "Ekipa strani %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we'll email " +"instructions for setting a new one." +msgstr "" +"Ste pozabili geslo? Vnesite vaš e-poštni naslov in poslali vam bomo navodila " +"za ponastavitev gesla." + +msgid "Email address:" +msgstr "E-poštni naslov:" + +msgid "Reset my password" +msgstr "Ponastavi moje geslo" + +msgid "All dates" +msgstr "Vsi datumi" + +#, python-format +msgid "Select %s" +msgstr "Izberite %s" + +#, python-format +msgid "Select %s to change" +msgstr "Izberite %s, ki ga želite spremeniti" + +msgid "Date:" +msgstr "Datum:" + +msgid "Time:" +msgstr "Ura:" + +msgid "Lookup" +msgstr "Poizvedba" + +msgid "Currently:" +msgstr "Trenutno:" + +msgid "Change:" +msgstr "Spremembe:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/sr/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/sr/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..ec32c12e --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/sr/LC_MESSAGES/django 3.po @@ -0,0 +1,719 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Branko Kokanovic , 2018 +# Igor Jerosimić, 2019,2021 +# Jannis Leidel , 2011 +# Janos Guljas , 2011-2012 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-05-17 05:10-0500\n" +"PO-Revision-Date: 2021-12-25 07:05+0000\n" +"Last-Translator: Igor Jerosimić\n" +"Language-Team: Serbian (http://www.transifex.com/django/django/language/" +"sr/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: sr\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Бриши означене објекте класе %(verbose_name_plural)s" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Успешно обрисано: %(count)d %(items)s." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Несуспело брисање %(name)s" + +msgid "Are you sure?" +msgstr "Да ли сте сигурни?" + +msgid "Administration" +msgstr "Администрација" + +msgid "All" +msgstr "Сви" + +msgid "Yes" +msgstr "Да" + +msgid "No" +msgstr "Не" + +msgid "Unknown" +msgstr "Непознато" + +msgid "Any date" +msgstr "Сви датуми" + +msgid "Today" +msgstr "Данас" + +msgid "Past 7 days" +msgstr "Последњих 7 дана" + +msgid "This month" +msgstr "Овај месец" + +msgid "This year" +msgstr "Ова година" + +msgid "No date" +msgstr "Нема датума" + +msgid "Has date" +msgstr "Има датум" + +msgid "Empty" +msgstr "Празно" + +msgid "Not empty" +msgstr "Није празно" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Молим вас унесите исправно %(username)s и лозинку. Обратите пажњу да мала и " +"велика слова представљају различите карактере." + +msgid "Action:" +msgstr "Радња:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Додај још један објекат класе %(verbose_name)s." + +msgid "Remove" +msgstr "Обриши" + +msgid "Addition" +msgstr "Додавања" + +msgid "Change" +msgstr "Измени" + +msgid "Deletion" +msgstr "Брисања" + +msgid "action time" +msgstr "време радње" + +msgid "user" +msgstr "корисник" + +msgid "content type" +msgstr "тип садржаја" + +msgid "object id" +msgstr "id објекта" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "опис објекта" + +msgid "action flag" +msgstr "ознака радње" + +msgid "change message" +msgstr "опис измене" + +msgid "log entry" +msgstr "запис у логовима" + +msgid "log entries" +msgstr "записи у логовима" + +#, python-format +msgid "Added “%(object)s”." +msgstr "Додат објекат класе „%(object)s“." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "Промењен објекат класе „%(object)s“ - %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "Уклоњен објекат класе „%(object)s“." + +msgid "LogEntry Object" +msgstr "Објекат уноса лога" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "Додат објекат {name} \"{object}\"." + +msgid "Added." +msgstr "Додато." + +msgid "and" +msgstr "и" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "" + +#, python-brace-format +msgid "Changed {fields}." +msgstr "Измењена поља {fields}." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "" + +msgid "No fields changed." +msgstr "Без измена у пољима." + +msgid "None" +msgstr "Ништа" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "" + +msgid "You may edit it again below." +msgstr "Можете га изменити опет испод" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Потребно је изабрати објекте да би се извршила акција над њима. Ниједан " +"објекат није промењен." + +msgid "No action selected." +msgstr "Није изабрана ниједна акција." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "" + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "" + +#, python-format +msgid "Add %s" +msgstr "Додај објекат класе %s" + +#, python-format +msgid "Change %s" +msgstr "Измени објекат класе %s" + +#, python-format +msgid "View %s" +msgstr "Преглед %s" + +msgid "Database error" +msgstr "Грешка у бази података" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "Успешно промењен %(count)s %(name)s." +msgstr[1] "Успешно промењена %(count)s %(name)s." +msgstr[2] "Успешно промењених %(count)s %(name)s." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s изабран" +msgstr[1] "Сва %(total_count)s изабрана" +msgstr[2] "Свих %(total_count)s изабраних" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 од %(cnt)s изабрано" + +#, python-format +msgid "Change history: %s" +msgstr "Историјат измена: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Да би избрисали %(class_name)s%(instance)s потребно је брисати и следеће " +"заштићене повезане објекте: %(related_objects)s" + +msgid "Django site admin" +msgstr "Django администрација сајта" + +msgid "Django administration" +msgstr "Django администрација" + +msgid "Site administration" +msgstr "Администрација система" + +msgid "Log in" +msgstr "Пријава" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s администрација" + +msgid "Page not found" +msgstr "Страница није пронађена" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "" + +msgid "Home" +msgstr "Почетна" + +msgid "Server error" +msgstr "Грешка на серверу" + +msgid "Server error (500)" +msgstr "Грешка на серверу (500)" + +msgid "Server Error (500)" +msgstr "Грешка на серверу (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" + +msgid "Run the selected action" +msgstr "Покрени одабрану радњу" + +msgid "Go" +msgstr "Почни" + +msgid "Click here to select the objects across all pages" +msgstr "Изабери све објекте на овој страници." + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Изабери све %(module_name)s од %(total_count)s укупно." + +msgid "Clear selection" +msgstr "Поништи избор" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Модели у апликацији %(name)s" + +msgid "Add" +msgstr "Додај" + +msgid "View" +msgstr "Преглед" + +msgid "You don’t have permission to view or edit anything." +msgstr "" + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" + +msgid "Enter a username and password." +msgstr "Унесите корисничко име и лозинку" + +msgid "Change password" +msgstr "Промена лозинке" + +msgid "Please correct the error below." +msgstr "Молимо исправите грешку испод." + +msgid "Please correct the errors below." +msgstr "Исправите грешке испод." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "Унесите нову лозинку за корисника %(username)s." + +msgid "Welcome," +msgstr "Добродошли," + +msgid "View site" +msgstr "Погледај сајт" + +msgid "Documentation" +msgstr "Документација" + +msgid "Log out" +msgstr "Одјава" + +#, python-format +msgid "Add %(name)s" +msgstr "Додај објекат класе %(name)s" + +msgid "History" +msgstr "Историјат" + +msgid "View on site" +msgstr "Преглед на сајту" + +msgid "Filter" +msgstr "Филтер" + +msgid "Clear all filters" +msgstr "Обриши све филтере" + +msgid "Remove from sorting" +msgstr "Избаци из сортирања" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Приоритет сортирања: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Укључи/искључи сортирање" + +msgid "Delete" +msgstr "Обриши" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Уклањање %(object_name)s „%(escaped_object)s“ повлачи уклањање свих објеката " +"који су повезани са овим објектом, али ваш налог нема дозволе за брисање " +"следећих типова објеката:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Да би избрисали изабран %(object_name)s „%(escaped_object)s“ потребно је " +"брисати и следеће заштићене повезане објекте:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Да сигурни да желите да обришете %(object_name)s „%(escaped_object)s“? " +"Следећи објекти који су у вези са овим објектом ће такође бити обрисани:" + +msgid "Objects" +msgstr "Објекти" + +msgid "Yes, I’m sure" +msgstr "Да, сигуран сам" + +msgid "No, take me back" +msgstr "Не, хоћу назад" + +msgid "Delete multiple objects" +msgstr "Брисање више објеката" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Да би избрисали изабране %(objects_name)s потребно је брисати и заштићене " +"повезане објекте, међутим ваш налог нема дозволе за брисање следећих типова " +"објеката:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Да би избрисали изабране %(objects_name)s потребно је брисати и следеће " +"заштићене повезане објекте:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Да ли сте сигурни да желите да избришете изабране %(objects_name)s? Сви " +"следећи објекти и објекти са њима повезани ће бити избрисани:" + +msgid "Delete?" +msgstr "Брисање?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " %(filter_title)s " + +msgid "Summary" +msgstr "Сумарно" + +msgid "Recent actions" +msgstr "Скорашње акције" + +msgid "My actions" +msgstr "Моје акције" + +msgid "None available" +msgstr "Нема података" + +msgid "Unknown content" +msgstr "Непознат садржај" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Пријављени сте као %(username)s, али немате овлашћења да приступите овој " +"страни. Да ли желите да се пријавите под неким другим налогом?" + +msgid "Forgotten your password or username?" +msgstr "Заборавили сте лозинку или корисничко име?" + +msgid "Toggle navigation" +msgstr "Укључи/искључи мени" + +msgid "Start typing to filter…" +msgstr "" + +msgid "Filter navigation items" +msgstr "" + +msgid "Date/time" +msgstr "Датум/време" + +msgid "User" +msgstr "Корисник" + +msgid "Action" +msgstr "Радња" + +msgid "entry" +msgstr "" + +msgid "entries" +msgstr "" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" + +msgid "Show all" +msgstr "Прикажи све" + +msgid "Save" +msgstr "Сачувај" + +msgid "Popup closing…" +msgstr "Попуп се затвара..." + +msgid "Search" +msgstr "Претрага" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s резултат" +msgstr[1] "%(counter)s резултата" +msgstr[2] "%(counter)s резултата" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "укупно %(full_result_count)s" + +msgid "Save as new" +msgstr "Сачувај као нови" + +msgid "Save and add another" +msgstr "Сачувај и додај следећи" + +msgid "Save and continue editing" +msgstr "Сачувај и настави са изменама" + +msgid "Save and view" +msgstr "Сними и погледај" + +msgid "Close" +msgstr "Затвори" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Измени одабрани модел %(model)s" + +#, python-format +msgid "Add another %(model)s" +msgstr "Додај још један модел %(model)s" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Обриши одабрани модел %(model)s" + +#, python-format +msgid "View selected %(model)s" +msgstr "" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "" + +msgid "Log in again" +msgstr "Поновна пријава" + +msgid "Password change" +msgstr "Измена лозинке" + +msgid "Your password was changed." +msgstr "Ваша лозинка је измењена." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" + +msgid "Change my password" +msgstr "Измени моју лозинку" + +msgid "Password reset" +msgstr "Ресетовање лозинке" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Ваша лозинка је постављена. Можете се пријавити." + +msgid "Password reset confirmation" +msgstr "Потврда ресетовања лозинке" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Унесите нову лозинку два пута како бисмо могли да проверимо да ли сте је " +"правилно унели." + +msgid "New password:" +msgstr "Нова лозинка:" + +msgid "Confirm password:" +msgstr "Потврда лозинке:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Линк за ресетовање лозинке није важећи, вероватно зато што је већ " +"искоришћен. Поново затражите ресетовање лозинке." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Примате ову поруку зато што сте затражили ресетовање лозинке за кориснички " +"налог на сајту %(site_name)s." + +msgid "Please go to the following page and choose a new password:" +msgstr "Идите на следећу страницу и поставите нову лозинку." + +msgid "Your username, in case you’ve forgotten:" +msgstr "Ваше корисничко име, уколико сте заборавили:" + +msgid "Thanks for using our site!" +msgstr "Хвала што користите наш сајт!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "Екипа сајта %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" + +msgid "Email address:" +msgstr "Адреса е-поште:" + +msgid "Reset my password" +msgstr "Ресетуј моју лозинку" + +msgid "All dates" +msgstr "Сви датуми" + +#, python-format +msgid "Select %s" +msgstr "Одабери објекат класе %s" + +#, python-format +msgid "Select %s to change" +msgstr "Одабери објекат класе %s за измену" + +#, python-format +msgid "Select %s to view" +msgstr "Одабери %s за преглед" + +msgid "Date:" +msgstr "Датум:" + +msgid "Time:" +msgstr "Време:" + +msgid "Lookup" +msgstr "Претражи" + +msgid "Currently:" +msgstr "Тренутно:" + +msgid "Change:" +msgstr "Измена:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/sw/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/sw/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..9a6706b1 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/sw/LC_MESSAGES/django 3.po @@ -0,0 +1,676 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Machaku , 2013-2014 +# Machaku , 2016 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2017-09-23 18:54+0000\n" +"Last-Translator: Jannis Leidel \n" +"Language-Team: Swahili (http://www.transifex.com/django/django/language/" +"sw/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: sw\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Umefanikiwa kufuta %(items)s %(count)d." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Huwezi kufuta %(name)s" + +msgid "Are you sure?" +msgstr "Una uhakika?" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Futa %(verbose_name_plural)s teule" + +msgid "Administration" +msgstr "Utawala" + +msgid "All" +msgstr "yote" + +msgid "Yes" +msgstr "Ndiyo" + +msgid "No" +msgstr "Hapana" + +msgid "Unknown" +msgstr "Haijulikani" + +msgid "Any date" +msgstr "Tarehe yoyote" + +msgid "Today" +msgstr "Leo" + +msgid "Past 7 days" +msgstr "Siku 7 zilizopita" + +msgid "This month" +msgstr "mwezi huu" + +msgid "This year" +msgstr "Mwaka huu" + +msgid "No date" +msgstr "Hakuna tarehe" + +msgid "Has date" +msgstr "Kuna tarehe" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Tafadhali ingiza %(username)s na nywila sahihi kwa akaunti ya msimamizi. " +"Kumbuka kuzingatia herufi kubwa na ndogo." + +msgid "Action:" +msgstr "Tendo" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Ongeza %(verbose_name)s" + +msgid "Remove" +msgstr "Ondoa" + +msgid "action time" +msgstr "muda wa tendo" + +msgid "user" +msgstr "mtumiaji" + +msgid "content type" +msgstr "aina ya maudhui" + +msgid "object id" +msgstr "Kitambulisho cha kitu" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/3/library/functions.html#repr) +msgid "object repr" +msgstr "`repr` ya kitu" + +msgid "action flag" +msgstr "bendera ya tendo" + +msgid "change message" +msgstr "badilisha ujumbe" + +msgid "log entry" +msgstr "ingizo kwenye kumbukumbu" + +msgid "log entries" +msgstr "maingizo kwenye kumbukumbu" + +#, python-format +msgid "Added \"%(object)s\"." +msgstr "Kuongezwa kwa \"%(object)s\"." + +#, python-format +msgid "Changed \"%(object)s\" - %(changes)s" +msgstr "Kubadilishwa kwa \"%(object)s\" - %(changes)s" + +#, python-format +msgid "Deleted \"%(object)s.\"" +msgstr "Kufutwa kwa \"%(object)s\"." + +msgid "LogEntry Object" +msgstr "Kitu cha Ingizo la Kumbukumbu" + +#, python-brace-format +msgid "Added {name} \"{object}\"." +msgstr "Kumeongezeka {name} \"{object}\"." + +msgid "Added." +msgstr "Imeongezwa" + +msgid "and" +msgstr "na" + +#, python-brace-format +msgid "Changed {fields} for {name} \"{object}\"." +msgstr "Mabadiliko ya {fields} yamefanyika katika {name} \"{object}\"." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "Mabadiliko yamefanyika katika {fields} " + +#, python-brace-format +msgid "Deleted {name} \"{object}\"." +msgstr "Futa {name} \"{object}\"." + +msgid "No fields changed." +msgstr "Hakuna uga uliobadilishwa." + +msgid "None" +msgstr "Hakuna" + +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may edit it again below." +msgstr "" +"Ingizo la {name} \"{obj}\" limefanyika kwa mafanikio. Unaweza kuhariri tena" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was added successfully." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." +msgstr "" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Nilazima kuchagua vitu ili kufanyia kitu fulani. Hakuna kitu " +"kilichochaguliwa." + +msgid "No action selected." +msgstr "Hakuna tendo lililochaguliwa" + +#, python-format +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "Ufutaji wa \"%(obj)s\" %(name)s umefanikiwa." + +#, python-format +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" +msgstr "" + +#, python-format +msgid "Add %s" +msgstr "Ongeza %s" + +#, python-format +msgid "Change %s" +msgstr "Badilisha %s" + +msgid "Database error" +msgstr "Hitilafu katika hifadhidata" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "mabadiliko ya %(name)s %(count)s yamefanikiwa." +msgstr[1] "mabadiliko ya %(name)s %(count)s yamefanikiwa." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s kuchaguliwa" +msgstr[1] "%(total_count)s (kila kitu) kuchaguliwa" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "Vilivyo chaguliwa ni 0 kati ya %(cnt)s" + +#, python-format +msgid "Change history: %s" +msgstr "Badilisha historia: %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(instance)s %(class_name)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Kufutwa kwa ingizo la %(instance)s %(class_name)s kutahitaji kufutwa kwa " +"vitu vifuatavyo vyenye mahusiano vilivyokingwa: %(related_objects)s" + +msgid "Django site admin" +msgstr "Utawala wa tovuti ya django" + +msgid "Django administration" +msgstr "Utawala wa Django" + +msgid "Site administration" +msgstr "Utawala wa tovuti" + +msgid "Log in" +msgstr "Ingia" + +#, python-format +msgid "%(app)s administration" +msgstr "Utawala wa %(app)s" + +msgid "Page not found" +msgstr "Ukurasa haujapatikana" + +msgid "We're sorry, but the requested page could not be found." +msgstr "Samahani, ukurasa uliohitajika haukupatikana." + +msgid "Home" +msgstr "Sebule" + +msgid "Server error" +msgstr "Hitilafu ya seva" + +msgid "Server error (500)" +msgstr "Hitilafu ya seva (500)" + +msgid "Server Error (500)" +msgstr "Hitilafu ya seva (500)" + +msgid "" +"There's been an error. It's been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Kumekuwa na hitilafu. Imeripotiwa kwa watawala kupitia barua pepe na " +"inatakiwa kurekebishwa mapema." + +msgid "Run the selected action" +msgstr "Fanya tendo lililochaguliwa." + +msgid "Go" +msgstr "Nenda" + +msgid "Click here to select the objects across all pages" +msgstr "Bofya hapa kuchagua viumbile katika kurasa zote" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Chagua kila %(module_name)s, (%(total_count)s). " + +msgid "Clear selection" +msgstr "Safisha chaguo" + +msgid "" +"First, enter a username and password. Then, you'll be able to edit more user " +"options." +msgstr "" +"Kwanza, ingiza jina lamtumiaji na nywila. Kisha, utaweza kuhariri zaidi " +"machaguo ya mtumiaji." + +msgid "Enter a username and password." +msgstr "Ingiza jina la mtumiaji na nywila." + +msgid "Change password" +msgstr "Badilisha nywila" + +msgid "Please correct the error below." +msgstr "Tafadhali sahihisha makosa yafuatayo " + +msgid "Please correct the errors below." +msgstr "Tafadhali sahihisha makosa yafuatayo." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "ingiza nywila ya mtumiaji %(username)s." + +msgid "Welcome," +msgstr "Karibu" + +msgid "View site" +msgstr "Tazama tovuti" + +msgid "Documentation" +msgstr "Nyaraka" + +msgid "Log out" +msgstr "Toka" + +#, python-format +msgid "Add %(name)s" +msgstr "Ongeza %(name)s" + +msgid "History" +msgstr "Historia" + +msgid "View on site" +msgstr "Ona kwenye tovuti" + +msgid "Filter" +msgstr "Chuja" + +msgid "Remove from sorting" +msgstr "Ondoa katika upangaji" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Kipaumbele katika mpangilio: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Geuza mpangilio" + +msgid "Delete" +msgstr "Futa" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Kufutwa kwa '%(escaped_object)s' %(object_name)s kutasababisha kufutwa kwa " +"vitu vinavyohuisana, lakini akaunti yako haina ruhusa ya kufuta vitu vya " +"aina zifuatazo:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Kufuta '%(escaped_object)s' %(object_name)s kutahitaji kufuta vitu " +"vifuatavyo ambavyo vinavyohuisana na vimelindwa:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Una uhakika kuwa unataka kufuta \"%(escaped_object)s\" %(object_name)s ? " +"Vitu vyote vinavyohuisana kati ya vifuatavyo vitafutwa:" + +msgid "Objects" +msgstr "Viumbile" + +msgid "Yes, I'm sure" +msgstr "Ndiyo, Nina uhakika" + +msgid "No, take me back" +msgstr "Hapana, nirudishe" + +msgid "Delete multiple objects" +msgstr "Futa viumbile mbalimbali" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Kufutwa kwa %(objects_name)s chaguliwa kutasababisha kufutwa kwa " +"vituvinavyohusiana, lakini akaunti yako haina ruhusa ya kufuta vitu vya " +"vifuatavyo:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Kufutwa kwa %(objects_name)s kutahitaji kufutwa kwa vitu vifuatavyo vyenye " +"uhusiano na vilivyolindwa:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Una uhakika kuwa unataka kufuta %(objects_name)s chaguliwa ? Vitu vyote kati " +"ya vifuatavyo vinavyohusiana vitafutwa:" + +msgid "Change" +msgstr "Badilisha" + +msgid "Delete?" +msgstr "Futa?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " Kwa %(filter_title)s" + +msgid "Summary" +msgstr "Muhtasari" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Models katika application %(name)s" + +msgid "Add" +msgstr "Ongeza" + +msgid "You don't have permission to edit anything." +msgstr "Huna ruhusa ya kuhariri chochote" + +msgid "Recent actions" +msgstr "Matendo ya karibuni" + +msgid "My actions" +msgstr "Matendo yangu" + +msgid "None available" +msgstr "Hakuna kilichopatikana" + +msgid "Unknown content" +msgstr "Maudhui hayajulikani" + +msgid "" +"Something's wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Kuna tatizo limetokea katika usanikishaji wako wa hifadhidata. Hakikisha " +"kuwa majedwali sahihi ya hifadhidata yameundwa, na hakikisha hifadhidata " +"inaweza kusomwana mtumiaji sahihi." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" + +msgid "Forgotten your password or username?" +msgstr "Umesahau jina na nenosiri lako?" + +msgid "Date/time" +msgstr "Tarehe/saa" + +msgid "User" +msgstr "Mtumiaji" + +msgid "Action" +msgstr "Tendo" + +msgid "" +"This object doesn't have a change history. It probably wasn't added via this " +"admin site." +msgstr "" +"Kiumbile hiki hakina historia ya kubadilika. Inawezekana hakikuwekwa kupitia " +"hii tovuti ya utawala." + +msgid "Show all" +msgstr "Onesha yotee" + +msgid "Save" +msgstr "Hifadhi" + +msgid "Popup closing..." +msgstr "Udukizi unafunga" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Badili %(model)s husika" + +#, python-format +msgid "Add another %(model)s" +msgstr "Ongeza %(model)s tena" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Futa %(model)s husika" + +msgid "Search" +msgstr "Tafuta" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "tokeo %(counter)s" +msgstr[1] "matokeo %(counter)s" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "jumla %(full_result_count)s" + +msgid "Save as new" +msgstr "Hifadhi kama mpya" + +msgid "Save and add another" +msgstr "Hifadhi na ongeza" + +msgid "Save and continue editing" +msgstr "Hifadhi na endelea kuhariri" + +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Ahsante kwa kutumia muda wako katika Tovuti yetu leo. " + +msgid "Log in again" +msgstr "ingia tena" + +msgid "Password change" +msgstr "Badilisha nywila" + +msgid "Your password was changed." +msgstr "Nywila yako imebadilishwa" + +msgid "" +"Please enter your old password, for security's sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Tafadhali ingiza nywila yako ya zamani, kwa ajili ya usalama, kisha ingiza " +"nywila mpya mara mbili ili tuweze kuthibitisha kuwa umelichapisha kwa " +"usahihi." + +msgid "Change my password" +msgstr "Badilisha nywila yangu" + +msgid "Password reset" +msgstr "Kuseti nywila upya" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Nywila yako imesetiwa. Unaweza kuendelea na kuingia sasa." + +msgid "Password reset confirmation" +msgstr "Uthibitisho wa kuseti nywila upya" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Tafadhali ingiza nywila mpya mara mbili ili tuweze kuthibitisha kuwa " +"umelichapisha kwa usahihi." + +msgid "New password:" +msgstr "Nywila mpya:" + +msgid "Confirm password:" +msgstr "Thibitisha nywila" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Kiungo cha kuseti nywila upya ni batili, inawezekana ni kwa sababu kiungo " +"hicho tayari kimetumika. tafadhali omba upya kuseti nywila." + +msgid "" +"We've emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" + +msgid "" +"If you don't receive an email, please make sure you've entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Ikiwa hujapata barua pepe, tafadhali hakikisha umeingiza anuani ya barua " +"pepe uliyoitumia kujisajili na angalia katika folda la spam" + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Umepata barua pepe hii kwa sababu ulihitaji ku seti upya nywila ya akaunti " +"yako ya %(site_name)s." + +msgid "Please go to the following page and choose a new password:" +msgstr "Tafadhali nenda ukurasa ufuatao na uchague nywila mpya:" + +msgid "Your username, in case you've forgotten:" +msgstr "Jina lako la mtumiaji, ikiwa umesahau:" + +msgid "Thanks for using our site!" +msgstr "Ahsante kwa kutumia tovui yetu!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "timu ya %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we'll email " +"instructions for setting a new one." +msgstr "" +"Umesahau nywila yako? Ingiza anuani yako ya barua pepe hapo chini, nasi " +"tutakutumia maelekezo ya kuseti nenosiri jipya. " + +msgid "Email address:" +msgstr "Anuani ya barua pepe:" + +msgid "Reset my password" +msgstr "Seti nywila yangu upya" + +msgid "All dates" +msgstr "Tarehe zote" + +#, python-format +msgid "Select %s" +msgstr "Chagua %s" + +#, python-format +msgid "Select %s to change" +msgstr "Chaguo %s kwa mabadilisho" + +msgid "Date:" +msgstr "Tarehe" + +msgid "Time:" +msgstr "Saa" + +msgid "Lookup" +msgstr "`Lookup`" + +msgid "Currently:" +msgstr "Kwa sasa:" + +msgid "Change:" +msgstr "Badilisha:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ta/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ta/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..ce41a7a5 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ta/LC_MESSAGES/django 3.po @@ -0,0 +1,643 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Jannis Leidel , 2011 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel \n" +"Language-Team: Tamil (http://www.transifex.com/django/django/language/ta/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ta\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "" + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "" + +msgid "Are you sure?" +msgstr "உறுதியாக சொல்கிறீர்களா?" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "" + +msgid "Administration" +msgstr "" + +msgid "All" +msgstr "அனைத்தும்" + +msgid "Yes" +msgstr "ஆம்" + +msgid "No" +msgstr "இல்லை" + +msgid "Unknown" +msgstr "தெரியாத" + +msgid "Any date" +msgstr "எந்த தேதியும்" + +msgid "Today" +msgstr "இன்று" + +msgid "Past 7 days" +msgstr "கடந்த 7 நாட்களில்" + +msgid "This month" +msgstr "இந்த மாதம்" + +msgid "This year" +msgstr "இந்த வருடம்" + +msgid "No date" +msgstr "" + +msgid "Has date" +msgstr "" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" + +msgid "Action:" +msgstr "" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "" + +msgid "Remove" +msgstr "அழிக்க" + +msgid "action time" +msgstr "செயல் நேரம்" + +msgid "user" +msgstr "" + +msgid "content type" +msgstr "" + +msgid "object id" +msgstr "பொருள் அடையாளம்" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/3/library/functions.html#repr) +msgid "object repr" +msgstr "பொருள் உருவகித்தம்" + +msgid "action flag" +msgstr "செயர்குறி" + +msgid "change message" +msgstr "செய்தியை மாற்று" + +msgid "log entry" +msgstr "புகுபதிவு உள்ளீடு" + +msgid "log entries" +msgstr "புகுபதிவு உள்ளீடுகள்" + +#, python-format +msgid "Added \"%(object)s\"." +msgstr "" + +#, python-format +msgid "Changed \"%(object)s\" - %(changes)s" +msgstr "" + +#, python-format +msgid "Deleted \"%(object)s.\"" +msgstr "" + +msgid "LogEntry Object" +msgstr "" + +#, python-brace-format +msgid "Added {name} \"{object}\"." +msgstr "" + +msgid "Added." +msgstr "" + +msgid "and" +msgstr "மற்றும்" + +#, python-brace-format +msgid "Changed {fields} for {name} \"{object}\"." +msgstr "" + +#, python-brace-format +msgid "Changed {fields}." +msgstr "" + +#, python-brace-format +msgid "Deleted {name} \"{object}\"." +msgstr "" + +msgid "No fields changed." +msgstr "எந்த புலமும் மாறவில்லை." + +msgid "None" +msgstr "" + +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was added successfully." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." +msgstr "" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" + +msgid "No action selected." +msgstr "" + +#, python-format +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "%(name)s \"%(obj)s\" வெற்றிகரமாக அழிக்கப்பட்டுள்ளது." + +#, python-format +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" +msgstr "" + +#, python-format +msgid "Add %s" +msgstr "%s யை சேர்க்க" + +#, python-format +msgid "Change %s" +msgstr "%s யை மாற்று" + +msgid "Database error" +msgstr "தகவல்சேமிப்பு பிழை" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "" + +#, python-format +msgid "Change history: %s" +msgstr "வரலாற்றை மாற்று: %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" + +msgid "Django site admin" +msgstr "டிஜாங்ஙோ தள நிர்வாகி" + +msgid "Django administration" +msgstr "டிஜாங்ஙோ நிர்வாகம் " + +msgid "Site administration" +msgstr "இணைய மேலான்மை" + +msgid "Log in" +msgstr "உள்ளே போ" + +#, python-format +msgid "%(app)s administration" +msgstr "" + +msgid "Page not found" +msgstr "பக்கத்தைக் காணவில்லை" + +msgid "We're sorry, but the requested page could not be found." +msgstr "நீங்கள் விரும்பிய பக்கத்தை காண இயலவில்லை,அதற்காக நாங்கள் வருந்துகிறோம்." + +msgid "Home" +msgstr "வீடு" + +msgid "Server error" +msgstr "சேவகன் பிழை" + +msgid "Server error (500)" +msgstr "சேவையகம் தவறு(500)" + +msgid "Server Error (500)" +msgstr "சேவையகம் பிழை(500)" + +msgid "" +"There's been an error. It's been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" + +msgid "Run the selected action" +msgstr "" + +msgid "Go" +msgstr "செல்" + +msgid "Click here to select the objects across all pages" +msgstr "" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "" + +msgid "Clear selection" +msgstr "" + +msgid "" +"First, enter a username and password. Then, you'll be able to edit more user " +"options." +msgstr "" +"முதலில்,பயனர்ப்பெயர் மற்றும் கடவுச்சொல்லை உள்ளிடவும்.அதன் பிறகு தான் நீங்கள் உங்கள் பெயரின் " +"விவரங்களை திருத்த முடியும்" + +msgid "Enter a username and password." +msgstr "" + +msgid "Change password" +msgstr "கடவுச்சொல்லை மாற்று" + +msgid "Please correct the error below." +msgstr "கீழே உள்ள தவறுகளைத் திருத்துக" + +msgid "Please correct the errors below." +msgstr "" + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "" + +msgid "Welcome," +msgstr "நல்வரவு," + +msgid "View site" +msgstr "" + +msgid "Documentation" +msgstr "ஆவனமாக்கம்" + +msgid "Log out" +msgstr "வெளியேறு" + +#, python-format +msgid "Add %(name)s" +msgstr "%(name)s சேர்க்க" + +msgid "History" +msgstr "வரலாறு" + +msgid "View on site" +msgstr "தளத்தில் பார்" + +msgid "Filter" +msgstr "வடிகட்டி" + +msgid "Remove from sorting" +msgstr "" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "" + +msgid "Toggle sorting" +msgstr "" + +msgid "Delete" +msgstr "நீக்குக" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"நீக்கும் '%(escaped_object)s' ஆனது %(object_name)s தொடர்புடைய மற்றவற்றையும் நீக்கும். " +"ஆனால் அதை நீக்குவதற்குரிய உரிமை உங்களுக்கு இல்லை" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"நீங்கள் இந்த \"%(escaped_object)s\" %(object_name)s நீக்குவதில் நிச்சயமா?தொடர்புடைய " +"மற்றவையும் நீக்கப்படும். " + +msgid "Objects" +msgstr "" + +msgid "Yes, I'm sure" +msgstr "ஆம், எனக்கு உறுதி" + +msgid "No, take me back" +msgstr "" + +msgid "Delete multiple objects" +msgstr "" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" + +msgid "Change" +msgstr "மாற்றுக" + +msgid "Delete?" +msgstr "" + +#, python-format +msgid " By %(filter_title)s " +msgstr "%(filter_title)s ஆல்" + +msgid "Summary" +msgstr "" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "" + +msgid "Add" +msgstr "சேர்க்க" + +msgid "You don't have permission to edit anything." +msgstr "உங்களுக்கு மாற்றுவதற்குரிய உரிமையில்லை" + +msgid "Recent actions" +msgstr "" + +msgid "My actions" +msgstr "" + +msgid "None available" +msgstr "எதுவும் கிடைக்கவில்லை" + +msgid "Unknown content" +msgstr "" + +msgid "" +"Something's wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"உங்களுடைய தகவல்சேமிப்பகத்தை நிறுவுவதில் சில தவறுகள் உள்ளது. அதற்கு இணையான " +"தகவல்சேமிப்பு அட்டவணையைதயாரிக்கவும். மேலும் பயனர் படிக்கும் படியான தகவல்சேமிப்பகத்தை " +"உருவாக்கவும்." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" + +msgid "Forgotten your password or username?" +msgstr "" + +msgid "Date/time" +msgstr "தேதி/நேரம் " + +msgid "User" +msgstr "பயனர்" + +msgid "Action" +msgstr "செயல்" + +msgid "" +"This object doesn't have a change history. It probably wasn't added via this " +"admin site." +msgstr "" +"இந்த பொருள் மாற்று வரலாற்றில் இல்லைஒரு வேளை நிர்வாகத்தளத்தின் மூலம் சேர்க்கப்படாமலிருக்கலாம்" + +msgid "Show all" +msgstr "எல்லாவற்றையும் காட்டு" + +msgid "Save" +msgstr "சேமிக்க" + +msgid "Popup closing..." +msgstr "" + +#, python-format +msgid "Change selected %(model)s" +msgstr "" + +#, python-format +msgid "Add another %(model)s" +msgstr "" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "" + +msgid "Search" +msgstr "" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s மொத்தம்" + +msgid "Save as new" +msgstr "புதியதாக சேமி" + +msgid "Save and add another" +msgstr "சேமித்து இன்னுமொன்றைச் சேர்" + +msgid "Save and continue editing" +msgstr "சேமித்து மாற்றத்தை தொடருக" + +msgid "Thanks for spending some quality time with the Web site today." +msgstr "வலைத்தளத்தில் உங்களது பொன்னான நேரத்தை செலவழித்தமைக்கு மிகுந்த நன்றி" + +msgid "Log in again" +msgstr "மீண்டும் உள்ளே பதிவு செய்யவும்" + +msgid "Password change" +msgstr "கடவுச்சொல் மாற்று" + +msgid "Your password was changed." +msgstr "உங்களுடைய கடவுச்சொல் மாற்றபட்டது" + +msgid "" +"Please enter your old password, for security's sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"பாதுகாப்பு காரணங்களுக்காக , முதலில் உங்களது பழைய கடவுச்சொல்லை உள்ளிடுக. அதன் பிறகு " +"புதிய கடவுச்சொல்லை இரு முறை உள்ளிடுக. இது உங்களது உள்ளிடுதலை சரிபார்க்க உதவும். " + +msgid "Change my password" +msgstr "கடவுச் சொல்லை மாற்றவும்" + +msgid "Password reset" +msgstr "கடவுச்சொல்லை மாற்றியமை" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "" + +msgid "Password reset confirmation" +msgstr "" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" + +msgid "New password:" +msgstr "புதிய கடவுச்சொல்:" + +msgid "Confirm password:" +msgstr "கடவுச்சொலின் மாற்றத்தை உறுதிப்படுத்து:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" + +msgid "" +"We've emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" + +msgid "" +"If you don't receive an email, please make sure you've entered the address " +"you registered with, and check your spam folder." +msgstr "" + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" + +msgid "Please go to the following page and choose a new password:" +msgstr "" + +msgid "Your username, in case you've forgotten:" +msgstr "உங்களது பயனாளர் பெயர், நீங்கள் மறந்திருந்தால்:" + +msgid "Thanks for using our site!" +msgstr "எங்களது வலைத்தளத்தை பயன் படுத்தியதற்கு மிகுந்த நன்றி" + +#, python-format +msgid "The %(site_name)s team" +msgstr "இந்த %(site_name)s -இன் குழு" + +msgid "" +"Forgotten your password? Enter your email address below, and we'll email " +"instructions for setting a new one." +msgstr "" + +msgid "Email address:" +msgstr "" + +msgid "Reset my password" +msgstr "எனது கடவுச்சொல்லை மாற்றியமை" + +msgid "All dates" +msgstr "அனைத்து தேதியும்" + +#, python-format +msgid "Select %s" +msgstr "%s யை தேர்ந்தெடு" + +#, python-format +msgid "Select %s to change" +msgstr "%s யை மாற்ற தேர்ந்தெடு" + +msgid "Date:" +msgstr "தேதி:" + +msgid "Time:" +msgstr "நேரம்:" + +msgid "Lookup" +msgstr "" + +msgid "Currently:" +msgstr "" + +msgid "Change:" +msgstr "" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/te/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/te/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..00a9e205 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/te/LC_MESSAGES/django 3.po @@ -0,0 +1,640 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# bhaskar teja yerneni , 2011 +# Jannis Leidel , 2011 +# ప్రవీణ్ ఇళ్ళ , 2011,2013 +# వీవెన్ , 2011 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel \n" +"Language-Team: Telugu (http://www.transifex.com/django/django/language/te/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: te\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d %(items)s జయప్రదముగా తీసేవేయబడినది." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "%(name)s తొలగించుట వీలుకాదు" + +msgid "Are you sure?" +msgstr "మీరు ఖచ్చితంగా ఇలా చేయాలనుకుంటున్నారా?" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "ఎంచుకోన్న %(verbose_name_plural)s తీసివేయుము " + +msgid "Administration" +msgstr "" + +msgid "All" +msgstr "అన్నీ" + +msgid "Yes" +msgstr "అవును" + +msgid "No" +msgstr "కాదు" + +msgid "Unknown" +msgstr "తెలియనది" + +msgid "Any date" +msgstr "ఏ రోజైన" + +msgid "Today" +msgstr "ఈ రోజు" + +msgid "Past 7 days" +msgstr "గత 7 రోజుల గా" + +msgid "This month" +msgstr "ఈ నెల" + +msgid "This year" +msgstr "ఈ సంవత్సరం" + +msgid "No date" +msgstr "" + +msgid "Has date" +msgstr "" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" + +msgid "Action:" +msgstr "చర్య:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "" + +msgid "Remove" +msgstr "తొలగించు" + +msgid "action time" +msgstr "పని సమయము " + +msgid "user" +msgstr "" + +msgid "content type" +msgstr "" + +msgid "object id" +msgstr "వస్తువు" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/3/library/functions.html#repr) +msgid "object repr" +msgstr "వస్తువు" + +msgid "action flag" +msgstr "పని ఫ్లాగ్" + +msgid "change message" +msgstr "సందేశము ని మార్చంది" + +msgid "log entry" +msgstr "లాగ్ ఎంట్రీ" + +msgid "log entries" +msgstr "లాగ్ ఎంట్రీలు" + +#, python-format +msgid "Added \"%(object)s\"." +msgstr "" + +#, python-format +msgid "Changed \"%(object)s\" - %(changes)s" +msgstr "" + +#, python-format +msgid "Deleted \"%(object)s.\"" +msgstr "" + +msgid "LogEntry Object" +msgstr "" + +#, python-brace-format +msgid "Added {name} \"{object}\"." +msgstr "" + +msgid "Added." +msgstr "" + +msgid "and" +msgstr "మరియు" + +#, python-brace-format +msgid "Changed {fields} for {name} \"{object}\"." +msgstr "" + +#, python-brace-format +msgid "Changed {fields}." +msgstr "" + +#, python-brace-format +msgid "Deleted {name} \"{object}\"." +msgstr "" + +msgid "No fields changed." +msgstr "క్షేత్రములు ఏమి మార్చబడలేదు" + +msgid "None" +msgstr "వొకటీ లేదు" + +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was added successfully." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." +msgstr "" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"అంశములపయి తదుపరి చర్య తీసుకోనటకు వాటిని ఎంపిక చేసుకోవలెను. ప్రస్తుతం ఎటువంటి అంశములు " +"మార్చబడలేదు." + +msgid "No action selected." +msgstr "మీరు ఎటువంటి చర్య తీసుకొనలేదు " + +#, python-format +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "%(name)s \"%(obj)s\" జయప్రదంగా తీసివేయబడ్డడి" + +#, python-format +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" +msgstr "" + +#, python-format +msgid "Add %s" +msgstr "%sని జత చేయండి " + +#, python-format +msgid "Change %s" +msgstr "%sని మార్చుము" + +msgid "Database error" +msgstr "దత్తాంశస్థానము పొరబాటు " + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s జయప్రదముగా మార్చబడినవి." +msgstr[1] "%(count)s %(name)s జయప్రదముగా మార్చబడినవి." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s ఎంపికయినది." +msgstr[1] "అన్ని %(total_count)s ఎంపికయినవి." + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 of %(cnt)s ఎంపికయినవి." + +#, python-format +msgid "Change history: %s" +msgstr "చరిత్రం మార్చు: %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" + +msgid "Django site admin" +msgstr "జాంగొ యొక్క నిర్వాహణదారులు" + +msgid "Django administration" +msgstr "జాంగొ నిర్వాహణ" + +msgid "Site administration" +msgstr "సైట్ నిర్వాహణ" + +msgid "Log in" +msgstr "ప్రవేశించండి" + +#, python-format +msgid "%(app)s administration" +msgstr "" + +msgid "Page not found" +msgstr "పుట దొరకలేదు" + +msgid "We're sorry, but the requested page could not be found." +msgstr "క్షమించండి మీరు కోరిన పుట దొరకలేడు" + +msgid "Home" +msgstr "నివాసము" + +msgid "Server error" +msgstr "సర్వర్ పొరబాటు" + +msgid "Server error (500)" +msgstr "సర్వర్ పొరబాటు (500)" + +msgid "Server Error (500)" +msgstr "సర్వర్ పొరబాటు (500)" + +msgid "" +"There's been an error. It's been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" + +msgid "Run the selected action" +msgstr "ఎంచుకున్న చర్యను నడుపు" + +msgid "Go" +msgstr "వెళ్లు" + +msgid "Click here to select the objects across all pages" +msgstr "" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "" + +msgid "Clear selection" +msgstr "ఎంపికను తుడిచివేయి" + +msgid "" +"First, enter a username and password. Then, you'll be able to edit more user " +"options." +msgstr "" + +msgid "Enter a username and password." +msgstr "ఒక వాడుకరిపేరు మరియు సంకేతపదాన్ని ప్రవేశపెట్టండి." + +msgid "Change password" +msgstr "సంకేతపదాన్ని మార్చుకోండి" + +msgid "Please correct the error below." +msgstr "క్రింద ఉన్న తప్పులు సరిదిద్దుకోండి" + +msgid "Please correct the errors below." +msgstr "" + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "" + +msgid "Welcome," +msgstr "సుస్వాగతం" + +msgid "View site" +msgstr "" + +msgid "Documentation" +msgstr "పత్రీకరణ" + +msgid "Log out" +msgstr "నిష్క్రమించండి" + +#, python-format +msgid "Add %(name)s" +msgstr "%(name)s జత చేయు" + +msgid "History" +msgstr "చరిత్ర" + +msgid "View on site" +msgstr "సైట్ లో చూడండి" + +msgid "Filter" +msgstr "వడపోత" + +msgid "Remove from sorting" +msgstr "క్రమీకరణ నుండి తొలగించు" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "" + +msgid "Toggle sorting" +msgstr "" + +msgid "Delete" +msgstr "తొలగించు" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" + +msgid "Objects" +msgstr "" + +msgid "Yes, I'm sure" +msgstr "అవును " + +msgid "No, take me back" +msgstr "" + +msgid "Delete multiple objects" +msgstr "" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" + +msgid "Change" +msgstr "మార్చు" + +msgid "Delete?" +msgstr "తొలగించాలా?" + +#, python-format +msgid " By %(filter_title)s " +msgstr "" + +msgid "Summary" +msgstr "" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "" + +msgid "Add" +msgstr "చేర్చు" + +msgid "You don't have permission to edit anything." +msgstr "మీకు ఏది మార్చటానికి అధికారము లేదు" + +msgid "Recent actions" +msgstr "" + +msgid "My actions" +msgstr "" + +msgid "None available" +msgstr "ఏమి దొరకలేదు" + +msgid "Unknown content" +msgstr "తెలియని విషయం" + +msgid "" +"Something's wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" + +msgid "Forgotten your password or username?" +msgstr "మీ సంకేతపదం లేదా వాడుకరిపేరును మర్చిపోయారా?" + +msgid "Date/time" +msgstr "తేదీ/సమయం" + +msgid "User" +msgstr "వాడుకరి" + +msgid "Action" +msgstr "చర్య" + +msgid "" +"This object doesn't have a change history. It probably wasn't added via this " +"admin site." +msgstr "" + +msgid "Show all" +msgstr "అన్నీ చూపించు" + +msgid "Save" +msgstr "భద్రపరుచు" + +msgid "Popup closing..." +msgstr "" + +#, python-format +msgid "Change selected %(model)s" +msgstr "" + +#, python-format +msgid "Add another %(model)s" +msgstr "" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "" + +msgid "Search" +msgstr "వెతుకు" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s ఫలితం" +msgstr[1] "%(counter)s ఫలితాలు" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s మొత్తము" + +msgid "Save as new" +msgstr "కొత్త దాని లా దాచు" + +msgid "Save and add another" +msgstr "దాచి కొత్త దానిని కలపండి" + +msgid "Save and continue editing" +msgstr "దాచి మార్చుటా ఉందండి" + +msgid "Thanks for spending some quality time with the Web site today." +msgstr "" + +msgid "Log in again" +msgstr "మళ్ళీ ప్రవేశించండి" + +msgid "Password change" +msgstr "అనుమతి పదం మార్పు" + +msgid "Your password was changed." +msgstr "మీ అనుమతి పదం మార్చబడిండి" + +msgid "" +"Please enter your old password, for security's sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"దయచేసి రక్షన కోసము, మీ పాత అనుమతి పదం ఇవ్వండి , కొత్త అనుమతి పదం రెండు సార్లు ఇవ్వండి , " +"ఎం దుకంటే మీరు తప్పు ఇస్తే సరిచేయటానికి " + +msgid "Change my password" +msgstr "నా సంకేతపదాన్ని మార్చు" + +msgid "Password reset" +msgstr "అనుమతి పదం తిరిగి అమర్చు" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "మీ అనుమతి పదం మర్చుబడినది. మీరు ఇప్పుదు లాగ్ ఇన్ అవ్వచ్చు." + +msgid "Password reset confirmation" +msgstr "అనుమతి పదం తిరిగి మార్చు ఖాయం చెయండి" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"దయచేసి రక్షన కోసము, మీ పాత అనుమతి పదం ఇవ్వండి , కొత్త అనుమతి పదం రెండు సార్లు ఇవ్వండి , " +"ఎం దుకంటే మీరు తప్పు ఇస్తే సరిచేయటానికి " + +msgid "New password:" +msgstr "కొత్త సంకేతపదం:" + +msgid "Confirm password:" +msgstr "సంకేతపదాన్ని నిర్ధారించండి:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" + +msgid "" +"We've emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" + +msgid "" +"If you don't receive an email, please make sure you've entered the address " +"you registered with, and check your spam folder." +msgstr "" + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" + +msgid "Please go to the following page and choose a new password:" +msgstr "" + +msgid "Your username, in case you've forgotten:" +msgstr "మీ వాడుకరిపేరు, ఒక వేళ మీరు మర్చిపోయివుంటే:" + +msgid "Thanks for using our site!" +msgstr "మా సైటుని ఉపయోగిస్తున్నందుకు ధన్యవాదములు!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "%(site_name)s జట్టు" + +msgid "" +"Forgotten your password? Enter your email address below, and we'll email " +"instructions for setting a new one." +msgstr "" + +msgid "Email address:" +msgstr "ఈమెయిలు చిరునామా:" + +msgid "Reset my password" +msgstr "అనుమతిపదం తిరిగి అమర్చు" + +msgid "All dates" +msgstr "అన్నీ తేదీలు" + +#, python-format +msgid "Select %s" +msgstr "%s ని ఎన్నుకోండి" + +#, python-format +msgid "Select %s to change" +msgstr "%s ని మార్చటానికి ఎన్నుకోండి" + +msgid "Date:" +msgstr "తారీఖు:" + +msgid "Time:" +msgstr "సమయం:" + +msgid "Lookup" +msgstr "అంశ శోధన." + +msgid "Currently:" +msgstr "ప్రస్తుతం" + +msgid "Change:" +msgstr "మార్చు:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/tg/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/tg/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..e786454e --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/tg/LC_MESSAGES/django 3.po @@ -0,0 +1,699 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Mariusz Felisiak , 2020 +# Surush Sufiew , 2020 +# Surush Sufiew , 2020 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-07-14 19:53+0200\n" +"PO-Revision-Date: 2020-07-30 18:53+0000\n" +"Last-Translator: Mariusz Felisiak \n" +"Language-Team: Tajik (http://www.transifex.com/django/django/language/tg/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: tg\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Муваффақона нест сохтед %(count)d %(items)s." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Нест карда нашуд %(name)s" + +msgid "Are you sure?" +msgstr "Шумо рози ҳастед ?" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Нест сохтани интихобшудаҳо %(verbose_name_plural)s" + +msgid "Administration" +msgstr "Маъмурият" + +msgid "All" +msgstr "Ҳама" + +msgid "Yes" +msgstr "Ҳа" + +msgid "No" +msgstr "Не" + +msgid "Unknown" +msgstr "Номуайян" + +msgid "Any date" +msgstr "Санаи бефарқ" + +msgid "Today" +msgstr "Имрӯз" + +msgid "Past 7 days" +msgstr "7 рӯзи охир" + +msgid "This month" +msgstr "Моҳи ҷорӣ" + +msgid "This year" +msgstr "Соли ҷорӣ" + +msgid "No date" +msgstr "Сана ишора нашудааст" + +msgid "Has date" +msgstr "Сана ишора шудааст" + +msgid "Empty" +msgstr "Холӣ" + +msgid "Not empty" +msgstr "Холӣ нест" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Хоҳиш менамоем %(username)s ва рамзро дуруст ворид созед. Ҳарду майдон " +"метавонанд духура бошанд." + +msgid "Action:" +msgstr "Амал:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Боз якто %(verbose_name)s илова кардан" + +msgid "Remove" +msgstr "Нест кардан" + +msgid "Addition" +msgstr "Иловакунӣ" + +msgid "Change" +msgstr "Тағйир додан" + +msgid "Deletion" +msgstr "Несткунӣ" + +msgid "action time" +msgstr "вақти амал" + +msgid "user" +msgstr "истифодабаранда" + +msgid "content type" +msgstr "намуди контент" + +msgid "object id" +msgstr "идентификатори объект" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "намоиши объект" + +msgid "action flag" +msgstr "намуди амал" + +msgid "change message" +msgstr "хабар оиди тағйирот" + +msgid "log entry" +msgstr "қайд дар дафтар" + +msgid "log entries" +msgstr "қайдҳо дар дафтар" + +#, python-format +msgid "Added “%(object)s”." +msgstr "Илова шуд \"%(object)s\"" + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "" + +msgid "LogEntry Object" +msgstr "Қайд дар дафтар" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "" + +msgid "Added." +msgstr "Илова шуд." + +msgid "and" +msgstr "ва" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "" + +#, python-brace-format +msgid "Changed {fields}." +msgstr "Тағйир ёфт {fields}." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "" + +msgid "No fields changed." +msgstr "Ягон майдон тағйир наёфт." + +msgid "None" +msgstr "Не" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "" + +msgid "You may edit it again below." +msgstr "Шумо метавонед ин объектро дар поён аз нав тағйир диҳед." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Барои иҷрои амал лозим аст, ки объектро интихоб намоед. Тағйирот барои " +"объектҳо ворид нашуданд " + +msgid "No action selected." +msgstr "Ҳеҷ амал инихоб нашудааст." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "" + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "" + +#, python-format +msgid "Add %s" +msgstr "Илова кардан %s" + +#, python-format +msgid "Change %s" +msgstr "Тағйир додан %s" + +#, python-format +msgid "View %s" +msgstr "Азназаргузаронӣ %s" + +msgid "Database error" +msgstr "Мушкилӣ дар базаи додаҳо" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "Интихоб карда шуд 0 аз %(cnt)s " + +#, python-format +msgid "Change history: %s" +msgstr "Таърихи вориди тағйирот: %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Несткунии объекти %(instance)s намуди %(class_name)s талаб мекунад, ки " +"объектҳои алоқамандшудаизерин низ нест карда шаванд: %(related_objects)s" + +msgid "Django site admin" +msgstr "Сомонаи маъмурии Django" + +msgid "Django administration" +msgstr "Маъмурияти Django" + +msgid "Site administration" +msgstr "Маъмурияти сомона" + +msgid "Log in" +msgstr "Ворид шудан" + +#, python-format +msgid "%(app)s administration" +msgstr "Маъмурияти барномаи «%(app)s»" + +msgid "Page not found" +msgstr "Саҳифа ёфт нашуд" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "" + +msgid "Home" +msgstr "Асосӣ" + +msgid "Server error" +msgstr "Мушкилӣ дар сервер" + +msgid "Server error (500)" +msgstr "Мушкилӣ дар сервер (500)" + +msgid "Server Error (500)" +msgstr "Мушкилӣ дар сервер (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" + +msgid "Run the selected action" +msgstr "Иҷрои амалҳои ихтихобшуда" + +msgid "Go" +msgstr "Иҷро кардан" + +msgid "Click here to select the objects across all pages" +msgstr "Барои интихоби объектҳо дар ҳамаи саҳифаҳо, инҷоро пахш намоед" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Интихоби ҳамаи %(module_name)s (%(total_count)s)" + +msgid "Clear selection" +msgstr "Бекоркунии интихоб" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Моелҳои барномаи %(name)s" + +msgid "Add" +msgstr "Илова кардан" + +msgid "View" +msgstr "Азназаргузаронӣ" + +msgid "You don’t have permission to view or edit anything." +msgstr "" + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" + +msgid "Enter a username and password." +msgstr "Ном ва рамзро ворид созед." + +msgid "Change password" +msgstr "Тағйир додани рамз" + +msgid "Please correct the error below." +msgstr "Хоҳишмандем, хатогии зеринро ислоҳ кунед." + +msgid "Please correct the errors below." +msgstr "Хоҳишмандем, хатогиҳои зеринро ислоҳ кунед." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "Рамзи навро ворид созед %(username)s." + +msgid "Welcome," +msgstr "Марҳамат," + +msgid "View site" +msgstr "Гузариш ба сомона" + +msgid "Documentation" +msgstr "Ҳуҷҷатнигорӣ" + +msgid "Log out" +msgstr "Баромад" + +#, python-format +msgid "Add %(name)s" +msgstr "Дохил кардани %(name)s" + +msgid "History" +msgstr "Таърих" + +msgid "View on site" +msgstr "Дар сомона дидан" + +msgid "Filter" +msgstr "Поло(Filter)" + +msgid "Clear all filters" +msgstr "" + +msgid "Remove from sorting" +msgstr "Аз қайди навъҳо баровардан" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Бартарии навъҳо: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Навъҷудокунӣ дар дигар раванд" + +msgid "Delete" +msgstr "Нест кардан" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Нест кардани %(object_name)s '%(escaped_object)s' ба нестсозии объектҳои ба " +"он алоқаманд оварда мерасонад, аммо'ҳисоби корбарӣ'-и (аккаунт) шумо иҷозати " +"нестсозии объектҳои зеринро надорад:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Нестсозии %(object_name)s '%(escaped_object)s' талаб менамояд, ки " +"объектҳоиалоқаманди муҳофизатии зерин нест карда шаванд:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Шумо боварӣ доред, ки ин элементҳо нест карда шаванд: %(object_name)s " +"\"%(escaped_object)s\"? Ҳамаи объектҳои алоқаманди зерин низ нест карда " +"мешаванд:" + +msgid "Objects" +msgstr "Объектҳо" + +msgid "Yes, I’m sure" +msgstr "" + +msgid "No, take me back" +msgstr "Не, баргаштан" + +msgid "Delete multiple objects" +msgstr "Нестсозии якчанд объектҳо" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Нест кардани %(objects_name)s ба нестсозии объектҳои ба он алоқаманд оварда " +"мерасонад, аммо'ҳисоби корбарӣ'-и (аккаунт) шумо иҷозати нестсозии объектҳои " +"зеринро надорад:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Нестсозии %(objects_name)s талаб менамояд, ки объектҳоиалоқаманди " +"муҳофизатии зерин нест карда шаванд:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Шумо боварӣ доред, ки ин элементҳо нест карда шаванд: %(objects_name)s? " +"Ҳамаи объектҳои алоқаманди зерин низ нест карда мешаванд:" + +msgid "Delete?" +msgstr "Нест кардан?" + +#, python-format +msgid " By %(filter_title)s " +msgstr "%(filter_title)s" + +msgid "Summary" +msgstr "Мухтасар" + +msgid "Recent actions" +msgstr "Амалҳои охирин" + +msgid "My actions" +msgstr "Амалҳои ман" + +msgid "None available" +msgstr "Дастнорас" + +msgid "Unknown content" +msgstr "Шакли номуайян" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Шумо ба система ҳамчун %(username)s, ворид шудед, вале салоҳияти шумобарои " +"азназаргузарониисаҳифаи мазкур нокифоя аст. Шояд шумо мехоҳед бо истифода аз " +"дигар 'ҳисоби корбарӣ' вориди система шавед." + +msgid "Forgotten your password or username?" +msgstr "Рамз ё номро фаромӯш кардед?" + +msgid "Toggle navigation" +msgstr "" + +msgid "Date/time" +msgstr "Сана ва вақт" + +msgid "User" +msgstr "Истифодабар" + +msgid "Action" +msgstr "Амал" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" + +msgid "Show all" +msgstr "Ҳамаро нишон додан" + +msgid "Save" +msgstr "Ҳифз кардан" + +msgid "Popup closing…" +msgstr "Равзанаи иловагӣ пӯшида мешавад..." + +msgid "Search" +msgstr "Ёфтан" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "" +msgstr[1] "" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s ҳамаги" + +msgid "Save as new" +msgstr "Ҳамчун объекти нав ҳифз кардан" + +msgid "Save and add another" +msgstr "Ҳифз кардан ва объекти дигар илова кардан" + +msgid "Save and continue editing" +msgstr "Ҳифз кардан ва танзимотро давом додан" + +msgid "Save and view" +msgstr "Ҳифз кардан ва аз назар гузаронидан" + +msgid "Close" +msgstr "Пӯшидан" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Объекти интихобшударо тағйир додан: \"%(model)s\"" + +#, python-format +msgid "Add another %(model)s" +msgstr "Воридсозии боз як объекти \"%(model)s\"" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Объекти зерини интихобшударо нест кардан \"%(model)s\"" + +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Барои вақти дар ин сомона сарф кардаатон миннатдорем." + +msgid "Log in again" +msgstr "Аз нав ворид шудан" + +msgid "Password change" +msgstr "Тағйири рамз" + +msgid "Your password was changed." +msgstr "Рамзи шумо тағйир дода шуд." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" + +msgid "Change my password" +msgstr "Тағйири рамзи ман" + +msgid "Password reset" +msgstr "Барқароркунии рамз" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Рамзи шумо ҳифз шуд. Акнун шумо метавонед ворид шавед." + +msgid "Password reset confirmation" +msgstr "Барқароркунии рамз тасдиқ карда шуд." + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Хоҳиш мекунем рамзи нави худро ду маротиба(бояд ҳарду мувофиқат кунанд) " +"дохил кунед." + +msgid "New password:" +msgstr "Рамзи нав:" + +msgid "Confirm password:" +msgstr "Рамзи тасдиқӣ:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Суроға барои барқароркунии рамз нодуруст аст. Эҳтимол алакай як маротиба " +"истифода шудааст.Амали барқароркунии рамзро такрор намоед." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Шумо ин матубро гирифтед барои он, ки аз сомонаи %(site_name)s, ки бо ин " +"почтаи электронӣ алоқаманд аст,ба мо дархост барои барқароркунии рамз қабул " +"шуд." + +msgid "Please go to the following page and choose a new password:" +msgstr "Хоҳишмандем ба ин саҳифа гузаред ва рамзи навро ворид созед:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "" + +msgid "Thanks for using our site!" +msgstr "Барои аз сомонаи мо истифода карданатон сипосгузорем!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "Гурӯҳи ташкили %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" + +msgid "Email address:" +msgstr "Суроғаи почтаи электронӣ:" + +msgid "Reset my password" +msgstr "Барқароркунии рамзи ман" + +msgid "All dates" +msgstr "Ҳама санаҳо" + +#, python-format +msgid "Select %s" +msgstr "Интихоб кунед %s" + +#, python-format +msgid "Select %s to change" +msgstr "Интихоби %s барои тағйирот ворид сохтан " + +#, python-format +msgid "Select %s to view" +msgstr "Интихоби %s барои азназаргузаронӣ" + +msgid "Date:" +msgstr "Сана:" + +msgid "Time:" +msgstr "Вақт:" + +msgid "Lookup" +msgstr "Ҷустуҷӯ" + +msgid "Currently:" +msgstr "Ҷорӣ:" + +msgid "Change:" +msgstr "Тағйир додан:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/tr/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/tr/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..046ae242 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/tr/LC_MESSAGES/django 3.po @@ -0,0 +1,762 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# BouRock, 2015-2023 +# BouRock, 2014-2015 +# Caner Başaran , 2013 +# Cihad GÜNDOĞDU , 2012 +# Cihad GÜNDOĞDU , 2014 +# Cihan Okyay , 2014 +# Jannis Leidel , 2011 +# Mesut Can Gürle , 2013 +# Murat Sahin , 2011 +# Yigit Guler , 2020 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 07:05+0000\n" +"Last-Translator: BouRock, 2015-2023\n" +"Language-Team: Turkish (http://www.transifex.com/django/django/language/" +"tr/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: tr\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Seçili %(verbose_name_plural)s nesnelerini sil" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d adet %(items)s başarılı olarak silindi." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "%(name)s silinemiyor" + +msgid "Are you sure?" +msgstr "Emin misiniz?" + +msgid "Administration" +msgstr "Yönetim" + +msgid "All" +msgstr "Tümü" + +msgid "Yes" +msgstr "Evet" + +msgid "No" +msgstr "Hayır" + +msgid "Unknown" +msgstr "Bilinmiyor" + +msgid "Any date" +msgstr "Herhangi bir tarih" + +msgid "Today" +msgstr "Bugün" + +msgid "Past 7 days" +msgstr "Son 7 gün" + +msgid "This month" +msgstr "Bu ay" + +msgid "This year" +msgstr "Bu yıl" + +msgid "No date" +msgstr "Tarih yok" + +msgid "Has date" +msgstr "Tarih var" + +msgid "Empty" +msgstr "Boş" + +msgid "Not empty" +msgstr "Boş değil" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Lütfen görevli hesabı için %(username)s ve parolanızı doğru girin. İki " +"alanın da büyük küçük harfe duyarlı olabildiğini unutmayın." + +msgid "Action:" +msgstr "Eylem:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Başka bir %(verbose_name)s ekle" + +msgid "Remove" +msgstr "Kaldır" + +msgid "Addition" +msgstr "Ekleme" + +msgid "Change" +msgstr "Değiştir" + +msgid "Deletion" +msgstr "Silme" + +msgid "action time" +msgstr "eylem zamanı" + +msgid "user" +msgstr "kullanıcı" + +msgid "content type" +msgstr "içerik türü" + +msgid "object id" +msgstr "nesne kimliği" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "nesne kodu" + +msgid "action flag" +msgstr "eylem işareti" + +msgid "change message" +msgstr "iletiyi değiştir" + +msgid "log entry" +msgstr "günlük girdisi" + +msgid "log entries" +msgstr "günlük girdisi" + +#, python-format +msgid "Added “%(object)s”." +msgstr "“%(object)s” eklendi." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "“%(object)s” değiştirildi — %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "“%(object)s” silindi." + +msgid "LogEntry Object" +msgstr "LogEntry Nesnesi" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "{name} “{object}” eklendi." + +msgid "Added." +msgstr "Eklendi." + +msgid "and" +msgstr "ve" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "{name} “{object}” için {fields} değiştirildi." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "{fields} değiştirildi." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "{name} “{object}” silindi." + +msgid "No fields changed." +msgstr "Değiştirilen alanlar yok." + +msgid "None" +msgstr "Hiçbiri" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" +"Birden fazla seçmek için “Ctrl” veya Mac’teki “Command” tuşuna basılı tutun." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "{name} “{obj}” başarılı olarak eklendi." + +msgid "You may edit it again below." +msgstr "Aşağıdan bunu tekrar düzenleyebilirsiniz." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"{name} “{obj}” başarılı olarak eklendi. Aşağıda başka bir {name} " +"ekleyebilirsiniz." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" +"{name} “{obj}” başarılı olarak değiştirildi. Aşağıda tekrar " +"düzenleyebilirsiniz." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" +"{name} “{obj}” başarılı olarak eklendi. Aşağıda tekrar düzenleyebilirsiniz." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" +"{name} “{obj}” başarılı olarak değiştirildi. Aşağıda başka bir {name} " +"ekleyebilirsiniz." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "{name} “{obj}” başarılı olarak değiştirildi." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Bunlar üzerinde eylemlerin uygulanması için öğeler seçilmek zorundadır. Hiç " +"öğe değiştirilmedi." + +msgid "No action selected." +msgstr "Seçilen eylem yok." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "%(name)s “%(obj)s” başarılı olarak silindi." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "“%(key)s” kimliği olan %(name)s mevcut değil. Belki silinmiş midir?" + +#, python-format +msgid "Add %s" +msgstr "%s ekle" + +#, python-format +msgid "Change %s" +msgstr "%s değiştir" + +#, python-format +msgid "View %s" +msgstr "%s göster" + +msgid "Database error" +msgstr "Veritabanı hatası" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s adet %(name)s başarılı olarak değiştirildi." +msgstr[1] "%(count)s adet %(name)s başarılı olarak değiştirildi." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s nesne seçildi" +msgstr[1] "Tüm %(total_count)s nesne seçildi" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 / %(cnt)s nesne seçildi" + +#, python-format +msgid "Change history: %s" +msgstr "Değişiklik geçmişi: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"%(class_name)s %(instance)s silinmesi aşağıda korunan ilgili nesnelerin de " +"silinmesini gerektirecektir: %(related_objects)s" + +msgid "Django site admin" +msgstr "Django site yöneticisi" + +msgid "Django administration" +msgstr "Django yönetimi" + +msgid "Site administration" +msgstr "Site yönetimi" + +msgid "Log in" +msgstr "Oturum aç" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s yönetimi" + +msgid "Page not found" +msgstr "Sayfa bulunamadı" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Üzgünüz, istediğiniz sayfa bulunamadı." + +msgid "Home" +msgstr "Giriş" + +msgid "Server error" +msgstr "Sunucu hatası" + +msgid "Server error (500)" +msgstr "Sunucu hatası (500)" + +msgid "Server Error (500)" +msgstr "Sunucu Hatası (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Bir hata oluştu. Site yöneticilerine e-posta yoluyla bildirildi ve kısa süre " +"içinde düzeltilecektir. Sabrınız için teşekkür ederiz." + +msgid "Run the selected action" +msgstr "Seçilen eylemi çalıştır" + +msgid "Go" +msgstr "Git" + +msgid "Click here to select the objects across all pages" +msgstr "Tüm sayfalardaki nesneleri seçmek için buraya tıklayın" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Tüm %(total_count)s %(module_name)s nesnelerini seç" + +msgid "Clear selection" +msgstr "Seçimi temizle" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "%(name)s uygulamasındaki modeller" + +msgid "Add" +msgstr "Ekle" + +msgid "View" +msgstr "Göster" + +msgid "You don’t have permission to view or edit anything." +msgstr "Hiçbir şeyi düzenlemek ve göstermek için izne sahip değilsiniz." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Önce, bir kullanıcı adı ve parola girin. Ondan sonra, daha fazla kullanıcı " +"seçeneğini düzenleyebileceksiniz." + +msgid "Enter a username and password." +msgstr "Kullanıcı adı ve parola girin." + +msgid "Change password" +msgstr "Parolayı değiştir" + +msgid "Please correct the error below." +msgid_plural "Please correct the errors below." +msgstr[0] "Lütfen aşağıdaki hatayı düzeltin." +msgstr[1] "Lütfen aşağıdaki hataları düzeltin." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "%(username)s kullanıcısı için yeni bir parola girin." + +msgid "Skip to main content" +msgstr "Ana içeriğe atla" + +msgid "Welcome," +msgstr "Hoş Geldiniz," + +msgid "View site" +msgstr "Siteyi göster" + +msgid "Documentation" +msgstr "Belgeler" + +msgid "Log out" +msgstr "Oturumu kapat" + +msgid "Breadcrumbs" +msgstr "İçerik haritaları" + +#, python-format +msgid "Add %(name)s" +msgstr "%(name)s ekle" + +msgid "History" +msgstr "Geçmiş" + +msgid "View on site" +msgstr "Sitede görüntüle" + +msgid "Filter" +msgstr "Süz" + +msgid "Clear all filters" +msgstr "Tüm süzgeçleri temizle" + +msgid "Remove from sorting" +msgstr "Sıralamadan kaldır" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Sıralama önceliği: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "Sıralamayı değiştir" + +msgid "Toggle theme (current theme: auto)" +msgstr "Temayı değiştir (şu anki tema: otomatik)" + +msgid "Toggle theme (current theme: light)" +msgstr "Temayı değiştir (şu anki tema: açık)" + +msgid "Toggle theme (current theme: dark)" +msgstr "Temayı değiştir (şu anki tema: koyu)" + +msgid "Delete" +msgstr "Sil" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"%(object_name)s '%(escaped_object)s' nesnesinin silinmesi, ilgili nesnelerin " +"silinmesi ile sonuçlanacak, ancak hesabınız aşağıdaki nesnelerin türünü " +"silmek için izine sahip değil." + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"%(object_name)s '%(escaped_object)s' nesnesinin silinmesi, aşağıda korunan " +"ilgili nesnelerin silinmesini gerektirecek:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"%(object_name)s \"%(escaped_object)s\" nesnesini silmek istediğinize emin " +"misiniz? Aşağıdaki ilgili öğelerin tümü silinecektir:" + +msgid "Objects" +msgstr "Nesneler" + +msgid "Yes, I’m sure" +msgstr "Evet, eminim" + +msgid "No, take me back" +msgstr "Hayır, beni geri götür" + +msgid "Delete multiple objects" +msgstr "Birden fazla nesneyi sil" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Seçilen %(objects_name)s nesnelerinin silinmesi, ilgili nesnelerin silinmesi " +"ile sonuçlanacak, ancak hesabınız aşağıdaki nesnelerin türünü silmek için " +"izine sahip değil." + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Seçilen %(objects_name)s nesnelerinin silinmesi, aşağıda korunan ilgili " +"nesnelerin silinmesini gerektirecek:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Seçilen %(objects_name)s nesnelerini silmek istediğinize emin misiniz? " +"Aşağıdaki nesnelerin tümü ve onların ilgili öğeleri silinecektir:" + +msgid "Delete?" +msgstr "Silinsin mi?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " %(filter_title)s süzgecine göre" + +msgid "Summary" +msgstr "Özet" + +msgid "Recent actions" +msgstr "Son eylemler" + +msgid "My actions" +msgstr "Eylemlerim" + +msgid "None available" +msgstr "Mevcut değil" + +msgid "Unknown content" +msgstr "Bilinmeyen içerik" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Veritabanı kurulumunuz ile ilgili birşeyler yanlış. Uygun veritabanı " +"tablolarının oluşturulduğundan ve veritabanının uygun kullanıcı tarafından " +"okunabilir olduğundan emin olun." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"%(username)s olarak kimlik doğrulamanız yapıldı, ancak bu sayfaya erişmek " +"için yetkili değilsiniz. Farklı bir hesapla oturum açmak ister misiniz?" + +msgid "Forgotten your password or username?" +msgstr "Kullanıcı adınızı veya parolanızı mı unuttunuz?" + +msgid "Toggle navigation" +msgstr "Gezinmeyi aç/kapat" + +msgid "Sidebar" +msgstr "Kenar çubuğu" + +msgid "Start typing to filter…" +msgstr "Süzmek için yazmaya başlayın..." + +msgid "Filter navigation items" +msgstr "Gezinti öğelerini süz" + +msgid "Date/time" +msgstr "Tarih/saat" + +msgid "User" +msgstr "Kullanıcı" + +msgid "Action" +msgstr "Eylem" + +msgid "entry" +msgid_plural "entries" +msgstr[0] "giriş" +msgstr[1] "giriş" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"Bu nesne değişme geçmişine sahip değil. Muhtemelen bu yönetici sitesi " +"aracılığıyla eklenmedi." + +msgid "Show all" +msgstr "Tümünü göster" + +msgid "Save" +msgstr "Kaydet" + +msgid "Popup closing…" +msgstr "Açılır pencere kapanıyor…" + +msgid "Search" +msgstr "Ara" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s sonuç" +msgstr[1] "%(counter)s sonuç" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "toplam %(full_result_count)s" + +msgid "Save as new" +msgstr "Yeni olarak kaydet" + +msgid "Save and add another" +msgstr "Kaydet ve başka birini ekle" + +msgid "Save and continue editing" +msgstr "Kaydet ve düzenlemeye devam et" + +msgid "Save and view" +msgstr "Kaydet ve göster" + +msgid "Close" +msgstr "Kapat" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Seçilen %(model)s değiştir" + +#, python-format +msgid "Add another %(model)s" +msgstr "Başka bir %(model)s ekle" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Seçilen %(model)s sil" + +#, python-format +msgid "View selected %(model)s" +msgstr "Seçilen %(model)s görüntüle" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "Bugün web sitesine ayırdığınız kaliteli zaman için teşekkür ederiz." + +msgid "Log in again" +msgstr "Tekrar oturum aç" + +msgid "Password change" +msgstr "Parola değiştime" + +msgid "Your password was changed." +msgstr "Parolanız değiştirildi." + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Güvenliğiniz için, lütfen eski parolanızı girin, ve ondan sonra yeni " +"parolanızı iki kere girin böylece doğru olarak yazdığınızı doğrulayabilelim." + +msgid "Change my password" +msgstr "Parolamı değiştir" + +msgid "Password reset" +msgstr "Parolayı sıfırla" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Parolanız ayarlandı. Şimdi devam edebilir ve oturum açabilirsiniz." + +msgid "Password reset confirmation" +msgstr "Parola sıfırlama onayı" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Lütfen yeni parolanızı iki kere girin böylece böylece doğru olarak " +"yazdığınızı doğrulayabilelim." + +msgid "New password:" +msgstr "Yeni parola:" + +msgid "Confirm password:" +msgstr "Parolayı onayla:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Parola sıfırlama bağlantısı geçersiz olmuş, çünkü zaten kullanılmış. Lütfen " +"yeni bir parola sıfırlama isteyin." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Eğer girdiğiniz e-posta ile bir hesabınız varsa, parolanızın ayarlanması " +"için size talimatları e-posta ile gönderdik. En kısa sürede almalısınız." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Eğer bir e-posta almadıysanız, lütfen kayıt olurken girdiğiniz adresi " +"kullandığınızdan emin olun ve istenmeyen mesajlar klasörünü kontrol edin." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Bu e-postayı alıyorsunuz çünkü %(site_name)s sitesindeki kullanıcı hesabınız " +"için bir parola sıfırlama istediniz." + +msgid "Please go to the following page and choose a new password:" +msgstr "Lütfen şurada belirtilen sayfaya gidin ve yeni bir parola seçin:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "Unutma ihtimalinize karşı, kullanıcı adınız:" + +msgid "Thanks for using our site!" +msgstr "Sitemizi kullandığınız için teşekkürler!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "%(site_name)s ekibi" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Parolanızı mı unuttunuz? Aşağıya e-posta adresinizi girin ve yeni bir tane " +"ayarlamak için talimatları e-posta ile gönderelim." + +msgid "Email address:" +msgstr "E-posta adresi:" + +msgid "Reset my password" +msgstr "Parolamı sıfırla" + +msgid "All dates" +msgstr "Tüm tarihler" + +#, python-format +msgid "Select %s" +msgstr "%s seç" + +#, python-format +msgid "Select %s to change" +msgstr "Değiştirmek için %s seçin" + +#, python-format +msgid "Select %s to view" +msgstr "Göstermek için %s seçin" + +msgid "Date:" +msgstr "Tarih:" + +msgid "Time:" +msgstr "Saat:" + +msgid "Lookup" +msgstr "Arama" + +msgid "Currently:" +msgstr "Şu anda:" + +msgid "Change:" +msgstr "Değiştir:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/tt/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/tt/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..e98f689b --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/tt/LC_MESSAGES/django 3.po @@ -0,0 +1,655 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Azat Khasanshin , 2011 +# v_ildar , 2014 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel \n" +"Language-Team: Tatar (http://www.transifex.com/django/django/language/tt/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: tt\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d %(items)s уңышлы рәвештә бетерелгән." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "%(name)s бетереп булмады" + +msgid "Are you sure?" +msgstr "Сез инанып карар кылдыгызмы?" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Сайланган %(verbose_name_plural)s бетерергә" + +msgid "Administration" +msgstr "" + +msgid "All" +msgstr "Барысы" + +msgid "Yes" +msgstr "Әйе" + +msgid "No" +msgstr "Юк" + +msgid "Unknown" +msgstr "Билгесез" + +msgid "Any date" +msgstr "Теләсә нинди көн һәм вакыт" + +msgid "Today" +msgstr "Бүген" + +msgid "Past 7 days" +msgstr "Соңгы 7 көн" + +msgid "This month" +msgstr "Бу ай" + +msgid "This year" +msgstr "Бу ел" + +msgid "No date" +msgstr "" + +msgid "Has date" +msgstr "" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" + +msgid "Action:" +msgstr "Гамәл:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Тагын бер %(verbose_name)s өстәргә" + +msgid "Remove" +msgstr "Бетерергә" + +msgid "action time" +msgstr "гамәл вакыты" + +msgid "user" +msgstr "" + +msgid "content type" +msgstr "" + +msgid "object id" +msgstr "объект идентификаторы" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/3/library/functions.html#repr) +msgid "object repr" +msgstr "объект фаразы" + +msgid "action flag" +msgstr "гамәл тибы" + +msgid "change message" +msgstr "үзгәрү белдерүе" + +msgid "log entry" +msgstr "журнал язмасы" + +msgid "log entries" +msgstr "журнал язмалары" + +#, python-format +msgid "Added \"%(object)s\"." +msgstr "" + +#, python-format +msgid "Changed \"%(object)s\" - %(changes)s" +msgstr "" + +#, python-format +msgid "Deleted \"%(object)s.\"" +msgstr "" + +msgid "LogEntry Object" +msgstr "" + +#, python-brace-format +msgid "Added {name} \"{object}\"." +msgstr "" + +msgid "Added." +msgstr "" + +msgid "and" +msgstr "һәм" + +#, python-brace-format +msgid "Changed {fields} for {name} \"{object}\"." +msgstr "" + +#, python-brace-format +msgid "Changed {fields}." +msgstr "" + +#, python-brace-format +msgid "Deleted {name} \"{object}\"." +msgstr "" + +msgid "No fields changed." +msgstr "Үзгәртелгән кырлар юк." + +msgid "None" +msgstr "Юк" + +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was added successfully." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." +msgstr "" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Элементар өстеннән гамәл кылу өчен алар сайланган булырга тиеш. Элементлар " +"үзгәртелмәгән." + +msgid "No action selected." +msgstr "Гамәл сайланмаган." + +#, python-format +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "%(name)s \"%(obj)s\" уңышлы рәвештә бетерелгән." + +#, python-format +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" +msgstr "" + +#, python-format +msgid "Add %s" +msgstr "%s өстәргә" + +#, python-format +msgid "Change %s" +msgstr "%s үзгәртергә" + +msgid "Database error" +msgstr "Бирелмәләр базасы хатасы" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s уңышлы рәвештә үзгәртелгән." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s сайланган" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "Барлык %(cnt)s объектан 0 сайланган" + +#, python-format +msgid "Change history: %s" +msgstr "Үзгәртү тарихы: %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" + +msgid "Django site admin" +msgstr "Django сайты идарәсе" + +msgid "Django administration" +msgstr "Django идарәсе" + +msgid "Site administration" +msgstr "Сайт идарәсе" + +msgid "Log in" +msgstr "Керергә" + +#, python-format +msgid "%(app)s administration" +msgstr "" + +msgid "Page not found" +msgstr "Сәхифә табылмаган" + +msgid "We're sorry, but the requested page could not be found." +msgstr "Кызганычка каршы, соралган сәхифә табылмады." + +msgid "Home" +msgstr "Башбит" + +msgid "Server error" +msgstr "Сервер хатасы" + +msgid "Server error (500)" +msgstr "Сервер хатасы (500)" + +msgid "Server Error (500)" +msgstr "Сервер хатасы (500)" + +msgid "" +"There's been an error. It's been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" + +msgid "Run the selected action" +msgstr "Сайланган гамәлне башкарырга" + +msgid "Go" +msgstr "Башкарырга" + +msgid "Click here to select the objects across all pages" +msgstr "Барлык сәхифәләрдә булган объектларны сайлау өчен монда чирттерегез" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Бөтен %(total_count)s %(module_name)s сайларга" + +msgid "Clear selection" +msgstr "Сайланганлыкны алырга" + +msgid "" +"First, enter a username and password. Then, you'll be able to edit more user " +"options." +msgstr "" +"Баштан логин һәм серсүзне кертегез. Аннан соң сез кулланучы турында күбрәк " +"мәгълүматне төзәтә алырсыз." + +msgid "Enter a username and password." +msgstr "Логин һәм серсүзне кертегез." + +msgid "Change password" +msgstr "Серсүзне үзгәртергә" + +msgid "Please correct the error below." +msgstr "Зинһар, биредәге хаталарны төзәтегез." + +msgid "Please correct the errors below." +msgstr "" + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "%(username)s кулланучы өчен яңа серсүзне кертегез." + +msgid "Welcome," +msgstr "Рәхим итегез," + +msgid "View site" +msgstr "" + +msgid "Documentation" +msgstr "Документация" + +msgid "Log out" +msgstr "Чыгарга" + +#, python-format +msgid "Add %(name)s" +msgstr "%(name)s өстәргә" + +msgid "History" +msgstr "Тарих" + +msgid "View on site" +msgstr "Сайтта карарга" + +msgid "Filter" +msgstr "Филтер" + +msgid "Remove from sorting" +msgstr "" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "" + +msgid "Toggle sorting" +msgstr "" + +msgid "Delete" +msgstr "Бетерергә" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"%(object_name)s '%(escaped_object)s' бетереүе аның белән бәйләнгән " +"объектларның бетерелүенә китерә ала, әмма сезнең хисап язмагызның киләсе " +"объект тибларын бетерү өчен хокуклары җитми:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"%(object_name)s '%(escaped_object)s' бетерүе киләсе сакланган объектларның " +"бетерелүен таләп итә:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Сез инанып %(object_name)s \"%(escaped_object)s\" бетерергә телисезме? " +"Барлык киләсе бәйләнгән объектлар да бетерелер:" + +msgid "Objects" +msgstr "" + +msgid "Yes, I'm sure" +msgstr "Әйе, мин инандым" + +msgid "No, take me back" +msgstr "" + +msgid "Delete multiple objects" +msgstr "Берничә объектны бетерергә" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Сайланган %(objects_name)s бетерүе аның белән бәйләнгән объектларның " +"бетерелүенә китерә ала, әмма сезнең хисап язмагызның киләсе объект тибларын " +"бетерү өчен хокуклары җитми:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"%(objects_name)s бетерүе киләсе аның белән бәйләнгән сакланган объектларның " +"бетерелүен таләп итә:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Сез инанып %(objects_name)s бетерергә телисезме? Барлык киләсе объектлар һәм " +"алар белән бәйләнгән элементлар да бетерелер:" + +msgid "Change" +msgstr "Үзгәртергә" + +msgid "Delete?" +msgstr "Бетерергә?" + +#, python-format +msgid " By %(filter_title)s " +msgstr "%(filter_title)s буенча" + +msgid "Summary" +msgstr "" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "" + +msgid "Add" +msgstr "Өстәргә" + +msgid "You don't have permission to edit anything." +msgstr "Төзәтү өчен хокукларыгыз җитми." + +msgid "Recent actions" +msgstr "" + +msgid "My actions" +msgstr "" + +msgid "None available" +msgstr "Тарих юк" + +msgid "Unknown content" +msgstr "Билгесез тип" + +msgid "" +"Something's wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Сезнең бирелмәләр базасы дөрес итем көйләнмәгән. Тиешле җәдвәлләр төзелгәнен " +"һәм тиешле кулланучының хокуклары җитәрлек булуын тикшерегез." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" + +msgid "Forgotten your password or username?" +msgstr "" + +msgid "Date/time" +msgstr "Көн һәм вакыт" + +msgid "User" +msgstr "Кулланучы" + +msgid "Action" +msgstr "Гамәл" + +msgid "" +"This object doesn't have a change history. It probably wasn't added via this " +"admin site." +msgstr "" +"Әлеге объектның үзгәртү тарихы юк. Бу идарә итү сайты буенча өстәлмәгән " +"булуы ихтимал." + +msgid "Show all" +msgstr "Бөтенесен күрсәтергә" + +msgid "Save" +msgstr "Сакларга" + +msgid "Popup closing..." +msgstr "" + +#, python-format +msgid "Change selected %(model)s" +msgstr "" + +#, python-format +msgid "Add another %(model)s" +msgstr "" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "" + +msgid "Search" +msgstr "Эзләргә" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s нәтиҗә" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "барлыгы %(full_result_count)s" + +msgid "Save as new" +msgstr "Яңа объект итеп сакларга" + +msgid "Save and add another" +msgstr "Сакларга һәм бүтән объектны өстәргә" + +msgid "Save and continue editing" +msgstr "Сакларга һәм төзәтүне дәвам итәргә" + +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Сайтыбызда үткәргән вакыт өчен рәхмәт." + +msgid "Log in again" +msgstr "Тагын керергә" + +msgid "Password change" +msgstr "Серсүзне үзгәртү" + +msgid "Your password was changed." +msgstr "Серсүзегез үзгәртелгән." + +msgid "" +"Please enter your old password, for security's sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Хәвефсезлек сәбәпле, зинһар, үзегезнең иске серсүзне кертегез, аннан яңа " +"серсүзне ике тапкыр кертегез (дөрес язылышын тикшерү өчен)." + +msgid "Change my password" +msgstr "Серсүземне үзгәртергә" + +msgid "Password reset" +msgstr "Серсүзне торгызу" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Серсүзегез үзгәртелгән. Сез хәзер керә аласыз." + +msgid "Password reset confirmation" +msgstr "Серсүзне торгызу раслау" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "Зинһар, тикшерү өчен яңа серсүзегезне ике тапкыр кертегез." + +msgid "New password:" +msgstr "Яңа серсуз:" + +msgid "Confirm password:" +msgstr "Серсүзне раслагыз:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Серсүзне торгызу өчен сылтама хаталы. Бәлки аның белән инде кулланганнар. " +"Зинһар, серсүзне тагын бер тапкыр торгызып карагыз." + +msgid "" +"We've emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" + +msgid "" +"If you don't receive an email, please make sure you've entered the address " +"you registered with, and check your spam folder." +msgstr "" + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" + +msgid "Please go to the following page and choose a new password:" +msgstr "Зинһар, бу сәхифәгә юнәлегез һәм яңа серсүзне кертегез:" + +msgid "Your username, in case you've forgotten:" +msgstr "Сезнең кулланучы исемегез (оныткан булсагыз):" + +msgid "Thanks for using our site!" +msgstr "Безнең сайтны куллану өчен рәхмәт!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "%(site_name)s сайтының төркеме" + +msgid "" +"Forgotten your password? Enter your email address below, and we'll email " +"instructions for setting a new one." +msgstr "" + +msgid "Email address:" +msgstr "Эл. почта адресы:" + +msgid "Reset my password" +msgstr "Серсүземне торгызырга" + +msgid "All dates" +msgstr "Бөтен көннәр" + +#, python-format +msgid "Select %s" +msgstr "%s сайлагыз" + +#, python-format +msgid "Select %s to change" +msgstr "Үзгәртү өчен %s сайлагыз" + +msgid "Date:" +msgstr "Көн:" + +msgid "Time:" +msgstr "Вакыт:" + +msgid "Lookup" +msgstr "Эзләү" + +msgid "Currently:" +msgstr "" + +msgid "Change:" +msgstr "" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ur/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ur/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..80f901e1 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/ur/LC_MESSAGES/django 3.po @@ -0,0 +1,661 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Mansoorulhaq Mansoor , 2011 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Jannis Leidel \n" +"Language-Team: Urdu (http://www.transifex.com/django/django/language/ur/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ur\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d %(items)s کو کامیابی سے مٹا دیا گیا۔" + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "%(name)s نہیں مٹایا جا سکتا" + +msgid "Are you sure?" +msgstr "آپ کو یقین ھے؟" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "منتخب شدہ %(verbose_name_plural)s مٹائیں" + +msgid "Administration" +msgstr "" + +msgid "All" +msgstr "تمام" + +msgid "Yes" +msgstr "ھاں" + +msgid "No" +msgstr "نھیں" + +msgid "Unknown" +msgstr "نامعلوم" + +msgid "Any date" +msgstr "کوئی تاریخ" + +msgid "Today" +msgstr "آج" + +msgid "Past 7 days" +msgstr "گزشتہ سات دن" + +msgid "This month" +msgstr "یہ مھینہ" + +msgid "This year" +msgstr "یہ سال" + +msgid "No date" +msgstr "" + +msgid "Has date" +msgstr "" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" + +msgid "Action:" +msgstr "کاروائی:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "دوسرا %(verbose_name)s درج کریں" + +msgid "Remove" +msgstr "خارج کریں" + +msgid "action time" +msgstr "کاروائی کا وقت" + +msgid "user" +msgstr "" + +msgid "content type" +msgstr "" + +msgid "object id" +msgstr "شے کا شناختی نمبر" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/3/library/functions.html#repr) +msgid "object repr" +msgstr "شے کا نمائندہ" + +msgid "action flag" +msgstr "کاروائی کا پرچم" + +msgid "change message" +msgstr "پیغام تبدیل کریں" + +msgid "log entry" +msgstr "لاگ کا اندراج" + +msgid "log entries" +msgstr "لاگ کے اندراج" + +#, python-format +msgid "Added \"%(object)s\"." +msgstr "" + +#, python-format +msgid "Changed \"%(object)s\" - %(changes)s" +msgstr "" + +#, python-format +msgid "Deleted \"%(object)s.\"" +msgstr "" + +msgid "LogEntry Object" +msgstr "" + +#, python-brace-format +msgid "Added {name} \"{object}\"." +msgstr "" + +msgid "Added." +msgstr "" + +msgid "and" +msgstr "اور" + +#, python-brace-format +msgid "Changed {fields} for {name} \"{object}\"." +msgstr "" + +#, python-brace-format +msgid "Changed {fields}." +msgstr "" + +#, python-brace-format +msgid "Deleted {name} \"{object}\"." +msgstr "" + +msgid "No fields changed." +msgstr "کوئی خانہ تبدیل نھیں کیا گیا۔" + +msgid "None" +msgstr "کوئی نھیں" + +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was added successfully." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." +msgstr "" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." +msgstr "" + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." +msgstr "" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"اشیاء پر کاروائی سرانجام دینے کے لئے ان کا منتخب ھونا ضروری ھے۔ کوئی شے " +"تبدیل نھیں کی گئی۔" + +msgid "No action selected." +msgstr "کوئی کاروائی منتخب نھیں کی گئی۔" + +#, python-format +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "%(name)s \"%(obj)s\" کامیابی سے مٹایا گیا تھا۔" + +#, python-format +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" +msgstr "" + +#, python-format +msgid "Add %s" +msgstr "%s کا اضافہ کریں" + +#, python-format +msgid "Change %s" +msgstr "%s تبدیل کریں" + +msgid "Database error" +msgstr "ڈیٹا بیس کی خرابی" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s کامیابی سے تبدیل کیا گیا تھا۔" +msgstr[1] "%(count)s %(name)s کامیابی سے تبدیل کیے گئے تھے۔" + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s منتخب کیا گیا۔" +msgstr[1] "تمام %(total_count)s منتخب کئے گئے۔" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "%(cnt)s میں سے 0 منتخب کیا گیا۔" + +#, python-format +msgid "Change history: %s" +msgstr "%s کی تبدیلی کا تاریخ نامہ" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" + +msgid "Django site admin" +msgstr "منتظم برائے جینگو سائٹ" + +msgid "Django administration" +msgstr "انتظامیہ برائے جینگو سائٹ" + +msgid "Site administration" +msgstr "سائٹ کی انتظامیہ" + +msgid "Log in" +msgstr "اندر جائیں" + +#, python-format +msgid "%(app)s administration" +msgstr "" + +msgid "Page not found" +msgstr "صفحہ نھیں ملا" + +msgid "We're sorry, but the requested page could not be found." +msgstr "ھم معذرت خواہ ھیں، مطلوبہ صفحہ نھیں مل سکا۔" + +msgid "Home" +msgstr "گھر" + +msgid "Server error" +msgstr "سرور کی خرابی" + +msgid "Server error (500)" +msgstr "سرور کی خرابی (500)" + +msgid "Server Error (500)" +msgstr "سرور کی خرابی (500)" + +msgid "" +"There's been an error. It's been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" + +msgid "Run the selected action" +msgstr "منتخب شدہ کاروائیاں چلائیں" + +msgid "Go" +msgstr "جاؤ" + +msgid "Click here to select the objects across all pages" +msgstr "تمام صفحات میں سے اشیاء منتخب کرنے کے لئے یہاں کلک کریں۔" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "تمام %(total_count)s %(module_name)s منتخب کریں" + +msgid "Clear selection" +msgstr "انتخاب صاف کریں" + +msgid "" +"First, enter a username and password. Then, you'll be able to edit more user " +"options." +msgstr "" +"پہلے نام صارف اور لفظ اجازت درج کریں۔ پھر آپ مزید صارف کے حقوق مدوّن کرنے کے " +"قابل ھوں گے۔" + +msgid "Enter a username and password." +msgstr "نام صارف اور لفظ اجازت درج کریں۔" + +msgid "Change password" +msgstr "لفظ اجازت تبدیل کریں" + +msgid "Please correct the error below." +msgstr "براہ کرم نیچے غلطیاں درست کریں۔" + +msgid "Please correct the errors below." +msgstr "" + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "صارف %(username)s کے لئے نیا لفظ اجازت درج کریں۔" + +msgid "Welcome," +msgstr "خوش آمدید،" + +msgid "View site" +msgstr "" + +msgid "Documentation" +msgstr "طریق استعمال" + +msgid "Log out" +msgstr "باہر جائیں" + +#, python-format +msgid "Add %(name)s" +msgstr "%(name)s کا اضافہ کریں" + +msgid "History" +msgstr "تاریخ نامہ" + +msgid "View on site" +msgstr "سائٹ پر مشاھدہ کریں" + +msgid "Filter" +msgstr "چھانٹیں" + +msgid "Remove from sorting" +msgstr "" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "" + +msgid "Toggle sorting" +msgstr "" + +msgid "Delete" +msgstr "مٹائیں" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"%(object_name)s '%(escaped_object)s' کو مٹانے کے نتیجے میں معتلقہ اشیاء مٹ " +"سکتی ھیں، مگر آپ کے کھاتے کو اشیاء کی مندرجہ ذیل اقسام مٹانے کا حق حاصل نھیں " +"ھے۔" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"%(object_name)s '%(escaped_object)s' کو مٹانے کے لئے مندرجہ ذیل محفوظ متعلقہ " +"اشیاء کو مٹانے کی ضرورت پڑ سکتی ھے۔" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"واقعی آپ %(object_name)s \"%(escaped_object)s\" کو مٹانا چاہتے ھیں۔ مندرجہ " +"ذیل تمام متعلقہ اجزاء مٹ جائیں گے۔" + +msgid "Objects" +msgstr "" + +msgid "Yes, I'm sure" +msgstr "ھاں، مجھے یقین ھے" + +msgid "No, take me back" +msgstr "" + +msgid "Delete multiple objects" +msgstr "متعدد اشیاء مٹائیں" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"منتخب شدہ %(objects_name)s کو مٹانے کے نتیجے میں متعلقہ اشیاء مٹ سکتی ھیں، " +"لیکن آپ کے کھاتے کو اشیاء کی مندرجہ ذیل اقسام کو مٹانے کا حق حاصل نھیں ھے۔" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"منتخب شدہ %(objects_name)s کو مٹانے کے لئے مندرجہ ذیل محفوظ شدہ اشیاء کو " +"مٹانے کی ضرورت پڑ سکتی ھے۔" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"واقعی آپ منتخب شدہ %(objects_name)s مٹانا چاھتے ھیں؟ مندرجہ ذیل اور ان سے " +"متعلقہ تمام اشیاء حذف ھو جائیں گی۔" + +msgid "Change" +msgstr "تدوین" + +msgid "Delete?" +msgstr "مٹاؤں؟" + +#, python-format +msgid " By %(filter_title)s " +msgstr "از %(filter_title)s" + +msgid "Summary" +msgstr "" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "" + +msgid "Add" +msgstr "اضافہ" + +msgid "You don't have permission to edit anything." +msgstr "آپ کو کوئی چیز مدوّن کرنے کا حق نھیں ھے۔" + +msgid "Recent actions" +msgstr "" + +msgid "My actions" +msgstr "" + +msgid "None available" +msgstr "کچھ دستیاب نھیں" + +msgid "Unknown content" +msgstr "نامعلوم مواد" + +msgid "" +"Something's wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"آپ کی ڈیٹا بیس کی تنصیب میں کوئی چیز خراب ھے۔ یقین کر لیں کہ موزون ڈیٹا بیس " +"ٹیبل بنائے گئے تھے، اور یقین کر لیں کہ ڈیٹ بیس مناسب صارف کے پڑھے جانے کے " +"قابل ھے۔" + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" + +msgid "Forgotten your password or username?" +msgstr "" + +msgid "Date/time" +msgstr "تاریخ/وقت" + +msgid "User" +msgstr "صارف" + +msgid "Action" +msgstr "کاروائی" + +msgid "" +"This object doesn't have a change history. It probably wasn't added via this " +"admin site." +msgstr "" +"اس شے کا تبدیلی کا تاریخ نامہ نھیں ھے۔ اس کا غالباً بذریعہ اس منتظم سائٹ کے " +"اضافہ نھیں کیا گیا۔" + +msgid "Show all" +msgstr "تمام دکھائیں" + +msgid "Save" +msgstr "محفوظ کریں" + +msgid "Popup closing..." +msgstr "" + +#, python-format +msgid "Change selected %(model)s" +msgstr "" + +#, python-format +msgid "Add another %(model)s" +msgstr "" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "" + +msgid "Search" +msgstr "تلاش کریں" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s نتیجہ" +msgstr[1] "%(counter)s نتائج" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "کل %(full_result_count)s" + +msgid "Save as new" +msgstr "بطور نیا محفوظ کریں" + +msgid "Save and add another" +msgstr "محفوظ کریں اور مزید اضافہ کریں" + +msgid "Save and continue editing" +msgstr "محفوظ کریں اور تدوین جاری رکھیں" + +msgid "Thanks for spending some quality time with the Web site today." +msgstr "ویب سائٹ پر آج کچھ معیاری وقت خرچ کرنے کے لئے شکریہ۔" + +msgid "Log in again" +msgstr "دوبارہ اندر جائیں" + +msgid "Password change" +msgstr "لفظ اجازت کی تبدیلی" + +msgid "Your password was changed." +msgstr "آپ کا لفظ اجازت تبدیل کر دیا گیا تھا۔" + +msgid "" +"Please enter your old password, for security's sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"براہ کرم سیکیورٹی کی خاطر اپنا پرانا لفظ اجازت درج کریں اور پھر اپنا نیا لفظ " +"اجازت دو مرتبہ درج کریں تاکہ ھم توثیق کر سکیں کہ آپ نے اسے درست درج کیا ھے۔" + +msgid "Change my password" +msgstr "میرا لفظ تبدیل کریں" + +msgid "Password reset" +msgstr "لفظ اجازت کی دوبارہ ترتیب" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "" +"آپ کا لفظ اجازت مرتب کر دیا گیا ھے۔ آپ کو آگے بڑھنے اور اندر جانے کی اجازت " +"ھے۔" + +msgid "Password reset confirmation" +msgstr "لفظ اجازت دوبارہ مرتب کرنے کی توثیق" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"براہ مھربانی اپنا نیا لفظ اجازت دو مرتبہ درج کریں تاکہ تاکہ ھم تصدیق کر سکیں " +"کہ تم نے اسے درست درج کیا ھے۔" + +msgid "New password:" +msgstr "نیا لفظ اجازت:" + +msgid "Confirm password:" +msgstr "لفظ اجازت کی توثیق:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"لفظ اجازت دوبارہ مرتب کرنے کا رابطہ (لنک) غلط تھا، غالباً یہ پہلے ھی استعمال " +"کیا چکا تھا۔ براہ مھربانی نیا لفظ اجازت مرتب کرنے کی درخواست کریں۔" + +msgid "" +"We've emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" + +msgid "" +"If you don't receive an email, please make sure you've entered the address " +"you registered with, and check your spam folder." +msgstr "" + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" + +msgid "Please go to the following page and choose a new password:" +msgstr "براہ مھربانی مندرجہ ذیل صفحے پر جائیں اور نیا لفظ اجازت پسند کریں:" + +msgid "Your username, in case you've forgotten:" +msgstr "نام صارف، بھول جانے کی صورت میں:" + +msgid "Thanks for using our site!" +msgstr "ھماری سائٹ استعمال کرنے کے لئے شکریہ" + +#, python-format +msgid "The %(site_name)s team" +msgstr "%(site_name)s کی ٹیم" + +msgid "" +"Forgotten your password? Enter your email address below, and we'll email " +"instructions for setting a new one." +msgstr "" + +msgid "Email address:" +msgstr "" + +msgid "Reset my password" +msgstr "میرا لفظ اجازت دوبارہ مرتب کریں" + +msgid "All dates" +msgstr "تمام تاریخیں" + +#, python-format +msgid "Select %s" +msgstr "%s منتخب کریں" + +#, python-format +msgid "Select %s to change" +msgstr "تبدیل کرنے کے لئے %s منتخب کریں" + +msgid "Date:" +msgstr "تاریخ:" + +msgid "Time:" +msgstr "وقت:" + +msgid "Lookup" +msgstr "ڈھونڈیں" + +msgid "Currently:" +msgstr "" + +msgid "Change:" +msgstr "" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..59b223fc --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/vi/LC_MESSAGES/django 3.po @@ -0,0 +1,726 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Dimitris Glezos , 2012 +# Jannis Leidel , 2011 +# Thanh Le Viet , 2013 +# Tran , 2011 +# Tran Van , 2011-2013,2016,2018 +# tinnguyen121221, 2021 +# Vuong Nguyen , 2011 +# xgenvn , 2014 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-09-21 10:22+0200\n" +"PO-Revision-Date: 2021-12-23 17:57+0000\n" +"Last-Translator: tinnguyen121221\n" +"Language-Team: Vietnamese (http://www.transifex.com/django/django/language/" +"vi/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: vi\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Xóa các %(verbose_name_plural)s đã chọn" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Đã xóa thành công %(count)d %(items)s ." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "Không thể xóa %(name)s" + +msgid "Are you sure?" +msgstr "Bạn có chắc chắn không?" + +msgid "Administration" +msgstr "Quản trị website" + +msgid "All" +msgstr "Tất cả" + +msgid "Yes" +msgstr "Có" + +msgid "No" +msgstr "Không" + +msgid "Unknown" +msgstr "Chưa xác định" + +msgid "Any date" +msgstr "Bất kì ngày nào" + +msgid "Today" +msgstr "Hôm nay" + +msgid "Past 7 days" +msgstr "7 ngày trước" + +msgid "This month" +msgstr "Tháng này" + +msgid "This year" +msgstr "Năm nay" + +msgid "No date" +msgstr "Không có ngày" + +msgid "Has date" +msgstr "Có ngày" + +msgid "Empty" +msgstr "Rỗng" + +msgid "Not empty" +msgstr "Không rỗng" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "" +"Bạn hãy nhập đúng %(username)s và mật khẩu. (Có phân biệt chữ hoa, thường)" + +msgid "Action:" +msgstr "Hoạt động:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Thêm một %(verbose_name)s " + +msgid "Remove" +msgstr "Gỡ bỏ" + +msgid "Addition" +msgstr "Thêm" + +msgid "Change" +msgstr "Thay đổi" + +msgid "Deletion" +msgstr "Xóa" + +msgid "action time" +msgstr "Thời gian tác động" + +msgid "user" +msgstr "người dùng" + +msgid "content type" +msgstr "kiểu nội dung" + +msgid "object id" +msgstr "Mã đối tượng" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "đối tượng repr" + +msgid "action flag" +msgstr "hiệu hành động" + +msgid "change message" +msgstr "thay đổi tin nhắn" + +msgid "log entry" +msgstr "đăng nhập" + +msgid "log entries" +msgstr "mục đăng nhập" + +#, python-format +msgid "Added “%(object)s”." +msgstr "Đã thêm “%(object)s”." + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "Đã thay đổi “%(object)s” — %(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "Đã xóa “%(object)s.”" + +msgid "LogEntry Object" +msgstr "LogEntry Object" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "Đã thêm {name} “{object}”." + +msgid "Added." +msgstr "Được thêm." + +msgid "and" +msgstr "và" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "Đã thay đổi {fields} cho {name} “{object}”." + +#, python-brace-format +msgid "Changed {fields}." +msgstr "Đã thay đổi {fields}." + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "Đã xóa {name} “{object}”." + +msgid "No fields changed." +msgstr "Không có trường nào thay đổi" + +msgid "None" +msgstr "Không" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "" +"Nhấn giữ phím “Control”, hoặc “Command” trên máy Mac, để chọn nhiều hơn một." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "{name} “{obj}” được thêm vào thành công." + +msgid "You may edit it again below." +msgstr "Bạn có thể chỉnh sửa lại bên dưới." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "" +"{name} “{obj}” được thêm vào thành công. Bạn có thể thêm một {name} khác bên " +"dưới." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "" +"{name} “{obj}” được chỉnh sửa thành công. Bạn có thể chỉnh sửa lại bên dưới." + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "" +"{name} “{obj}” được thêm vào thành công. Bạn có thể chỉnh sửa lại bên dưới." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "" +"{name} “{obj}” được chỉnh sửa thành công. Bạn có thể thêm một {name} khác " +"bên dưới." + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "{name} “{obj}” đã được thay đổi thành công." + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Mục tiêu phải được chọn mới có thể thực hiện hành động trên chúng. Không có " +"mục tiêu nào đã được thay đổi." + +msgid "No action selected." +msgstr "Không có hoạt động nào được lựa chọn." + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "%(name)s “%(obj)s” đã được xóa thành công." + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "%(name)s với ID “%(key)s” không tồn tại. Có lẽ nó đã bị xóa?" + +#, python-format +msgid "Add %s" +msgstr "Thêm %s" + +#, python-format +msgid "Change %s" +msgstr "Thay đổi %s" + +#, python-format +msgid "View %s" +msgstr "Xem %s" + +msgid "Database error" +msgstr "Cơ sở dữ liệu bị lỗi" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] " %(count)s %(name)s đã được thay đổi thành công." + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "Tất cả %(total_count)s đã được chọn" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 của %(cnt)s được chọn" + +#, python-format +msgid "Change history: %s" +msgstr "Lịch sử thay đổi: %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"Xóa %(class_name)s %(instance)s sẽ tự động xóa các đối tượng liên quan sau: " +"%(related_objects)s" + +msgid "Django site admin" +msgstr "Trang web admin Django" + +msgid "Django administration" +msgstr "Trang quản trị cho Django" + +msgid "Site administration" +msgstr "Site quản trị hệ thống." + +msgid "Log in" +msgstr "Đăng nhập" + +#, python-format +msgid "%(app)s administration" +msgstr "Quản lý %(app)s" + +msgid "Page not found" +msgstr "Không tìm thấy trang nào" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "Rất tiếc, không thể tìm thấy trang được yêu cầu." + +msgid "Home" +msgstr "Trang chủ" + +msgid "Server error" +msgstr "Lỗi máy chủ" + +msgid "Server error (500)" +msgstr "Lỗi máy chủ (500)" + +msgid "Server Error (500)" +msgstr "Lỗi máy chủ (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"Có lỗi xảy ra. Lỗi sẽ được gửi đến quản trị website qua email và sẽ được " +"khắc phục sớm. Cám ơn bạn." + +msgid "Run the selected action" +msgstr "Bắt đầu hành động lựa chọn" + +msgid "Go" +msgstr "Đi đến" + +msgid "Click here to select the objects across all pages" +msgstr "Click vào đây để lựa chọn các đối tượng trên tất cả các trang" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Hãy chọn tất cả %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "Xóa lựa chọn" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "Các mô models trong %(name)s" + +msgid "Add" +msgstr "Thêm vào" + +msgid "View" +msgstr "Xem" + +msgid "You don’t have permission to view or edit anything." +msgstr "Bạn không có quyền xem hoặc chỉnh sửa bất cứ gì." + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "" +"Đầu tiên, điền tên đăng nhập và mật khẩu. Sau đó, bạn mới có thể chỉnh sửa " +"nhiều hơn lựa chọn của người dùng." + +msgid "Enter a username and password." +msgstr "Điền tên đăng nhập và mật khẩu." + +msgid "Change password" +msgstr "Đổi mật khẩu" + +msgid "Please correct the error below." +msgstr "Hãy sửa lỗi sai dưới đây" + +msgid "Please correct the errors below." +msgstr "Hãy chỉnh sửa lại các lỗi sau." + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "Hãy nhập mật khẩu mới cho người sử dụng %(username)s." + +msgid "Welcome," +msgstr "Chào mừng bạn," + +msgid "View site" +msgstr "Xem trang web" + +msgid "Documentation" +msgstr "Tài liệu" + +msgid "Log out" +msgstr "Thoát" + +#, python-format +msgid "Add %(name)s" +msgstr "Thêm vào %(name)s" + +msgid "History" +msgstr "Bản ghi nhớ" + +msgid "View on site" +msgstr "Xem trên trang web" + +msgid "Filter" +msgstr "Bộ lọc" + +msgid "Clear all filters" +msgstr "Xóa tất cả bộ lọc" + +msgid "Remove from sorting" +msgstr "Bỏ khỏi sắp xếp" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "Sắp xếp theo:%(priority_number)s" + +msgid "Toggle sorting" +msgstr "Hoán đổi sắp xếp" + +msgid "Delete" +msgstr "Xóa" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Xóa %(object_name)s '%(escaped_object)s' sẽ làm mất những dữ liệu có liên " +"quan. Tài khoản của bạn không được cấp quyển xóa những dữ liệu đi kèm theo." + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"Xóa các %(object_name)s ' %(escaped_object)s ' sẽ bắt buộc xóa các đối " +"tượng được bảo vệ sau đây:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Bạn có chắc là muốn xóa %(object_name)s \"%(escaped_object)s\"?Tất cả những " +"dữ liệu đi kèm dưới đây cũng sẽ bị mất:" + +msgid "Objects" +msgstr "Đối tượng" + +msgid "Yes, I’m sure" +msgstr "Có, tôi chắc chắn" + +msgid "No, take me back" +msgstr "Không, đưa tôi trở lại" + +msgid "Delete multiple objects" +msgstr "Xóa nhiều đối tượng" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"Xóa các %(objects_name)s sẽ bắt buộc xóa các đối tượng liên quan, nhưng tài " +"khoản của bạn không có quyền xóa các loại đối tượng sau đây:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" +"Xóa các %(objects_name)s sẽ bắt buộc xóa các đối tượng đã được bảo vệ sau " +"đây:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"Bạn chắc chắn muốn xóa những lựa chọn %(objects_name)s? Tất cả những đối " +"tượng sau và những đối tượng liên quan sẽ được xóa:" + +msgid "Delete?" +msgstr "Bạn muốn xóa?" + +#, python-format +msgid " By %(filter_title)s " +msgstr "Bởi %(filter_title)s " + +msgid "Summary" +msgstr "Tóm tắt" + +msgid "Recent actions" +msgstr "Hoạt động gần đây" + +msgid "My actions" +msgstr "Hoạt động của tôi" + +msgid "None available" +msgstr "Không có sẵn" + +msgid "Unknown content" +msgstr "Không biết nội dung" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Một vài lỗi với cơ sở dữ liệu cài đặt của bạn. Hãy chắc chắn bảng biểu dữ " +"liệu được tạo phù hợp và dữ liệu có thể được đọc bởi những người sử dụng phù " +"hợp." + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"Bạn đã xác thực bằng tài khoản %(username)s, nhưng không đủ quyền để truy " +"cập trang này. Bạn có muốn đăng nhập bằng một tài khoản khác?" + +msgid "Forgotten your password or username?" +msgstr "Bạn quên mật khẩu hoặc tài khoản?" + +msgid "Toggle navigation" +msgstr "" + +msgid "Start typing to filter…" +msgstr "Nhập để lọc..." + +msgid "Filter navigation items" +msgstr "" + +msgid "Date/time" +msgstr "Ngày/giờ" + +msgid "User" +msgstr "Người dùng" + +msgid "Action" +msgstr "Hành động" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" +"Đối tượng này không có một lịch sử thay đổi. Nó có lẽ đã không được thêm vào " +"qua trang web admin." + +msgid "Show all" +msgstr "Hiện tất cả" + +msgid "Save" +msgstr "Lưu lại" + +msgid "Popup closing…" +msgstr "Đang đóng cửa sổ popup ..." + +msgid "Search" +msgstr "Tìm kiếm" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s kết quả" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "tổng số %(full_result_count)s" + +msgid "Save as new" +msgstr "Lưu mới" + +msgid "Save and add another" +msgstr "Lưu và thêm mới" + +msgid "Save and continue editing" +msgstr "Lưu và tiếp tục chỉnh sửa" + +msgid "Save and view" +msgstr "Lưu lại và xem" + +msgid "Close" +msgstr "Đóng" + +#, python-format +msgid "Change selected %(model)s" +msgstr "Thay đổi %(model)s đã chọn" + +#, python-format +msgid "Add another %(model)s" +msgstr "Thêm %(model)s khác" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "Xóa %(model)s đã chọn" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "Cảm ơn bạn đã dành thời gian với trang web." + +msgid "Log in again" +msgstr "Đăng nhập lại" + +msgid "Password change" +msgstr "Thay đổi mật khẩu" + +msgid "Your password was changed." +msgstr "Mật khẩu của bạn đã được thay đổi" + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Hãy nhập lại mật khẩu cũ và sau đó nhập mật khẩu mới hai lần để chúng tôi có " +"thể kiểm tra lại xem bạn đã gõ chính xác hay chưa." + +msgid "Change my password" +msgstr "Thay đổi mật khẩu" + +msgid "Password reset" +msgstr "Lập lại mật khẩu" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "Mật khẩu của bạn đã được lập lại. Bạn hãy thử đăng nhập." + +msgid "Password reset confirmation" +msgstr "Xác nhận việc lập lại mật khẩu" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "" +"Hãy nhập mật khẩu mới hai lần để chúng tôi có thể kiểm tra xem bạn đã gõ " +"chính xác chưa" + +msgid "New password:" +msgstr "Mật khẩu mới" + +msgid "Confirm password:" +msgstr "Nhập lại mật khẩu:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "" +"Liên kết đặt lại mật khẩu không hợp lệ, có thể vì nó đã được sử dụng. Xin " +"vui lòng yêu cầu đặt lại mật khẩu mới." + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"Chúng tôi đã gửi cho bạn hướng dẫn thiết lập mật khẩu của bạn qua email, nếu " +"tài khoản tồn tại với email bạn đã nhập. Bạn sẽ nhận được chúng sớm." + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"Nếu bạn không nhận được email, hãy đảm bảo rằng bạn đã nhập địa chỉ mà bạn " +"đã đăng ký và kiểm tra thư mục spam của mình." + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Bạn nhận được email này vì bạn đã yêu cầu làm mới lại mật khẩu cho tài khoản " +"của bạn tại %(site_name)s." + +msgid "Please go to the following page and choose a new password:" +msgstr "Hãy vào đường link dưới đây và chọn một mật khẩu mới" + +msgid "Your username, in case you’ve forgotten:" +msgstr "Tên đăng nhập của bạn, trường hợp bạn quên nó:" + +msgid "Thanks for using our site!" +msgstr "Cảm ơn bạn đã sử dụng website của chúng tôi!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "Đội của %(site_name)s" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"Quên mật khẩu? Nhập địa chỉ email vào ô dưới đây. Chúng tôi sẽ email cho bạn " +"hướng dẫn cách thiết lập mật khẩu mới." + +msgid "Email address:" +msgstr "Địa chỉ Email:" + +msgid "Reset my password" +msgstr "Làm lại mật khẩu" + +msgid "All dates" +msgstr "Tất cả các ngày" + +#, python-format +msgid "Select %s" +msgstr "Chọn %s" + +#, python-format +msgid "Select %s to change" +msgstr "Chọn %s để thay đổi" + +#, python-format +msgid "Select %s to view" +msgstr "Chọn %s để xem" + +msgid "Date:" +msgstr "Ngày:" + +msgid "Time:" +msgstr "Giờ:" + +msgid "Lookup" +msgstr "Tìm" + +msgid "Currently:" +msgstr "Hiện nay:" + +msgid "Change:" +msgstr "Thay đổi:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/zh_Hans/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/zh_Hans/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..937a66c6 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/zh_Hans/LC_MESSAGES/django 3.po @@ -0,0 +1,744 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# lanbla , 2021 +# Brian Wang , 2018 +# Fulong Sun , 2016 +# Huanqun Yang, 2022 +# Jannis Leidel , 2011 +# Kevin Sze , 2012 +# Lele Long , 2011,2015 +# Le Yang , 2018 +# li beite , 2020 +# Liping Wang , 2016-2017 +# mozillazg , 2016 +# Ronald White , 2013-2014 +# Sean Lee , 2013 +# Sean Lee , 2013 +# slene , 2011 +# Suntravel Chris , 2019 +# Wentao Han , 2018,2020 +# xuyi wang , 2018 +# yf zhan , 2018 +# dykai , 2019 +# ced773123cfad7b4e8b79ca80f736af9, 2012 +# Fangjiaqi77 <370358679@qq.com>, 2020 +# Kevin Sze , 2012 +# 考证 李 , 2020 +# 雨翌 , 2016 +# 高乐喆 , 2023 +# Ronald White , 2013 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-17 02:13-0600\n" +"PO-Revision-Date: 2023-04-25 07:05+0000\n" +"Last-Translator: 高乐喆 , 2023\n" +"Language-Team: Chinese (China) (http://www.transifex.com/django/django/" +"language/zh_CN/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: zh_CN\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "删除所选的 %(verbose_name_plural)s" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "成功删除了 %(count)d 个 %(items)s" + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "无法删除 %(name)s" + +msgid "Are you sure?" +msgstr "你确定吗?" + +msgid "Administration" +msgstr "管理" + +msgid "All" +msgstr "全部" + +msgid "Yes" +msgstr "是" + +msgid "No" +msgstr "否" + +msgid "Unknown" +msgstr "未知" + +msgid "Any date" +msgstr "任意日期" + +msgid "Today" +msgstr "今天" + +msgid "Past 7 days" +msgstr "过去7天" + +msgid "This month" +msgstr "本月" + +msgid "This year" +msgstr "今年" + +msgid "No date" +msgstr "没有日期" + +msgid "Has date" +msgstr "具有日期" + +msgid "Empty" +msgstr "空" + +msgid "Not empty" +msgstr "非空" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "请输入一个正确的 %(username)s 和密码. 注意他们都是区分大小写的." + +msgid "Action:" +msgstr "动作" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "添加另一个 %(verbose_name)s" + +msgid "Remove" +msgstr "删除" + +msgid "Addition" +msgstr "添加" + +msgid "Change" +msgstr "修改" + +msgid "Deletion" +msgstr "删除" + +msgid "action time" +msgstr "操作时间" + +msgid "user" +msgstr "用户" + +msgid "content type" +msgstr "内容类型" + +msgid "object id" +msgstr "对象id" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/library/functions.html#repr) +msgid "object repr" +msgstr "对象表示" + +msgid "action flag" +msgstr "动作标志" + +msgid "change message" +msgstr "修改消息" + +msgid "log entry" +msgstr "日志记录" + +msgid "log entries" +msgstr "日志记录" + +#, python-format +msgid "Added “%(object)s”." +msgstr "添加了“%(object)s”。" + +#, python-format +msgid "Changed “%(object)s” — %(changes)s" +msgstr "修改了“%(object)s”—%(changes)s" + +#, python-format +msgid "Deleted “%(object)s.”" +msgstr "删除了“%(object)s”。" + +msgid "LogEntry Object" +msgstr "LogEntry对象" + +#, python-brace-format +msgid "Added {name} “{object}”." +msgstr "添加了 {name}“{object}”。" + +msgid "Added." +msgstr "已添加。" + +msgid "and" +msgstr "和" + +#, python-brace-format +msgid "Changed {fields} for {name} “{object}”." +msgstr "修改了 {name}“{object}”的 {fields}。" + +#, python-brace-format +msgid "Changed {fields}." +msgstr "已修改{fields}。" + +#, python-brace-format +msgid "Deleted {name} “{object}”." +msgstr "删除了 {name}“{object}”。" + +msgid "No fields changed." +msgstr "没有字段被修改。" + +msgid "None" +msgstr "无" + +msgid "Hold down “Control”, or “Command” on a Mac, to select more than one." +msgstr "按住 Control 键或 Mac 上的 Command 键来选择多项。" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully." +msgstr "成功添加了 {name}“{obj}”。" + +msgid "You may edit it again below." +msgstr "您可以在下面再次编辑它." + +#, python-brace-format +msgid "" +"The {name} “{obj}” was added successfully. You may add another {name} below." +msgstr "成功添加了 {name}“{obj}”。你可以在下面添加另一个 {name}。" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may edit it again below." +msgstr "成功修改了 {name}“{obj}”。你可以在下面再次编辑它。" + +#, python-brace-format +msgid "The {name} “{obj}” was added successfully. You may edit it again below." +msgstr "成功添加了 {name}“{obj}”。你可以在下面再次编辑它。" + +#, python-brace-format +msgid "" +"The {name} “{obj}” was changed successfully. You may add another {name} " +"below." +msgstr "成功修改了 {name}“{obj}”。你可以在下面添加另一个 {name}。" + +#, python-brace-format +msgid "The {name} “{obj}” was changed successfully." +msgstr "成功修改了 {name}“{obj}”。" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "条目必须选中以对其进行操作。没有任何条目被更改。" + +msgid "No action selected." +msgstr "未选择动作" + +#, python-format +msgid "The %(name)s “%(obj)s” was deleted successfully." +msgstr "成功删除了 %(name)s“%(obj)s”。" + +#, python-format +msgid "%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?" +msgstr "ID 为“%(key)s”的 %(name)s 不存在。可能已经被删除了?" + +#, python-format +msgid "Add %s" +msgstr "增加 %s" + +#, python-format +msgid "Change %s" +msgstr "修改 %s" + +#, python-format +msgid "View %s" +msgstr "查看 %s" + +msgid "Database error" +msgstr "数据库错误" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "总共 %(count)s 个 %(name)s 变更成功。" + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "选中了 %(total_count)s 个" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "%(cnt)s 个中 0 个被选" + +#, python-format +msgid "Change history: %s" +msgstr "变更历史: %s" + +#. Translators: Model verbose name and instance +#. representation, suitable to be an item in a +#. list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"删除 %(class_name)s %(instance)s 将需要删除以下受保护的相关对象: " +"%(related_objects)s" + +msgid "Django site admin" +msgstr "Django 站点管理员" + +msgid "Django administration" +msgstr "Django 管理" + +msgid "Site administration" +msgstr "站点管理" + +msgid "Log in" +msgstr "登录" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s 管理" + +msgid "Page not found" +msgstr "页面没有找到" + +msgid "We’re sorry, but the requested page could not be found." +msgstr "非常抱歉,请求的页面不存在。" + +msgid "Home" +msgstr "首页" + +msgid "Server error" +msgstr "服务器错误" + +msgid "Server error (500)" +msgstr "服务器错误(500)" + +msgid "Server Error (500)" +msgstr "服务器错误 (500)" + +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"发生了错误,已经通过电子邮件报告给了网站管理员。我们会尽快修复,感谢您的耐心" +"等待。" + +msgid "Run the selected action" +msgstr "运行选中的动作" + +msgid "Go" +msgstr "执行" + +msgid "Click here to select the objects across all pages" +msgstr "点击此处选择所有页面中包含的对象。" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "选中所有的 %(total_count)s 个 %(module_name)s" + +msgid "Clear selection" +msgstr "清除选中" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "在应用程序 %(name)s 中的模型" + +msgid "Add" +msgstr "增加" + +msgid "View" +msgstr "查看" + +msgid "You don’t have permission to view or edit anything." +msgstr "你没有查看或编辑的权限。" + +msgid "" +"First, enter a username and password. Then, you’ll be able to edit more user " +"options." +msgstr "输入用户名和密码后,你将能够编辑更多的用户选项。" + +msgid "Enter a username and password." +msgstr "输入用户名和密码" + +msgid "Change password" +msgstr "修改密码" + +msgid "Please correct the error below." +msgid_plural "Please correct the errors below." +msgstr[0] "请更正以下错误。" + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "为用户 %(username)s 输入一个新的密码。" + +msgid "Skip to main content" +msgstr "跳到主要内容" + +msgid "Welcome," +msgstr "欢迎," + +msgid "View site" +msgstr "查看站点" + +msgid "Documentation" +msgstr "文档" + +msgid "Log out" +msgstr "注销" + +msgid "Breadcrumbs" +msgstr "面包屑" + +#, python-format +msgid "Add %(name)s" +msgstr "增加 %(name)s" + +msgid "History" +msgstr "历史" + +msgid "View on site" +msgstr "在站点上查看" + +msgid "Filter" +msgstr "过滤器" + +msgid "Clear all filters" +msgstr "清除所有过滤器" + +msgid "Remove from sorting" +msgstr "删除排序" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "排序优先级: %(priority_number)s" + +msgid "Toggle sorting" +msgstr "正逆序切换" + +msgid "Toggle theme (current theme: auto)" +msgstr "切换主题(当前主题:自动)" + +msgid "Toggle theme (current theme: light)" +msgstr "切换主题(当前主题:浅色)" + +msgid "Toggle theme (current theme: dark)" +msgstr "切换主题(当前主题:深色)" + +msgid "Delete" +msgstr "删除" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"删除 %(object_name)s '%(escaped_object)s' 会导致删除相关的对象,但你的帐号无" +"权删除下列类型的对象:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"要删除 %(object_name)s '%(escaped_object)s', 将要求删除以下受保护的相关对象:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"你确认想要删除 %(object_name)s \"%(escaped_object)s\"? 下列所有相关的项目都" +"将被删除:" + +msgid "Objects" +msgstr "对象" + +msgid "Yes, I’m sure" +msgstr "是的,我确定" + +msgid "No, take me back" +msgstr "不,返回" + +msgid "Delete multiple objects" +msgstr "删除多个对象" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"要删除所选的 %(objects_name)s 结果会删除相关对象, 但你的账户没有权限删除这类" +"对象:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "要删除所选的 %(objects_name)s, 将要求删除以下受保护的相关对象:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"请确认要删除选中的 %(objects_name)s 吗?以下所有对象和余它们相关的条目将都会" +"被删除:" + +msgid "Delete?" +msgstr "删除?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " 以 %(filter_title)s" + +msgid "Summary" +msgstr "概览" + +msgid "Recent actions" +msgstr "最近动作" + +msgid "My actions" +msgstr "我的动作" + +msgid "None available" +msgstr "无可用的" + +msgid "Unknown content" +msgstr "未知内容" + +msgid "" +"Something’s wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"数据库设置有误。请检查所需的数据库表格是否已经创建,以及数据库用户是否具有正" +"确的权限。" + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"您当前以%(username)s登录,但是没有这个页面的访问权限。您想使用另外一个账号登" +"录吗?" + +msgid "Forgotten your password or username?" +msgstr "忘记了您的密码或用户名?" + +msgid "Toggle navigation" +msgstr "切换导航" + +msgid "Sidebar" +msgstr "侧边栏" + +msgid "Start typing to filter…" +msgstr "开始输入以筛选..." + +msgid "Filter navigation items" +msgstr "筛选导航项目" + +msgid "Date/time" +msgstr "日期/时间" + +msgid "User" +msgstr "用户" + +msgid "Action" +msgstr "动作" + +msgid "entry" +msgid_plural "entries" +msgstr[0] "条目" + +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "此对象没有修改历史。它可能不是通过管理站点添加的。" + +msgid "Show all" +msgstr "显示全部" + +msgid "Save" +msgstr "保存" + +msgid "Popup closing…" +msgstr "弹窗关闭中..." + +msgid "Search" +msgstr "搜索" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s 条结果。" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "总共 %(full_result_count)s" + +msgid "Save as new" +msgstr "保存为新的" + +msgid "Save and add another" +msgstr "保存并增加另一个" + +msgid "Save and continue editing" +msgstr "保存并继续编辑" + +msgid "Save and view" +msgstr "保存并查看" + +msgid "Close" +msgstr "关闭" + +#, python-format +msgid "Change selected %(model)s" +msgstr "更改选中的%(model)s" + +#, python-format +msgid "Add another %(model)s" +msgstr "增加另一个 %(model)s" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "取消选中 %(model)s" + +#, python-format +msgid "View selected %(model)s" +msgstr "查看已选择的%(model)s" + +msgid "Thanks for spending some quality time with the web site today." +msgstr "感谢您今天与本网站共度一段高品质时光。" + +msgid "Log in again" +msgstr "重新登录" + +msgid "Password change" +msgstr "密码修改" + +msgid "Your password was changed." +msgstr "你的密码已修改。" + +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "安全起见请输入你的旧密码。然后输入两次你的新密码以确保输入正确。" + +msgid "Change my password" +msgstr "修改我的密码" + +msgid "Password reset" +msgstr "密码重设" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "你的密码己经设置完成,现在你可以继续进行登录。" + +msgid "Password reset confirmation" +msgstr "密码重设确认" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "请输入两遍新密码,以便我们校验你输入的是否正确。" + +msgid "New password:" +msgstr "新密码:" + +msgid "Confirm password:" +msgstr "确认密码:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "密码重置链接无效,可能是因为它已使用。可以请求一次新的密码重置。" + +msgid "" +"We’ve emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"如果你所输入的电子邮箱存在对应的用户,我们将通过电子邮件向你发送设置密码的操" +"作步骤说明。你应该很快就会收到。" + +msgid "" +"If you don’t receive an email, please make sure you’ve entered the address " +"you registered with, and check your spam folder." +msgstr "" +"如果你没有收到电子邮件,请检查输入的是你注册的电子邮箱地址。另外,也请检查你" +"的垃圾邮件文件夹。" + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "你收到这封邮件是因为你请求重置你在网站 %(site_name)s上的用户账户密码。" + +msgid "Please go to the following page and choose a new password:" +msgstr "请访问该页面并选择一个新密码:" + +msgid "Your username, in case you’ve forgotten:" +msgstr "提醒一下,你的用户名是:" + +msgid "Thanks for using our site!" +msgstr "感谢使用我们的站点!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "%(site_name)s 团队" + +msgid "" +"Forgotten your password? Enter your email address below, and we’ll email " +"instructions for setting a new one." +msgstr "" +"忘记密码?在下面输入你的电子邮箱地址,我们将会把设置新密码的操作步骤说明通过" +"电子邮件发送给你。" + +msgid "Email address:" +msgstr "电子邮件地址:" + +msgid "Reset my password" +msgstr "重设我的密码" + +msgid "All dates" +msgstr "所有日期" + +#, python-format +msgid "Select %s" +msgstr "选择 %s" + +#, python-format +msgid "Select %s to change" +msgstr "选择 %s 来修改" + +#, python-format +msgid "Select %s to view" +msgstr "选择%s查看" + +msgid "Date:" +msgstr "日期:" + +msgid "Time:" +msgstr "时间:" + +msgid "Lookup" +msgstr "查询" + +msgid "Currently:" +msgstr "当前:" + +msgid "Change:" +msgstr "更改:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/locale/zh_Hant/LC_MESSAGES/django 3.po b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/zh_Hant/LC_MESSAGES/django 3.po new file mode 100644 index 00000000..6a8f35a8 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/locale/zh_Hant/LC_MESSAGES/django 3.po @@ -0,0 +1,660 @@ +# This file is distributed under the same license as the Django package. +# +# Translators: +# Chen Chun-Chia , 2015 +# ilay , 2012 +# Jannis Leidel , 2011 +# mail6543210 , 2013-2014 +# ming hsien tzang , 2011 +# tcc , 2011 +# Tzu-ping Chung , 2016-2017 +# Yeh-Yung , 2013 +# Yeh-Yung , 2012 +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-19 16:49+0100\n" +"PO-Revision-Date: 2017-09-19 16:40+0000\n" +"Last-Translator: Tzu-ping Chung \n" +"Language-Team: Chinese (Taiwan) (http://www.transifex.com/django/django/" +"language/zh_TW/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: zh_TW\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "成功的刪除了 %(count)d 個 %(items)s." + +#, python-format +msgid "Cannot delete %(name)s" +msgstr "無法刪除 %(name)s" + +msgid "Are you sure?" +msgstr "你確定嗎?" + +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "刪除所選的 %(verbose_name_plural)s" + +msgid "Administration" +msgstr "管理" + +msgid "All" +msgstr "全部" + +msgid "Yes" +msgstr "是" + +msgid "No" +msgstr "否" + +msgid "Unknown" +msgstr "未知" + +msgid "Any date" +msgstr "任何日期" + +msgid "Today" +msgstr "今天" + +msgid "Past 7 days" +msgstr "過去 7 天" + +msgid "This month" +msgstr "本月" + +msgid "This year" +msgstr "今年" + +msgid "No date" +msgstr "沒有日期" + +msgid "Has date" +msgstr "有日期" + +#, python-format +msgid "" +"Please enter the correct %(username)s and password for a staff account. Note " +"that both fields may be case-sensitive." +msgstr "請輸入正確的工作人員%(username)s及密碼。請注意兩者皆區分大小寫。" + +msgid "Action:" +msgstr "動作:" + +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "新增其它 %(verbose_name)s" + +msgid "Remove" +msgstr "移除" + +msgid "action time" +msgstr "動作時間" + +msgid "user" +msgstr "使用者" + +msgid "content type" +msgstr "內容類型" + +msgid "object id" +msgstr "物件 id" + +#. Translators: 'repr' means representation +#. (https://docs.python.org/3/library/functions.html#repr) +msgid "object repr" +msgstr "物件 repr" + +msgid "action flag" +msgstr "動作旗標" + +msgid "change message" +msgstr "變更訊息" + +msgid "log entry" +msgstr "紀錄項目" + +msgid "log entries" +msgstr "紀錄項目" + +#, python-format +msgid "Added \"%(object)s\"." +msgstr "\"%(object)s\" 已新增。" + +#, python-format +msgid "Changed \"%(object)s\" - %(changes)s" +msgstr "\"%(object)s\" - %(changes)s 已變更。" + +#, python-format +msgid "Deleted \"%(object)s.\"" +msgstr "\"%(object)s\" 已刪除。" + +msgid "LogEntry Object" +msgstr "紀錄項目" + +#, python-brace-format +msgid "Added {name} \"{object}\"." +msgstr "{name} \"{object}\" 已新增。" + +msgid "Added." +msgstr "已新增。" + +msgid "and" +msgstr "和" + +#, python-brace-format +msgid "Changed {fields} for {name} \"{object}\"." +msgstr "{name} \"{object}\" 的 {fields} 已變更。" + +#, python-brace-format +msgid "Changed {fields}." +msgstr "{fields} 已變更。" + +#, python-brace-format +msgid "Deleted {name} \"{object}\"." +msgstr "{name} \"{object}\" 已刪除。" + +msgid "No fields changed." +msgstr "沒有欄位被變更。" + +msgid "None" +msgstr "無" + +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "按住 \"Control\" 或 \"Command\" (Mac),可選取多個值" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may edit it again below." +msgstr "{name} \"{obj}\" 新增成功。你可以在下面再次編輯它。" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was added successfully. You may add another {name} " +"below." +msgstr "{name} \"{obj}\" 新增成功。你可以在下方加入其他 {name}。" + +#, python-brace-format +msgid "The {name} \"{obj}\" was added successfully." +msgstr "{name} \"{obj}\" 已成功新增。" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may edit it again below." +msgstr "{name} \"{obj}\" 變更成功。你可以在下方再次編輯。" + +#, python-brace-format +msgid "" +"The {name} \"{obj}\" was changed successfully. You may add another {name} " +"below." +msgstr "{name} \"{obj}\" 變更成功。你可以在下方加入其他 {name}。" + +#, python-brace-format +msgid "The {name} \"{obj}\" was changed successfully." +msgstr "{name} \"{obj}\" 已成功變更。" + +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "必須要有項目被選到才能對它們進行動作。沒有項目變更。" + +msgid "No action selected." +msgstr "沒有動作被選。" + +#, python-format +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "%(name)s \"%(obj)s\" 已成功刪除。" + +#, python-format +msgid "%(name)s with ID \"%(key)s\" doesn't exist. Perhaps it was deleted?" +msgstr "不存在 ID 為「%(key)s」的 %(name)s。或許它已被刪除?" + +#, python-format +msgid "Add %s" +msgstr "新增 %s" + +#, python-format +msgid "Change %s" +msgstr "變更 %s" + +msgid "Database error" +msgstr "資料庫錯誤" + +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "共 %(count)s %(name)s 已變更成功。" + +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "全部 %(total_count)s 個被選" + +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "%(cnt)s 中 0 個被選" + +#, python-format +msgid "Change history: %s" +msgstr "變更歷史: %s" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "%(class_name)s %(instance)s" + +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" +"刪除 %(class_name)s %(instance)s 將會同時刪除下面受保護的相關物件:" +"%(related_objects)s" + +msgid "Django site admin" +msgstr "Django 網站管理" + +msgid "Django administration" +msgstr "Django 管理" + +msgid "Site administration" +msgstr "網站管理" + +msgid "Log in" +msgstr "登入" + +#, python-format +msgid "%(app)s administration" +msgstr "%(app)s 管理" + +msgid "Page not found" +msgstr "頁面沒有找到" + +msgid "We're sorry, but the requested page could not be found." +msgstr "很抱歉,請求頁面無法找到。" + +msgid "Home" +msgstr "首頁" + +msgid "Server error" +msgstr "伺服器錯誤" + +msgid "Server error (500)" +msgstr "伺服器錯誤 (500)" + +msgid "Server Error (500)" +msgstr "伺服器錯誤 (500)" + +msgid "" +"There's been an error. It's been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" +"存在一個錯誤。已透過電子郵件回報給網站管理員,並且應該很快就會被修正。謝謝你" +"的關心。" + +msgid "Run the selected action" +msgstr "執行選擇的動作" + +msgid "Go" +msgstr "去" + +msgid "Click here to select the objects across all pages" +msgstr "點選這裡可選取全部頁面的物件" + +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "選擇全部 %(total_count)s %(module_name)s" + +msgid "Clear selection" +msgstr "清除選擇" + +msgid "" +"First, enter a username and password. Then, you'll be able to edit more user " +"options." +msgstr "首先,輸入一個使用者名稱和密碼。然後你可以編輯更多使用者選項。" + +msgid "Enter a username and password." +msgstr "輸入一個使用者名稱和密碼。" + +msgid "Change password" +msgstr "變更密碼" + +msgid "Please correct the error below." +msgstr "請更正下面的錯誤。" + +msgid "Please correct the errors below." +msgstr "請修正以下錯誤" + +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "為使用者%(username)s輸入一個新的密碼。" + +msgid "Welcome," +msgstr "歡迎," + +msgid "View site" +msgstr "檢視網站" + +msgid "Documentation" +msgstr "文件" + +msgid "Log out" +msgstr "登出" + +#, python-format +msgid "Add %(name)s" +msgstr "新增 %(name)s" + +msgid "History" +msgstr "歷史" + +msgid "View on site" +msgstr "在網站上檢視" + +msgid "Filter" +msgstr "過濾器" + +msgid "Remove from sorting" +msgstr "從排序中移除" + +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "優先排序:%(priority_number)s" + +msgid "Toggle sorting" +msgstr "切換排序" + +msgid "Delete" +msgstr "刪除" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"刪除 %(object_name)s '%(escaped_object)s' 會把相關的物件也刪除,不過你的帳號" +"並沒有刪除以下型態物件的權限:" + +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" +"要刪除 %(object_name)s '%(escaped_object)s', 將要求刪除下面受保護的相關物件:" + +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"你確定想要刪除 %(object_name)s \"%(escaped_object)s\"?以下所有的相關項目都會" +"被刪除:" + +msgid "Objects" +msgstr "物件" + +msgid "Yes, I'm sure" +msgstr "是的,我確定" + +msgid "No, take me back" +msgstr "不,請帶我回去" + +msgid "Delete multiple objects" +msgstr "刪除多個物件" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" +"要刪除所選的 %(objects_name)s, 結果會刪除相關物件, 但你的帳號無權刪除下面物件" +"型態:" + +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "要刪除所選的 %(objects_name)s, 將要求刪除下面受保護的相關物件:" + +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" +"你是否確定要刪除已選的 %(objects_name)s? 下面全部物件及其相關項目都將被刪除:" + +msgid "Change" +msgstr "變更" + +msgid "Delete?" +msgstr "刪除?" + +#, python-format +msgid " By %(filter_title)s " +msgstr " 以 %(filter_title)s" + +msgid "Summary" +msgstr "總結" + +#, python-format +msgid "Models in the %(name)s application" +msgstr "%(name)s 應用程式中的Model" + +msgid "Add" +msgstr "新增" + +msgid "You don't have permission to edit anything." +msgstr "你沒有編輯任何東西的權限。" + +msgid "Recent actions" +msgstr "最近的動作" + +msgid "My actions" +msgstr "我的動作" + +msgid "None available" +msgstr "無可用的" + +msgid "Unknown content" +msgstr "未知內容" + +msgid "" +"Something's wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"你的資料庫安裝有錯誤。確定資料庫表格已經建立,並確定資料庫可被合適的使用者讀" +"取。" + +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" +"您已認證為 %(username)s,但並沒有瀏覽此頁面的權限。您是否希望以其他帳號登入?" + +msgid "Forgotten your password or username?" +msgstr "忘了你的密碼或是使用者名稱?" + +msgid "Date/time" +msgstr "日期/時間" + +msgid "User" +msgstr "使用者" + +msgid "Action" +msgstr "動作" + +msgid "" +"This object doesn't have a change history. It probably wasn't added via this " +"admin site." +msgstr "這個物件沒有變更的歷史。它可能不是透過這個管理網站新增的。" + +msgid "Show all" +msgstr "顯示全部" + +msgid "Save" +msgstr "儲存" + +msgid "Popup closing..." +msgstr "關閉彈出視窗中⋯⋯" + +#, python-format +msgid "Change selected %(model)s" +msgstr "變更所選的 %(model)s" + +#, python-format +msgid "Add another %(model)s" +msgstr "新增其它 %(model)s" + +#, python-format +msgid "Delete selected %(model)s" +msgstr "刪除所選的 %(model)s" + +msgid "Search" +msgstr "搜尋" + +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "%(counter)s 結果" + +#, python-format +msgid "%(full_result_count)s total" +msgstr "總共 %(full_result_count)s" + +msgid "Save as new" +msgstr "儲存為新的" + +msgid "Save and add another" +msgstr "儲存並新增另一個" + +msgid "Save and continue editing" +msgstr "儲存並繼續編輯" + +msgid "Thanks for spending some quality time with the Web site today." +msgstr "感謝你今天花了重要的時間停留在本網站。" + +msgid "Log in again" +msgstr "重新登入" + +msgid "Password change" +msgstr "密碼變更" + +msgid "Your password was changed." +msgstr "你的密碼已變更。" + +msgid "" +"Please enter your old password, for security's sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"為了安全上的考量,請輸入你的舊密碼,再輸入新密碼兩次,讓我們核驗你已正確地輸" +"入。" + +msgid "Change my password" +msgstr "變更我的密碼" + +msgid "Password reset" +msgstr "密碼重設" + +msgid "Your password has been set. You may go ahead and log in now." +msgstr "你的密碼已設置,現在可以繼續登入。" + +msgid "Password reset confirmation" +msgstr "密碼重設確認" + +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "請輸入你的新密碼兩次, 這樣我們才能檢查你的輸入是否正確。" + +msgid "New password:" +msgstr "新密碼:" + +msgid "Confirm password:" +msgstr "確認密碼:" + +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "密碼重設連結無效,可能因為他已使用。請重新請求密碼重設。" + +msgid "" +"We've emailed you instructions for setting your password, if an account " +"exists with the email you entered. You should receive them shortly." +msgstr "" +"若您提交的電子郵件地址存在對應帳號,我們已寄出重設密碼的相關指示。您應該很快" +"就會收到。" + +msgid "" +"If you don't receive an email, please make sure you've entered the address " +"you registered with, and check your spam folder." +msgstr "" +"如果您未收到電子郵件,請確認您輸入的電子郵件地址與您註冊時輸入的一致,並檢查" +"您的垃圾郵件匣。" + +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "這封電子郵件來自 %(site_name)s,因為你要求為帳號重新設定密碼。" + +msgid "Please go to the following page and choose a new password:" +msgstr "請到該頁面選擇一個新的密碼:" + +msgid "Your username, in case you've forgotten:" +msgstr "你的使用者名稱,萬一你已經忘記的話:" + +msgid "Thanks for using our site!" +msgstr "感謝使用本網站!" + +#, python-format +msgid "The %(site_name)s team" +msgstr "%(site_name)s 團隊" + +msgid "" +"Forgotten your password? Enter your email address below, and we'll email " +"instructions for setting a new one." +msgstr "" +"忘記你的密碼? 請在下面輸入你的電子郵件位址, 然後我們會寄出設定新密碼的操作指" +"示。" + +msgid "Email address:" +msgstr "電子信箱:" + +msgid "Reset my password" +msgstr "重設我的密碼" + +msgid "All dates" +msgstr "所有日期" + +#, python-format +msgid "Select %s" +msgstr "選擇 %s" + +#, python-format +msgid "Select %s to change" +msgstr "選擇 %s 來變更" + +msgid "Date:" +msgstr "日期" + +msgid "Time:" +msgstr "時間" + +msgid "Lookup" +msgstr "查詢" + +msgid "Currently:" +msgstr "目前:" + +msgid "Change:" +msgstr "變動:" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/options 3.py b/virt/lib/python3.9/site-packages/django/contrib/admin/options 3.py new file mode 100644 index 00000000..66acabbb --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/options 3.py @@ -0,0 +1,2501 @@ +import copy +import json +import re +from functools import partial, update_wrapper +from urllib.parse import quote as urlquote + +from django import forms +from django.conf import settings +from django.contrib import messages +from django.contrib.admin import helpers, widgets +from django.contrib.admin.checks import ( + BaseModelAdminChecks, + InlineModelAdminChecks, + ModelAdminChecks, +) +from django.contrib.admin.decorators import display +from django.contrib.admin.exceptions import DisallowedModelAdminToField +from django.contrib.admin.templatetags.admin_urls import add_preserved_filters +from django.contrib.admin.utils import ( + NestedObjects, + construct_change_message, + flatten_fieldsets, + get_deleted_objects, + lookup_spawns_duplicates, + model_format_dict, + model_ngettext, + quote, + unquote, +) +from django.contrib.admin.widgets import AutocompleteSelect, AutocompleteSelectMultiple +from django.contrib.auth import get_permission_codename +from django.core.exceptions import ( + FieldDoesNotExist, + FieldError, + PermissionDenied, + ValidationError, +) +from django.core.paginator import Paginator +from django.db import models, router, transaction +from django.db.models.constants import LOOKUP_SEP +from django.forms.formsets import DELETION_FIELD_NAME, all_valid +from django.forms.models import ( + BaseInlineFormSet, + inlineformset_factory, + modelform_defines_fields, + modelform_factory, + modelformset_factory, +) +from django.forms.widgets import CheckboxSelectMultiple, SelectMultiple +from django.http import HttpResponseRedirect +from django.http.response import HttpResponseBase +from django.template.response import SimpleTemplateResponse, TemplateResponse +from django.urls import reverse +from django.utils.decorators import method_decorator +from django.utils.html import format_html +from django.utils.http import urlencode +from django.utils.safestring import mark_safe +from django.utils.text import ( + capfirst, + format_lazy, + get_text_list, + smart_split, + unescape_string_literal, +) +from django.utils.translation import gettext as _ +from django.utils.translation import ngettext +from django.views.decorators.csrf import csrf_protect +from django.views.generic import RedirectView + +IS_POPUP_VAR = "_popup" +TO_FIELD_VAR = "_to_field" + + +HORIZONTAL, VERTICAL = 1, 2 + + +def get_content_type_for_model(obj): + # Since this module gets imported in the application's root package, + # it cannot import models from other applications at the module level. + from django.contrib.contenttypes.models import ContentType + + return ContentType.objects.get_for_model(obj, for_concrete_model=False) + + +def get_ul_class(radio_style): + return "radiolist" if radio_style == VERTICAL else "radiolist inline" + + +class IncorrectLookupParameters(Exception): + pass + + +# Defaults for formfield_overrides. ModelAdmin subclasses can change this +# by adding to ModelAdmin.formfield_overrides. + +FORMFIELD_FOR_DBFIELD_DEFAULTS = { + models.DateTimeField: { + "form_class": forms.SplitDateTimeField, + "widget": widgets.AdminSplitDateTime, + }, + models.DateField: {"widget": widgets.AdminDateWidget}, + models.TimeField: {"widget": widgets.AdminTimeWidget}, + models.TextField: {"widget": widgets.AdminTextareaWidget}, + models.URLField: {"widget": widgets.AdminURLFieldWidget}, + models.IntegerField: {"widget": widgets.AdminIntegerFieldWidget}, + models.BigIntegerField: {"widget": widgets.AdminBigIntegerFieldWidget}, + models.CharField: {"widget": widgets.AdminTextInputWidget}, + models.ImageField: {"widget": widgets.AdminFileWidget}, + models.FileField: {"widget": widgets.AdminFileWidget}, + models.EmailField: {"widget": widgets.AdminEmailInputWidget}, + models.UUIDField: {"widget": widgets.AdminUUIDInputWidget}, +} + +csrf_protect_m = method_decorator(csrf_protect) + + +class BaseModelAdmin(metaclass=forms.MediaDefiningClass): + """Functionality common to both ModelAdmin and InlineAdmin.""" + + autocomplete_fields = () + raw_id_fields = () + fields = None + exclude = None + fieldsets = None + form = forms.ModelForm + filter_vertical = () + filter_horizontal = () + radio_fields = {} + prepopulated_fields = {} + formfield_overrides = {} + readonly_fields = () + ordering = None + sortable_by = None + view_on_site = True + show_full_result_count = True + checks_class = BaseModelAdminChecks + + def check(self, **kwargs): + return self.checks_class().check(self, **kwargs) + + def __init__(self): + # Merge FORMFIELD_FOR_DBFIELD_DEFAULTS with the formfield_overrides + # rather than simply overwriting. + overrides = copy.deepcopy(FORMFIELD_FOR_DBFIELD_DEFAULTS) + for k, v in self.formfield_overrides.items(): + overrides.setdefault(k, {}).update(v) + self.formfield_overrides = overrides + + def formfield_for_dbfield(self, db_field, request, **kwargs): + """ + Hook for specifying the form Field instance for a given database Field + instance. + + If kwargs are given, they're passed to the form Field's constructor. + """ + # If the field specifies choices, we don't need to look for special + # admin widgets - we just need to use a select widget of some kind. + if db_field.choices: + return self.formfield_for_choice_field(db_field, request, **kwargs) + + # ForeignKey or ManyToManyFields + if isinstance(db_field, (models.ForeignKey, models.ManyToManyField)): + # Combine the field kwargs with any options for formfield_overrides. + # Make sure the passed in **kwargs override anything in + # formfield_overrides because **kwargs is more specific, and should + # always win. + if db_field.__class__ in self.formfield_overrides: + kwargs = {**self.formfield_overrides[db_field.__class__], **kwargs} + + # Get the correct formfield. + if isinstance(db_field, models.ForeignKey): + formfield = self.formfield_for_foreignkey(db_field, request, **kwargs) + elif isinstance(db_field, models.ManyToManyField): + formfield = self.formfield_for_manytomany(db_field, request, **kwargs) + + # For non-raw_id fields, wrap the widget with a wrapper that adds + # extra HTML -- the "add other" interface -- to the end of the + # rendered output. formfield can be None if it came from a + # OneToOneField with parent_link=True or a M2M intermediary. + if formfield and db_field.name not in self.raw_id_fields: + related_modeladmin = self.admin_site._registry.get( + db_field.remote_field.model + ) + wrapper_kwargs = {} + if related_modeladmin: + wrapper_kwargs.update( + can_add_related=related_modeladmin.has_add_permission(request), + can_change_related=related_modeladmin.has_change_permission( + request + ), + can_delete_related=related_modeladmin.has_delete_permission( + request + ), + can_view_related=related_modeladmin.has_view_permission( + request + ), + ) + formfield.widget = widgets.RelatedFieldWidgetWrapper( + formfield.widget, + db_field.remote_field, + self.admin_site, + **wrapper_kwargs, + ) + + return formfield + + # If we've got overrides for the formfield defined, use 'em. **kwargs + # passed to formfield_for_dbfield override the defaults. + for klass in db_field.__class__.mro(): + if klass in self.formfield_overrides: + kwargs = {**copy.deepcopy(self.formfield_overrides[klass]), **kwargs} + return db_field.formfield(**kwargs) + + # For any other type of field, just call its formfield() method. + return db_field.formfield(**kwargs) + + def formfield_for_choice_field(self, db_field, request, **kwargs): + """ + Get a form Field for a database Field that has declared choices. + """ + # If the field is named as a radio_field, use a RadioSelect + if db_field.name in self.radio_fields: + # Avoid stomping on custom widget/choices arguments. + if "widget" not in kwargs: + kwargs["widget"] = widgets.AdminRadioSelect( + attrs={ + "class": get_ul_class(self.radio_fields[db_field.name]), + } + ) + if "choices" not in kwargs: + kwargs["choices"] = db_field.get_choices( + include_blank=db_field.blank, blank_choice=[("", _("None"))] + ) + return db_field.formfield(**kwargs) + + def get_field_queryset(self, db, db_field, request): + """ + If the ModelAdmin specifies ordering, the queryset should respect that + ordering. Otherwise don't specify the queryset, let the field decide + (return None in that case). + """ + related_admin = self.admin_site._registry.get(db_field.remote_field.model) + if related_admin is not None: + ordering = related_admin.get_ordering(request) + if ordering is not None and ordering != (): + return db_field.remote_field.model._default_manager.using(db).order_by( + *ordering + ) + return None + + def formfield_for_foreignkey(self, db_field, request, **kwargs): + """ + Get a form Field for a ForeignKey. + """ + db = kwargs.get("using") + + if "widget" not in kwargs: + if db_field.name in self.get_autocomplete_fields(request): + kwargs["widget"] = AutocompleteSelect( + db_field, self.admin_site, using=db + ) + elif db_field.name in self.raw_id_fields: + kwargs["widget"] = widgets.ForeignKeyRawIdWidget( + db_field.remote_field, self.admin_site, using=db + ) + elif db_field.name in self.radio_fields: + kwargs["widget"] = widgets.AdminRadioSelect( + attrs={ + "class": get_ul_class(self.radio_fields[db_field.name]), + } + ) + kwargs["empty_label"] = ( + kwargs.get("empty_label", _("None")) if db_field.blank else None + ) + + if "queryset" not in kwargs: + queryset = self.get_field_queryset(db, db_field, request) + if queryset is not None: + kwargs["queryset"] = queryset + + return db_field.formfield(**kwargs) + + def formfield_for_manytomany(self, db_field, request, **kwargs): + """ + Get a form Field for a ManyToManyField. + """ + # If it uses an intermediary model that isn't auto created, don't show + # a field in admin. + if not db_field.remote_field.through._meta.auto_created: + return None + db = kwargs.get("using") + + if "widget" not in kwargs: + autocomplete_fields = self.get_autocomplete_fields(request) + if db_field.name in autocomplete_fields: + kwargs["widget"] = AutocompleteSelectMultiple( + db_field, + self.admin_site, + using=db, + ) + elif db_field.name in self.raw_id_fields: + kwargs["widget"] = widgets.ManyToManyRawIdWidget( + db_field.remote_field, + self.admin_site, + using=db, + ) + elif db_field.name in [*self.filter_vertical, *self.filter_horizontal]: + kwargs["widget"] = widgets.FilteredSelectMultiple( + db_field.verbose_name, db_field.name in self.filter_vertical + ) + if "queryset" not in kwargs: + queryset = self.get_field_queryset(db, db_field, request) + if queryset is not None: + kwargs["queryset"] = queryset + + form_field = db_field.formfield(**kwargs) + if ( + isinstance(form_field.widget, SelectMultiple) + and form_field.widget.allow_multiple_selected + and not isinstance( + form_field.widget, (CheckboxSelectMultiple, AutocompleteSelectMultiple) + ) + ): + msg = _( + "Hold down “Control”, or “Command” on a Mac, to select more than one." + ) + help_text = form_field.help_text + form_field.help_text = ( + format_lazy("{} {}", help_text, msg) if help_text else msg + ) + return form_field + + def get_autocomplete_fields(self, request): + """ + Return a list of ForeignKey and/or ManyToMany fields which should use + an autocomplete widget. + """ + return self.autocomplete_fields + + def get_view_on_site_url(self, obj=None): + if obj is None or not self.view_on_site: + return None + + if callable(self.view_on_site): + return self.view_on_site(obj) + elif hasattr(obj, "get_absolute_url"): + # use the ContentType lookup if view_on_site is True + return reverse( + "admin:view_on_site", + kwargs={ + "content_type_id": get_content_type_for_model(obj).pk, + "object_id": obj.pk, + }, + current_app=self.admin_site.name, + ) + + def get_empty_value_display(self): + """ + Return the empty_value_display set on ModelAdmin or AdminSite. + """ + try: + return mark_safe(self.empty_value_display) + except AttributeError: + return mark_safe(self.admin_site.empty_value_display) + + def get_exclude(self, request, obj=None): + """ + Hook for specifying exclude. + """ + return self.exclude + + def get_fields(self, request, obj=None): + """ + Hook for specifying fields. + """ + if self.fields: + return self.fields + # _get_form_for_get_fields() is implemented in subclasses. + form = self._get_form_for_get_fields(request, obj) + return [*form.base_fields, *self.get_readonly_fields(request, obj)] + + def get_fieldsets(self, request, obj=None): + """ + Hook for specifying fieldsets. + """ + if self.fieldsets: + return self.fieldsets + return [(None, {"fields": self.get_fields(request, obj)})] + + def get_inlines(self, request, obj): + """Hook for specifying custom inlines.""" + return self.inlines + + def get_ordering(self, request): + """ + Hook for specifying field ordering. + """ + return self.ordering or () # otherwise we might try to *None, which is bad ;) + + def get_readonly_fields(self, request, obj=None): + """ + Hook for specifying custom readonly fields. + """ + return self.readonly_fields + + def get_prepopulated_fields(self, request, obj=None): + """ + Hook for specifying custom prepopulated fields. + """ + return self.prepopulated_fields + + def get_queryset(self, request): + """ + Return a QuerySet of all model instances that can be edited by the + admin site. This is used by changelist_view. + """ + qs = self.model._default_manager.get_queryset() + # TODO: this should be handled by some parameter to the ChangeList. + ordering = self.get_ordering(request) + if ordering: + qs = qs.order_by(*ordering) + return qs + + def get_sortable_by(self, request): + """Hook for specifying which fields can be sorted in the changelist.""" + return ( + self.sortable_by + if self.sortable_by is not None + else self.get_list_display(request) + ) + + def lookup_allowed(self, lookup, value): + from django.contrib.admin.filters import SimpleListFilter + + model = self.model + # Check FKey lookups that are allowed, so that popups produced by + # ForeignKeyRawIdWidget, on the basis of ForeignKey.limit_choices_to, + # are allowed to work. + for fk_lookup in model._meta.related_fkey_lookups: + # As ``limit_choices_to`` can be a callable, invoke it here. + if callable(fk_lookup): + fk_lookup = fk_lookup() + if (lookup, value) in widgets.url_params_from_lookup_dict( + fk_lookup + ).items(): + return True + + relation_parts = [] + prev_field = None + for part in lookup.split(LOOKUP_SEP): + try: + field = model._meta.get_field(part) + except FieldDoesNotExist: + # Lookups on nonexistent fields are ok, since they're ignored + # later. + break + # It is allowed to filter on values that would be found from local + # model anyways. For example, if you filter on employee__department__id, + # then the id value would be found already from employee__department_id. + if not prev_field or ( + prev_field.is_relation + and field not in prev_field.path_infos[-1].target_fields + ): + relation_parts.append(part) + if not getattr(field, "path_infos", None): + # This is not a relational field, so further parts + # must be transforms. + break + prev_field = field + model = field.path_infos[-1].to_opts.model + + if len(relation_parts) <= 1: + # Either a local field filter, or no fields at all. + return True + valid_lookups = {self.date_hierarchy} + for filter_item in self.list_filter: + if isinstance(filter_item, type) and issubclass( + filter_item, SimpleListFilter + ): + valid_lookups.add(filter_item.parameter_name) + elif isinstance(filter_item, (list, tuple)): + valid_lookups.add(filter_item[0]) + else: + valid_lookups.add(filter_item) + + # Is it a valid relational lookup? + return not { + LOOKUP_SEP.join(relation_parts), + LOOKUP_SEP.join(relation_parts + [part]), + }.isdisjoint(valid_lookups) + + def to_field_allowed(self, request, to_field): + """ + Return True if the model associated with this admin should be + allowed to be referenced by the specified field. + """ + try: + field = self.opts.get_field(to_field) + except FieldDoesNotExist: + return False + + # Always allow referencing the primary key since it's already possible + # to get this information from the change view URL. + if field.primary_key: + return True + + # Allow reverse relationships to models defining m2m fields if they + # target the specified field. + for many_to_many in self.opts.many_to_many: + if many_to_many.m2m_target_field_name() == to_field: + return True + + # Make sure at least one of the models registered for this site + # references this field through a FK or a M2M relationship. + registered_models = set() + for model, admin in self.admin_site._registry.items(): + registered_models.add(model) + for inline in admin.inlines: + registered_models.add(inline.model) + + related_objects = ( + f + for f in self.opts.get_fields(include_hidden=True) + if (f.auto_created and not f.concrete) + ) + for related_object in related_objects: + related_model = related_object.related_model + remote_field = related_object.field.remote_field + if ( + any(issubclass(model, related_model) for model in registered_models) + and hasattr(remote_field, "get_related_field") + and remote_field.get_related_field() == field + ): + return True + + return False + + def has_add_permission(self, request): + """ + Return True if the given request has permission to add an object. + Can be overridden by the user in subclasses. + """ + opts = self.opts + codename = get_permission_codename("add", opts) + return request.user.has_perm("%s.%s" % (opts.app_label, codename)) + + def has_change_permission(self, request, obj=None): + """ + Return True if the given request has permission to change the given + Django model instance, the default implementation doesn't examine the + `obj` parameter. + + Can be overridden by the user in subclasses. In such case it should + return True if the given request has permission to change the `obj` + model instance. If `obj` is None, this should return True if the given + request has permission to change *any* object of the given type. + """ + opts = self.opts + codename = get_permission_codename("change", opts) + return request.user.has_perm("%s.%s" % (opts.app_label, codename)) + + def has_delete_permission(self, request, obj=None): + """ + Return True if the given request has permission to delete the given + Django model instance, the default implementation doesn't examine the + `obj` parameter. + + Can be overridden by the user in subclasses. In such case it should + return True if the given request has permission to delete the `obj` + model instance. If `obj` is None, this should return True if the given + request has permission to delete *any* object of the given type. + """ + opts = self.opts + codename = get_permission_codename("delete", opts) + return request.user.has_perm("%s.%s" % (opts.app_label, codename)) + + def has_view_permission(self, request, obj=None): + """ + Return True if the given request has permission to view the given + Django model instance. The default implementation doesn't examine the + `obj` parameter. + + If overridden by the user in subclasses, it should return True if the + given request has permission to view the `obj` model instance. If `obj` + is None, it should return True if the request has permission to view + any object of the given type. + """ + opts = self.opts + codename_view = get_permission_codename("view", opts) + codename_change = get_permission_codename("change", opts) + return request.user.has_perm( + "%s.%s" % (opts.app_label, codename_view) + ) or request.user.has_perm("%s.%s" % (opts.app_label, codename_change)) + + def has_view_or_change_permission(self, request, obj=None): + return self.has_view_permission(request, obj) or self.has_change_permission( + request, obj + ) + + def has_module_permission(self, request): + """ + Return True if the given request has any permission in the given + app label. + + Can be overridden by the user in subclasses. In such case it should + return True if the given request has permission to view the module on + the admin index page and access the module's index page. Overriding it + does not restrict access to the add, change or delete views. Use + `ModelAdmin.has_(add|change|delete)_permission` for that. + """ + return request.user.has_module_perms(self.opts.app_label) + + +class ModelAdmin(BaseModelAdmin): + """Encapsulate all admin options and functionality for a given model.""" + + list_display = ("__str__",) + list_display_links = () + list_filter = () + list_select_related = False + list_per_page = 100 + list_max_show_all = 200 + list_editable = () + search_fields = () + search_help_text = None + date_hierarchy = None + save_as = False + save_as_continue = True + save_on_top = False + paginator = Paginator + preserve_filters = True + inlines = () + + # Custom templates (designed to be over-ridden in subclasses) + add_form_template = None + change_form_template = None + change_list_template = None + delete_confirmation_template = None + delete_selected_confirmation_template = None + object_history_template = None + popup_response_template = None + + # Actions + actions = () + action_form = helpers.ActionForm + actions_on_top = True + actions_on_bottom = False + actions_selection_counter = True + checks_class = ModelAdminChecks + + def __init__(self, model, admin_site): + self.model = model + self.opts = model._meta + self.admin_site = admin_site + super().__init__() + + def __str__(self): + return "%s.%s" % (self.opts.app_label, self.__class__.__name__) + + def __repr__(self): + return ( + f"<{self.__class__.__qualname__}: model={self.model.__qualname__} " + f"site={self.admin_site!r}>" + ) + + def get_inline_instances(self, request, obj=None): + inline_instances = [] + for inline_class in self.get_inlines(request, obj): + inline = inline_class(self.model, self.admin_site) + if request: + if not ( + inline.has_view_or_change_permission(request, obj) + or inline.has_add_permission(request, obj) + or inline.has_delete_permission(request, obj) + ): + continue + if not inline.has_add_permission(request, obj): + inline.max_num = 0 + inline_instances.append(inline) + + return inline_instances + + def get_urls(self): + from django.urls import path + + def wrap(view): + def wrapper(*args, **kwargs): + return self.admin_site.admin_view(view)(*args, **kwargs) + + wrapper.model_admin = self + return update_wrapper(wrapper, view) + + info = self.opts.app_label, self.opts.model_name + + return [ + path("", wrap(self.changelist_view), name="%s_%s_changelist" % info), + path("add/", wrap(self.add_view), name="%s_%s_add" % info), + path( + "/history/", + wrap(self.history_view), + name="%s_%s_history" % info, + ), + path( + "/delete/", + wrap(self.delete_view), + name="%s_%s_delete" % info, + ), + path( + "/change/", + wrap(self.change_view), + name="%s_%s_change" % info, + ), + # For backwards compatibility (was the change url before 1.9) + path( + "/", + wrap( + RedirectView.as_view( + pattern_name="%s:%s_%s_change" + % ((self.admin_site.name,) + info) + ) + ), + ), + ] + + @property + def urls(self): + return self.get_urls() + + @property + def media(self): + extra = "" if settings.DEBUG else ".min" + js = [ + "vendor/jquery/jquery%s.js" % extra, + "jquery.init.js", + "core.js", + "admin/RelatedObjectLookups.js", + "actions.js", + "urlify.js", + "prepopulate.js", + "vendor/xregexp/xregexp%s.js" % extra, + ] + return forms.Media(js=["admin/js/%s" % url for url in js]) + + def get_model_perms(self, request): + """ + Return a dict of all perms for this model. This dict has the keys + ``add``, ``change``, ``delete``, and ``view`` mapping to the True/False + for each of those actions. + """ + return { + "add": self.has_add_permission(request), + "change": self.has_change_permission(request), + "delete": self.has_delete_permission(request), + "view": self.has_view_permission(request), + } + + def _get_form_for_get_fields(self, request, obj): + return self.get_form(request, obj, fields=None) + + def get_form(self, request, obj=None, change=False, **kwargs): + """ + Return a Form class for use in the admin add view. This is used by + add_view and change_view. + """ + if "fields" in kwargs: + fields = kwargs.pop("fields") + else: + fields = flatten_fieldsets(self.get_fieldsets(request, obj)) + excluded = self.get_exclude(request, obj) + exclude = [] if excluded is None else list(excluded) + readonly_fields = self.get_readonly_fields(request, obj) + exclude.extend(readonly_fields) + # Exclude all fields if it's a change form and the user doesn't have + # the change permission. + if ( + change + and hasattr(request, "user") + and not self.has_change_permission(request, obj) + ): + exclude.extend(fields) + if excluded is None and hasattr(self.form, "_meta") and self.form._meta.exclude: + # Take the custom ModelForm's Meta.exclude into account only if the + # ModelAdmin doesn't define its own. + exclude.extend(self.form._meta.exclude) + # if exclude is an empty list we pass None to be consistent with the + # default on modelform_factory + exclude = exclude or None + + # Remove declared form fields which are in readonly_fields. + new_attrs = dict.fromkeys( + f for f in readonly_fields if f in self.form.declared_fields + ) + form = type(self.form.__name__, (self.form,), new_attrs) + + defaults = { + "form": form, + "fields": fields, + "exclude": exclude, + "formfield_callback": partial(self.formfield_for_dbfield, request=request), + **kwargs, + } + + if defaults["fields"] is None and not modelform_defines_fields( + defaults["form"] + ): + defaults["fields"] = forms.ALL_FIELDS + + try: + return modelform_factory(self.model, **defaults) + except FieldError as e: + raise FieldError( + "%s. Check fields/fieldsets/exclude attributes of class %s." + % (e, self.__class__.__name__) + ) + + def get_changelist(self, request, **kwargs): + """ + Return the ChangeList class for use on the changelist page. + """ + from django.contrib.admin.views.main import ChangeList + + return ChangeList + + def get_changelist_instance(self, request): + """ + Return a `ChangeList` instance based on `request`. May raise + `IncorrectLookupParameters`. + """ + list_display = self.get_list_display(request) + list_display_links = self.get_list_display_links(request, list_display) + # Add the action checkboxes if any actions are available. + if self.get_actions(request): + list_display = ["action_checkbox", *list_display] + sortable_by = self.get_sortable_by(request) + ChangeList = self.get_changelist(request) + return ChangeList( + request, + self.model, + list_display, + list_display_links, + self.get_list_filter(request), + self.date_hierarchy, + self.get_search_fields(request), + self.get_list_select_related(request), + self.list_per_page, + self.list_max_show_all, + self.list_editable, + self, + sortable_by, + self.search_help_text, + ) + + def get_object(self, request, object_id, from_field=None): + """ + Return an instance matching the field and value provided, the primary + key is used if no field is provided. Return ``None`` if no match is + found or the object_id fails validation. + """ + queryset = self.get_queryset(request) + model = queryset.model + field = ( + model._meta.pk if from_field is None else model._meta.get_field(from_field) + ) + try: + object_id = field.to_python(object_id) + return queryset.get(**{field.name: object_id}) + except (model.DoesNotExist, ValidationError, ValueError): + return None + + def get_changelist_form(self, request, **kwargs): + """ + Return a Form class for use in the Formset on the changelist page. + """ + defaults = { + "formfield_callback": partial(self.formfield_for_dbfield, request=request), + **kwargs, + } + if defaults.get("fields") is None and not modelform_defines_fields( + defaults.get("form") + ): + defaults["fields"] = forms.ALL_FIELDS + + return modelform_factory(self.model, **defaults) + + def get_changelist_formset(self, request, **kwargs): + """ + Return a FormSet class for use on the changelist page if list_editable + is used. + """ + defaults = { + "formfield_callback": partial(self.formfield_for_dbfield, request=request), + **kwargs, + } + return modelformset_factory( + self.model, + self.get_changelist_form(request), + extra=0, + fields=self.list_editable, + **defaults, + ) + + def get_formsets_with_inlines(self, request, obj=None): + """ + Yield formsets and the corresponding inlines. + """ + for inline in self.get_inline_instances(request, obj): + yield inline.get_formset(request, obj), inline + + def get_paginator( + self, request, queryset, per_page, orphans=0, allow_empty_first_page=True + ): + return self.paginator(queryset, per_page, orphans, allow_empty_first_page) + + def log_addition(self, request, obj, message): + """ + Log that an object has been successfully added. + + The default implementation creates an admin LogEntry object. + """ + from django.contrib.admin.models import ADDITION, LogEntry + + return LogEntry.objects.log_action( + user_id=request.user.pk, + content_type_id=get_content_type_for_model(obj).pk, + object_id=obj.pk, + object_repr=str(obj), + action_flag=ADDITION, + change_message=message, + ) + + def log_change(self, request, obj, message): + """ + Log that an object has been successfully changed. + + The default implementation creates an admin LogEntry object. + """ + from django.contrib.admin.models import CHANGE, LogEntry + + return LogEntry.objects.log_action( + user_id=request.user.pk, + content_type_id=get_content_type_for_model(obj).pk, + object_id=obj.pk, + object_repr=str(obj), + action_flag=CHANGE, + change_message=message, + ) + + def log_deletion(self, request, obj, object_repr): + """ + Log that an object will be deleted. Note that this method must be + called before the deletion. + + The default implementation creates an admin LogEntry object. + """ + from django.contrib.admin.models import DELETION, LogEntry + + return LogEntry.objects.log_action( + user_id=request.user.pk, + content_type_id=get_content_type_for_model(obj).pk, + object_id=obj.pk, + object_repr=object_repr, + action_flag=DELETION, + ) + + @display(description=mark_safe('')) + def action_checkbox(self, obj): + """ + A list_display column containing a checkbox widget. + """ + return helpers.checkbox.render(helpers.ACTION_CHECKBOX_NAME, str(obj.pk)) + + @staticmethod + def _get_action_description(func, name): + return getattr(func, "short_description", capfirst(name.replace("_", " "))) + + def _get_base_actions(self): + """Return the list of actions, prior to any request-based filtering.""" + actions = [] + base_actions = (self.get_action(action) for action in self.actions or []) + # get_action might have returned None, so filter any of those out. + base_actions = [action for action in base_actions if action] + base_action_names = {name for _, name, _ in base_actions} + + # Gather actions from the admin site first + for name, func in self.admin_site.actions: + if name in base_action_names: + continue + description = self._get_action_description(func, name) + actions.append((func, name, description)) + # Add actions from this ModelAdmin. + actions.extend(base_actions) + return actions + + def _filter_actions_by_permissions(self, request, actions): + """Filter out any actions that the user doesn't have access to.""" + filtered_actions = [] + for action in actions: + callable = action[0] + if not hasattr(callable, "allowed_permissions"): + filtered_actions.append(action) + continue + permission_checks = ( + getattr(self, "has_%s_permission" % permission) + for permission in callable.allowed_permissions + ) + if any(has_permission(request) for has_permission in permission_checks): + filtered_actions.append(action) + return filtered_actions + + def get_actions(self, request): + """ + Return a dictionary mapping the names of all actions for this + ModelAdmin to a tuple of (callable, name, description) for each action. + """ + # If self.actions is set to None that means actions are disabled on + # this page. + if self.actions is None or IS_POPUP_VAR in request.GET: + return {} + actions = self._filter_actions_by_permissions(request, self._get_base_actions()) + return {name: (func, name, desc) for func, name, desc in actions} + + def get_action_choices(self, request, default_choices=models.BLANK_CHOICE_DASH): + """ + Return a list of choices for use in a form object. Each choice is a + tuple (name, description). + """ + choices = [] + default_choices + for func, name, description in self.get_actions(request).values(): + choice = (name, description % model_format_dict(self.opts)) + choices.append(choice) + return choices + + def get_action(self, action): + """ + Return a given action from a parameter, which can either be a callable, + or the name of a method on the ModelAdmin. Return is a tuple of + (callable, name, description). + """ + # If the action is a callable, just use it. + if callable(action): + func = action + action = action.__name__ + + # Next, look for a method. Grab it off self.__class__ to get an unbound + # method instead of a bound one; this ensures that the calling + # conventions are the same for functions and methods. + elif hasattr(self.__class__, action): + func = getattr(self.__class__, action) + + # Finally, look for a named method on the admin site + else: + try: + func = self.admin_site.get_action(action) + except KeyError: + return None + + description = self._get_action_description(func, action) + return func, action, description + + def get_list_display(self, request): + """ + Return a sequence containing the fields to be displayed on the + changelist. + """ + return self.list_display + + def get_list_display_links(self, request, list_display): + """ + Return a sequence containing the fields to be displayed as links + on the changelist. The list_display parameter is the list of fields + returned by get_list_display(). + """ + if ( + self.list_display_links + or self.list_display_links is None + or not list_display + ): + return self.list_display_links + else: + # Use only the first item in list_display as link + return list(list_display)[:1] + + def get_list_filter(self, request): + """ + Return a sequence containing the fields to be displayed as filters in + the right sidebar of the changelist page. + """ + return self.list_filter + + def get_list_select_related(self, request): + """ + Return a list of fields to add to the select_related() part of the + changelist items query. + """ + return self.list_select_related + + def get_search_fields(self, request): + """ + Return a sequence containing the fields to be searched whenever + somebody submits a search query. + """ + return self.search_fields + + def get_search_results(self, request, queryset, search_term): + """ + Return a tuple containing a queryset to implement the search + and a boolean indicating if the results may contain duplicates. + """ + + # Apply keyword searches. + def construct_search(field_name): + if field_name.startswith("^"): + return "%s__istartswith" % field_name[1:] + elif field_name.startswith("="): + return "%s__iexact" % field_name[1:] + elif field_name.startswith("@"): + return "%s__search" % field_name[1:] + # Use field_name if it includes a lookup. + opts = queryset.model._meta + lookup_fields = field_name.split(LOOKUP_SEP) + # Go through the fields, following all relations. + prev_field = None + for path_part in lookup_fields: + if path_part == "pk": + path_part = opts.pk.name + try: + field = opts.get_field(path_part) + except FieldDoesNotExist: + # Use valid query lookups. + if prev_field and prev_field.get_lookup(path_part): + return field_name + else: + prev_field = field + if hasattr(field, "path_infos"): + # Update opts to follow the relation. + opts = field.path_infos[-1].to_opts + # Otherwise, use the field with icontains. + return "%s__icontains" % field_name + + may_have_duplicates = False + search_fields = self.get_search_fields(request) + if search_fields and search_term: + orm_lookups = [ + construct_search(str(search_field)) for search_field in search_fields + ] + term_queries = [] + for bit in smart_split(search_term): + if bit.startswith(('"', "'")) and bit[0] == bit[-1]: + bit = unescape_string_literal(bit) + or_queries = models.Q.create( + [(orm_lookup, bit) for orm_lookup in orm_lookups], + connector=models.Q.OR, + ) + term_queries.append(or_queries) + queryset = queryset.filter(models.Q.create(term_queries)) + may_have_duplicates |= any( + lookup_spawns_duplicates(self.opts, search_spec) + for search_spec in orm_lookups + ) + return queryset, may_have_duplicates + + def get_preserved_filters(self, request): + """ + Return the preserved filters querystring. + """ + match = request.resolver_match + if self.preserve_filters and match: + current_url = "%s:%s" % (match.app_name, match.url_name) + changelist_url = "admin:%s_%s_changelist" % ( + self.opts.app_label, + self.opts.model_name, + ) + if current_url == changelist_url: + preserved_filters = request.GET.urlencode() + else: + preserved_filters = request.GET.get("_changelist_filters") + + if preserved_filters: + return urlencode({"_changelist_filters": preserved_filters}) + return "" + + def construct_change_message(self, request, form, formsets, add=False): + """ + Construct a JSON structure describing changes from a changed object. + """ + return construct_change_message(form, formsets, add) + + def message_user( + self, request, message, level=messages.INFO, extra_tags="", fail_silently=False + ): + """ + Send a message to the user. The default implementation + posts a message using the django.contrib.messages backend. + + Exposes almost the same API as messages.add_message(), but accepts the + positional arguments in a different order to maintain backwards + compatibility. For convenience, it accepts the `level` argument as + a string rather than the usual level number. + """ + if not isinstance(level, int): + # attempt to get the level if passed a string + try: + level = getattr(messages.constants, level.upper()) + except AttributeError: + levels = messages.constants.DEFAULT_TAGS.values() + levels_repr = ", ".join("`%s`" % level for level in levels) + raise ValueError( + "Bad message level string: `%s`. Possible values are: %s" + % (level, levels_repr) + ) + + messages.add_message( + request, level, message, extra_tags=extra_tags, fail_silently=fail_silently + ) + + def save_form(self, request, form, change): + """ + Given a ModelForm return an unsaved instance. ``change`` is True if + the object is being changed, and False if it's being added. + """ + return form.save(commit=False) + + def save_model(self, request, obj, form, change): + """ + Given a model instance save it to the database. + """ + obj.save() + + def delete_model(self, request, obj): + """ + Given a model instance delete it from the database. + """ + obj.delete() + + def delete_queryset(self, request, queryset): + """Given a queryset, delete it from the database.""" + queryset.delete() + + def save_formset(self, request, form, formset, change): + """ + Given an inline formset save it to the database. + """ + formset.save() + + def save_related(self, request, form, formsets, change): + """ + Given the ``HttpRequest``, the parent ``ModelForm`` instance, the + list of inline formsets and a boolean value based on whether the + parent is being added or changed, save the related objects to the + database. Note that at this point save_form() and save_model() have + already been called. + """ + form.save_m2m() + for formset in formsets: + self.save_formset(request, form, formset, change=change) + + def render_change_form( + self, request, context, add=False, change=False, form_url="", obj=None + ): + app_label = self.opts.app_label + preserved_filters = self.get_preserved_filters(request) + form_url = add_preserved_filters( + {"preserved_filters": preserved_filters, "opts": self.opts}, form_url + ) + view_on_site_url = self.get_view_on_site_url(obj) + has_editable_inline_admin_formsets = False + for inline in context["inline_admin_formsets"]: + if ( + inline.has_add_permission + or inline.has_change_permission + or inline.has_delete_permission + ): + has_editable_inline_admin_formsets = True + break + context.update( + { + "add": add, + "change": change, + "has_view_permission": self.has_view_permission(request, obj), + "has_add_permission": self.has_add_permission(request), + "has_change_permission": self.has_change_permission(request, obj), + "has_delete_permission": self.has_delete_permission(request, obj), + "has_editable_inline_admin_formsets": ( + has_editable_inline_admin_formsets + ), + "has_file_field": context["adminform"].form.is_multipart() + or any( + admin_formset.formset.is_multipart() + for admin_formset in context["inline_admin_formsets"] + ), + "has_absolute_url": view_on_site_url is not None, + "absolute_url": view_on_site_url, + "form_url": form_url, + "opts": self.opts, + "content_type_id": get_content_type_for_model(self.model).pk, + "save_as": self.save_as, + "save_on_top": self.save_on_top, + "to_field_var": TO_FIELD_VAR, + "is_popup_var": IS_POPUP_VAR, + "app_label": app_label, + } + ) + if add and self.add_form_template is not None: + form_template = self.add_form_template + else: + form_template = self.change_form_template + + request.current_app = self.admin_site.name + + return TemplateResponse( + request, + form_template + or [ + "admin/%s/%s/change_form.html" % (app_label, self.opts.model_name), + "admin/%s/change_form.html" % app_label, + "admin/change_form.html", + ], + context, + ) + + def response_add(self, request, obj, post_url_continue=None): + """ + Determine the HttpResponse for the add_view stage. + """ + opts = obj._meta + preserved_filters = self.get_preserved_filters(request) + obj_url = reverse( + "admin:%s_%s_change" % (opts.app_label, opts.model_name), + args=(quote(obj.pk),), + current_app=self.admin_site.name, + ) + # Add a link to the object's change form if the user can edit the obj. + if self.has_change_permission(request, obj): + obj_repr = format_html('{}', urlquote(obj_url), obj) + else: + obj_repr = str(obj) + msg_dict = { + "name": opts.verbose_name, + "obj": obj_repr, + } + # Here, we distinguish between different save types by checking for + # the presence of keys in request.POST. + + if IS_POPUP_VAR in request.POST: + to_field = request.POST.get(TO_FIELD_VAR) + if to_field: + attr = str(to_field) + else: + attr = obj._meta.pk.attname + value = obj.serializable_value(attr) + popup_response_data = json.dumps( + { + "value": str(value), + "obj": str(obj), + } + ) + return TemplateResponse( + request, + self.popup_response_template + or [ + "admin/%s/%s/popup_response.html" + % (opts.app_label, opts.model_name), + "admin/%s/popup_response.html" % opts.app_label, + "admin/popup_response.html", + ], + { + "popup_response_data": popup_response_data, + }, + ) + + elif "_continue" in request.POST or ( + # Redirecting after "Save as new". + "_saveasnew" in request.POST + and self.save_as_continue + and self.has_change_permission(request, obj) + ): + msg = _("The {name} “{obj}” was added successfully.") + if self.has_change_permission(request, obj): + msg += " " + _("You may edit it again below.") + self.message_user(request, format_html(msg, **msg_dict), messages.SUCCESS) + if post_url_continue is None: + post_url_continue = obj_url + post_url_continue = add_preserved_filters( + {"preserved_filters": preserved_filters, "opts": opts}, + post_url_continue, + ) + return HttpResponseRedirect(post_url_continue) + + elif "_addanother" in request.POST: + msg = format_html( + _( + "The {name} “{obj}” was added successfully. You may add another " + "{name} below." + ), + **msg_dict, + ) + self.message_user(request, msg, messages.SUCCESS) + redirect_url = request.path + redirect_url = add_preserved_filters( + {"preserved_filters": preserved_filters, "opts": opts}, redirect_url + ) + return HttpResponseRedirect(redirect_url) + + else: + msg = format_html( + _("The {name} “{obj}” was added successfully."), **msg_dict + ) + self.message_user(request, msg, messages.SUCCESS) + return self.response_post_save_add(request, obj) + + def response_change(self, request, obj): + """ + Determine the HttpResponse for the change_view stage. + """ + + if IS_POPUP_VAR in request.POST: + opts = obj._meta + to_field = request.POST.get(TO_FIELD_VAR) + attr = str(to_field) if to_field else opts.pk.attname + value = request.resolver_match.kwargs["object_id"] + new_value = obj.serializable_value(attr) + popup_response_data = json.dumps( + { + "action": "change", + "value": str(value), + "obj": str(obj), + "new_value": str(new_value), + } + ) + return TemplateResponse( + request, + self.popup_response_template + or [ + "admin/%s/%s/popup_response.html" + % (opts.app_label, opts.model_name), + "admin/%s/popup_response.html" % opts.app_label, + "admin/popup_response.html", + ], + { + "popup_response_data": popup_response_data, + }, + ) + + opts = self.opts + preserved_filters = self.get_preserved_filters(request) + + msg_dict = { + "name": opts.verbose_name, + "obj": format_html('{}', urlquote(request.path), obj), + } + if "_continue" in request.POST: + msg = format_html( + _( + "The {name} “{obj}” was changed successfully. You may edit it " + "again below." + ), + **msg_dict, + ) + self.message_user(request, msg, messages.SUCCESS) + redirect_url = request.path + redirect_url = add_preserved_filters( + {"preserved_filters": preserved_filters, "opts": opts}, redirect_url + ) + return HttpResponseRedirect(redirect_url) + + elif "_saveasnew" in request.POST: + msg = format_html( + _( + "The {name} “{obj}” was added successfully. You may edit it again " + "below." + ), + **msg_dict, + ) + self.message_user(request, msg, messages.SUCCESS) + redirect_url = reverse( + "admin:%s_%s_change" % (opts.app_label, opts.model_name), + args=(obj.pk,), + current_app=self.admin_site.name, + ) + redirect_url = add_preserved_filters( + {"preserved_filters": preserved_filters, "opts": opts}, redirect_url + ) + return HttpResponseRedirect(redirect_url) + + elif "_addanother" in request.POST: + msg = format_html( + _( + "The {name} “{obj}” was changed successfully. You may add another " + "{name} below." + ), + **msg_dict, + ) + self.message_user(request, msg, messages.SUCCESS) + redirect_url = reverse( + "admin:%s_%s_add" % (opts.app_label, opts.model_name), + current_app=self.admin_site.name, + ) + redirect_url = add_preserved_filters( + {"preserved_filters": preserved_filters, "opts": opts}, redirect_url + ) + return HttpResponseRedirect(redirect_url) + + else: + msg = format_html( + _("The {name} “{obj}” was changed successfully."), **msg_dict + ) + self.message_user(request, msg, messages.SUCCESS) + return self.response_post_save_change(request, obj) + + def _response_post_save(self, request, obj): + if self.has_view_or_change_permission(request): + post_url = reverse( + "admin:%s_%s_changelist" % (self.opts.app_label, self.opts.model_name), + current_app=self.admin_site.name, + ) + preserved_filters = self.get_preserved_filters(request) + post_url = add_preserved_filters( + {"preserved_filters": preserved_filters, "opts": self.opts}, post_url + ) + else: + post_url = reverse("admin:index", current_app=self.admin_site.name) + return HttpResponseRedirect(post_url) + + def response_post_save_add(self, request, obj): + """ + Figure out where to redirect after the 'Save' button has been pressed + when adding a new object. + """ + return self._response_post_save(request, obj) + + def response_post_save_change(self, request, obj): + """ + Figure out where to redirect after the 'Save' button has been pressed + when editing an existing object. + """ + return self._response_post_save(request, obj) + + def response_action(self, request, queryset): + """ + Handle an admin action. This is called if a request is POSTed to the + changelist; it returns an HttpResponse if the action was handled, and + None otherwise. + """ + + # There can be multiple action forms on the page (at the top + # and bottom of the change list, for example). Get the action + # whose button was pushed. + try: + action_index = int(request.POST.get("index", 0)) + except ValueError: + action_index = 0 + + # Construct the action form. + data = request.POST.copy() + data.pop(helpers.ACTION_CHECKBOX_NAME, None) + data.pop("index", None) + + # Use the action whose button was pushed + try: + data.update({"action": data.getlist("action")[action_index]}) + except IndexError: + # If we didn't get an action from the chosen form that's invalid + # POST data, so by deleting action it'll fail the validation check + # below. So no need to do anything here + pass + + action_form = self.action_form(data, auto_id=None) + action_form.fields["action"].choices = self.get_action_choices(request) + + # If the form's valid we can handle the action. + if action_form.is_valid(): + action = action_form.cleaned_data["action"] + select_across = action_form.cleaned_data["select_across"] + func = self.get_actions(request)[action][0] + + # Get the list of selected PKs. If nothing's selected, we can't + # perform an action on it, so bail. Except we want to perform + # the action explicitly on all objects. + selected = request.POST.getlist(helpers.ACTION_CHECKBOX_NAME) + if not selected and not select_across: + # Reminder that something needs to be selected or nothing will happen + msg = _( + "Items must be selected in order to perform " + "actions on them. No items have been changed." + ) + self.message_user(request, msg, messages.WARNING) + return None + + if not select_across: + # Perform the action only on the selected objects + queryset = queryset.filter(pk__in=selected) + + response = func(self, request, queryset) + + # Actions may return an HttpResponse-like object, which will be + # used as the response from the POST. If not, we'll be a good + # little HTTP citizen and redirect back to the changelist page. + if isinstance(response, HttpResponseBase): + return response + else: + return HttpResponseRedirect(request.get_full_path()) + else: + msg = _("No action selected.") + self.message_user(request, msg, messages.WARNING) + return None + + def response_delete(self, request, obj_display, obj_id): + """ + Determine the HttpResponse for the delete_view stage. + """ + if IS_POPUP_VAR in request.POST: + popup_response_data = json.dumps( + { + "action": "delete", + "value": str(obj_id), + } + ) + return TemplateResponse( + request, + self.popup_response_template + or [ + "admin/%s/%s/popup_response.html" + % (self.opts.app_label, self.opts.model_name), + "admin/%s/popup_response.html" % self.opts.app_label, + "admin/popup_response.html", + ], + { + "popup_response_data": popup_response_data, + }, + ) + + self.message_user( + request, + _("The %(name)s “%(obj)s” was deleted successfully.") + % { + "name": self.opts.verbose_name, + "obj": obj_display, + }, + messages.SUCCESS, + ) + + if self.has_change_permission(request, None): + post_url = reverse( + "admin:%s_%s_changelist" % (self.opts.app_label, self.opts.model_name), + current_app=self.admin_site.name, + ) + preserved_filters = self.get_preserved_filters(request) + post_url = add_preserved_filters( + {"preserved_filters": preserved_filters, "opts": self.opts}, post_url + ) + else: + post_url = reverse("admin:index", current_app=self.admin_site.name) + return HttpResponseRedirect(post_url) + + def render_delete_form(self, request, context): + app_label = self.opts.app_label + + request.current_app = self.admin_site.name + context.update( + to_field_var=TO_FIELD_VAR, + is_popup_var=IS_POPUP_VAR, + media=self.media, + ) + + return TemplateResponse( + request, + self.delete_confirmation_template + or [ + "admin/{}/{}/delete_confirmation.html".format( + app_label, self.opts.model_name + ), + "admin/{}/delete_confirmation.html".format(app_label), + "admin/delete_confirmation.html", + ], + context, + ) + + def get_inline_formsets(self, request, formsets, inline_instances, obj=None): + # Edit permissions on parent model are required for editable inlines. + can_edit_parent = ( + self.has_change_permission(request, obj) + if obj + else self.has_add_permission(request) + ) + inline_admin_formsets = [] + for inline, formset in zip(inline_instances, formsets): + fieldsets = list(inline.get_fieldsets(request, obj)) + readonly = list(inline.get_readonly_fields(request, obj)) + if can_edit_parent: + has_add_permission = inline.has_add_permission(request, obj) + has_change_permission = inline.has_change_permission(request, obj) + has_delete_permission = inline.has_delete_permission(request, obj) + else: + # Disable all edit-permissions, and override formset settings. + has_add_permission = ( + has_change_permission + ) = has_delete_permission = False + formset.extra = formset.max_num = 0 + has_view_permission = inline.has_view_permission(request, obj) + prepopulated = dict(inline.get_prepopulated_fields(request, obj)) + inline_admin_formset = helpers.InlineAdminFormSet( + inline, + formset, + fieldsets, + prepopulated, + readonly, + model_admin=self, + has_add_permission=has_add_permission, + has_change_permission=has_change_permission, + has_delete_permission=has_delete_permission, + has_view_permission=has_view_permission, + ) + inline_admin_formsets.append(inline_admin_formset) + return inline_admin_formsets + + def get_changeform_initial_data(self, request): + """ + Get the initial form data from the request's GET params. + """ + initial = dict(request.GET.items()) + for k in initial: + try: + f = self.opts.get_field(k) + except FieldDoesNotExist: + continue + # We have to special-case M2Ms as a list of comma-separated PKs. + if isinstance(f, models.ManyToManyField): + initial[k] = initial[k].split(",") + return initial + + def _get_obj_does_not_exist_redirect(self, request, opts, object_id): + """ + Create a message informing the user that the object doesn't exist + and return a redirect to the admin index page. + """ + msg = _("%(name)s with ID “%(key)s” doesn’t exist. Perhaps it was deleted?") % { + "name": opts.verbose_name, + "key": unquote(object_id), + } + self.message_user(request, msg, messages.WARNING) + url = reverse("admin:index", current_app=self.admin_site.name) + return HttpResponseRedirect(url) + + @csrf_protect_m + def changeform_view(self, request, object_id=None, form_url="", extra_context=None): + with transaction.atomic(using=router.db_for_write(self.model)): + return self._changeform_view(request, object_id, form_url, extra_context) + + def _changeform_view(self, request, object_id, form_url, extra_context): + to_field = request.POST.get(TO_FIELD_VAR, request.GET.get(TO_FIELD_VAR)) + if to_field and not self.to_field_allowed(request, to_field): + raise DisallowedModelAdminToField( + "The field %s cannot be referenced." % to_field + ) + + if request.method == "POST" and "_saveasnew" in request.POST: + object_id = None + + add = object_id is None + + if add: + if not self.has_add_permission(request): + raise PermissionDenied + obj = None + + else: + obj = self.get_object(request, unquote(object_id), to_field) + + if request.method == "POST": + if not self.has_change_permission(request, obj): + raise PermissionDenied + else: + if not self.has_view_or_change_permission(request, obj): + raise PermissionDenied + + if obj is None: + return self._get_obj_does_not_exist_redirect( + request, self.opts, object_id + ) + + fieldsets = self.get_fieldsets(request, obj) + ModelForm = self.get_form( + request, obj, change=not add, fields=flatten_fieldsets(fieldsets) + ) + if request.method == "POST": + form = ModelForm(request.POST, request.FILES, instance=obj) + formsets, inline_instances = self._create_formsets( + request, + form.instance, + change=not add, + ) + form_validated = form.is_valid() + if form_validated: + new_object = self.save_form(request, form, change=not add) + else: + new_object = form.instance + if all_valid(formsets) and form_validated: + self.save_model(request, new_object, form, not add) + self.save_related(request, form, formsets, not add) + change_message = self.construct_change_message( + request, form, formsets, add + ) + if add: + self.log_addition(request, new_object, change_message) + return self.response_add(request, new_object) + else: + self.log_change(request, new_object, change_message) + return self.response_change(request, new_object) + else: + form_validated = False + else: + if add: + initial = self.get_changeform_initial_data(request) + form = ModelForm(initial=initial) + formsets, inline_instances = self._create_formsets( + request, form.instance, change=False + ) + else: + form = ModelForm(instance=obj) + formsets, inline_instances = self._create_formsets( + request, obj, change=True + ) + + if not add and not self.has_change_permission(request, obj): + readonly_fields = flatten_fieldsets(fieldsets) + else: + readonly_fields = self.get_readonly_fields(request, obj) + admin_form = helpers.AdminForm( + form, + list(fieldsets), + # Clear prepopulated fields on a view-only form to avoid a crash. + self.get_prepopulated_fields(request, obj) + if add or self.has_change_permission(request, obj) + else {}, + readonly_fields, + model_admin=self, + ) + media = self.media + admin_form.media + + inline_formsets = self.get_inline_formsets( + request, formsets, inline_instances, obj + ) + for inline_formset in inline_formsets: + media += inline_formset.media + + if add: + title = _("Add %s") + elif self.has_change_permission(request, obj): + title = _("Change %s") + else: + title = _("View %s") + context = { + **self.admin_site.each_context(request), + "title": title % self.opts.verbose_name, + "subtitle": str(obj) if obj else None, + "adminform": admin_form, + "object_id": object_id, + "original": obj, + "is_popup": IS_POPUP_VAR in request.POST or IS_POPUP_VAR in request.GET, + "to_field": to_field, + "media": media, + "inline_admin_formsets": inline_formsets, + "errors": helpers.AdminErrorList(form, formsets), + "preserved_filters": self.get_preserved_filters(request), + } + + # Hide the "Save" and "Save and continue" buttons if "Save as New" was + # previously chosen to prevent the interface from getting confusing. + if ( + request.method == "POST" + and not form_validated + and "_saveasnew" in request.POST + ): + context["show_save"] = False + context["show_save_and_continue"] = False + # Use the change template instead of the add template. + add = False + + context.update(extra_context or {}) + + return self.render_change_form( + request, context, add=add, change=not add, obj=obj, form_url=form_url + ) + + def add_view(self, request, form_url="", extra_context=None): + return self.changeform_view(request, None, form_url, extra_context) + + def change_view(self, request, object_id, form_url="", extra_context=None): + return self.changeform_view(request, object_id, form_url, extra_context) + + def _get_edited_object_pks(self, request, prefix): + """Return POST data values of list_editable primary keys.""" + pk_pattern = re.compile( + r"{}-\d+-{}$".format(re.escape(prefix), self.opts.pk.name) + ) + return [value for key, value in request.POST.items() if pk_pattern.match(key)] + + def _get_list_editable_queryset(self, request, prefix): + """ + Based on POST data, return a queryset of the objects that were edited + via list_editable. + """ + object_pks = self._get_edited_object_pks(request, prefix) + queryset = self.get_queryset(request) + validate = queryset.model._meta.pk.to_python + try: + for pk in object_pks: + validate(pk) + except ValidationError: + # Disable the optimization if the POST data was tampered with. + return queryset + return queryset.filter(pk__in=object_pks) + + @csrf_protect_m + def changelist_view(self, request, extra_context=None): + """ + The 'change list' admin view for this model. + """ + from django.contrib.admin.views.main import ERROR_FLAG + + app_label = self.opts.app_label + if not self.has_view_or_change_permission(request): + raise PermissionDenied + + try: + cl = self.get_changelist_instance(request) + except IncorrectLookupParameters: + # Wacky lookup parameters were given, so redirect to the main + # changelist page, without parameters, and pass an 'invalid=1' + # parameter via the query string. If wacky parameters were given + # and the 'invalid=1' parameter was already in the query string, + # something is screwed up with the database, so display an error + # page. + if ERROR_FLAG in request.GET: + return SimpleTemplateResponse( + "admin/invalid_setup.html", + { + "title": _("Database error"), + }, + ) + return HttpResponseRedirect(request.path + "?" + ERROR_FLAG + "=1") + + # If the request was POSTed, this might be a bulk action or a bulk + # edit. Try to look up an action or confirmation first, but if this + # isn't an action the POST will fall through to the bulk edit check, + # below. + action_failed = False + selected = request.POST.getlist(helpers.ACTION_CHECKBOX_NAME) + + actions = self.get_actions(request) + # Actions with no confirmation + if ( + actions + and request.method == "POST" + and "index" in request.POST + and "_save" not in request.POST + ): + if selected: + response = self.response_action( + request, queryset=cl.get_queryset(request) + ) + if response: + return response + else: + action_failed = True + else: + msg = _( + "Items must be selected in order to perform " + "actions on them. No items have been changed." + ) + self.message_user(request, msg, messages.WARNING) + action_failed = True + + # Actions with confirmation + if ( + actions + and request.method == "POST" + and helpers.ACTION_CHECKBOX_NAME in request.POST + and "index" not in request.POST + and "_save" not in request.POST + ): + if selected: + response = self.response_action( + request, queryset=cl.get_queryset(request) + ) + if response: + return response + else: + action_failed = True + + if action_failed: + # Redirect back to the changelist page to avoid resubmitting the + # form if the user refreshes the browser or uses the "No, take + # me back" button on the action confirmation page. + return HttpResponseRedirect(request.get_full_path()) + + # If we're allowing changelist editing, we need to construct a formset + # for the changelist given all the fields to be edited. Then we'll + # use the formset to validate/process POSTed data. + formset = cl.formset = None + + # Handle POSTed bulk-edit data. + if request.method == "POST" and cl.list_editable and "_save" in request.POST: + if not self.has_change_permission(request): + raise PermissionDenied + FormSet = self.get_changelist_formset(request) + modified_objects = self._get_list_editable_queryset( + request, FormSet.get_default_prefix() + ) + formset = cl.formset = FormSet( + request.POST, request.FILES, queryset=modified_objects + ) + if formset.is_valid(): + changecount = 0 + with transaction.atomic(using=router.db_for_write(self.model)): + for form in formset.forms: + if form.has_changed(): + obj = self.save_form(request, form, change=True) + self.save_model(request, obj, form, change=True) + self.save_related(request, form, formsets=[], change=True) + change_msg = self.construct_change_message( + request, form, None + ) + self.log_change(request, obj, change_msg) + changecount += 1 + if changecount: + msg = ngettext( + "%(count)s %(name)s was changed successfully.", + "%(count)s %(name)s were changed successfully.", + changecount, + ) % { + "count": changecount, + "name": model_ngettext(self.opts, changecount), + } + self.message_user(request, msg, messages.SUCCESS) + + return HttpResponseRedirect(request.get_full_path()) + + # Handle GET -- construct a formset for display. + elif cl.list_editable and self.has_change_permission(request): + FormSet = self.get_changelist_formset(request) + formset = cl.formset = FormSet(queryset=cl.result_list) + + # Build the list of media to be used by the formset. + if formset: + media = self.media + formset.media + else: + media = self.media + + # Build the action form and populate it with available actions. + if actions: + action_form = self.action_form(auto_id=None) + action_form.fields["action"].choices = self.get_action_choices(request) + media += action_form.media + else: + action_form = None + + selection_note_all = ngettext( + "%(total_count)s selected", "All %(total_count)s selected", cl.result_count + ) + + context = { + **self.admin_site.each_context(request), + "module_name": str(self.opts.verbose_name_plural), + "selection_note": _("0 of %(cnt)s selected") % {"cnt": len(cl.result_list)}, + "selection_note_all": selection_note_all % {"total_count": cl.result_count}, + "title": cl.title, + "subtitle": None, + "is_popup": cl.is_popup, + "to_field": cl.to_field, + "cl": cl, + "media": media, + "has_add_permission": self.has_add_permission(request), + "opts": cl.opts, + "action_form": action_form, + "actions_on_top": self.actions_on_top, + "actions_on_bottom": self.actions_on_bottom, + "actions_selection_counter": self.actions_selection_counter, + "preserved_filters": self.get_preserved_filters(request), + **(extra_context or {}), + } + + request.current_app = self.admin_site.name + + return TemplateResponse( + request, + self.change_list_template + or [ + "admin/%s/%s/change_list.html" % (app_label, self.opts.model_name), + "admin/%s/change_list.html" % app_label, + "admin/change_list.html", + ], + context, + ) + + def get_deleted_objects(self, objs, request): + """ + Hook for customizing the delete process for the delete view and the + "delete selected" action. + """ + return get_deleted_objects(objs, request, self.admin_site) + + @csrf_protect_m + def delete_view(self, request, object_id, extra_context=None): + with transaction.atomic(using=router.db_for_write(self.model)): + return self._delete_view(request, object_id, extra_context) + + def _delete_view(self, request, object_id, extra_context): + "The 'delete' admin view for this model." + app_label = self.opts.app_label + + to_field = request.POST.get(TO_FIELD_VAR, request.GET.get(TO_FIELD_VAR)) + if to_field and not self.to_field_allowed(request, to_field): + raise DisallowedModelAdminToField( + "The field %s cannot be referenced." % to_field + ) + + obj = self.get_object(request, unquote(object_id), to_field) + + if not self.has_delete_permission(request, obj): + raise PermissionDenied + + if obj is None: + return self._get_obj_does_not_exist_redirect(request, self.opts, object_id) + + # Populate deleted_objects, a data structure of all related objects that + # will also be deleted. + ( + deleted_objects, + model_count, + perms_needed, + protected, + ) = self.get_deleted_objects([obj], request) + + if request.POST and not protected: # The user has confirmed the deletion. + if perms_needed: + raise PermissionDenied + obj_display = str(obj) + attr = str(to_field) if to_field else self.opts.pk.attname + obj_id = obj.serializable_value(attr) + self.log_deletion(request, obj, obj_display) + self.delete_model(request, obj) + + return self.response_delete(request, obj_display, obj_id) + + object_name = str(self.opts.verbose_name) + + if perms_needed or protected: + title = _("Cannot delete %(name)s") % {"name": object_name} + else: + title = _("Are you sure?") + + context = { + **self.admin_site.each_context(request), + "title": title, + "subtitle": None, + "object_name": object_name, + "object": obj, + "deleted_objects": deleted_objects, + "model_count": dict(model_count).items(), + "perms_lacking": perms_needed, + "protected": protected, + "opts": self.opts, + "app_label": app_label, + "preserved_filters": self.get_preserved_filters(request), + "is_popup": IS_POPUP_VAR in request.POST or IS_POPUP_VAR in request.GET, + "to_field": to_field, + **(extra_context or {}), + } + + return self.render_delete_form(request, context) + + def history_view(self, request, object_id, extra_context=None): + "The 'history' admin view for this model." + from django.contrib.admin.models import LogEntry + from django.contrib.admin.views.main import PAGE_VAR + + # First check if the user can see this history. + model = self.model + obj = self.get_object(request, unquote(object_id)) + if obj is None: + return self._get_obj_does_not_exist_redirect( + request, model._meta, object_id + ) + + if not self.has_view_or_change_permission(request, obj): + raise PermissionDenied + + # Then get the history for this object. + app_label = self.opts.app_label + action_list = ( + LogEntry.objects.filter( + object_id=unquote(object_id), + content_type=get_content_type_for_model(model), + ) + .select_related() + .order_by("action_time") + ) + + paginator = self.get_paginator(request, action_list, 100) + page_number = request.GET.get(PAGE_VAR, 1) + page_obj = paginator.get_page(page_number) + page_range = paginator.get_elided_page_range(page_obj.number) + + context = { + **self.admin_site.each_context(request), + "title": _("Change history: %s") % obj, + "subtitle": None, + "action_list": page_obj, + "page_range": page_range, + "page_var": PAGE_VAR, + "pagination_required": paginator.count > 100, + "module_name": str(capfirst(self.opts.verbose_name_plural)), + "object": obj, + "opts": self.opts, + "preserved_filters": self.get_preserved_filters(request), + **(extra_context or {}), + } + + request.current_app = self.admin_site.name + + return TemplateResponse( + request, + self.object_history_template + or [ + "admin/%s/%s/object_history.html" % (app_label, self.opts.model_name), + "admin/%s/object_history.html" % app_label, + "admin/object_history.html", + ], + context, + ) + + def get_formset_kwargs(self, request, obj, inline, prefix): + formset_params = { + "instance": obj, + "prefix": prefix, + "queryset": inline.get_queryset(request), + } + if request.method == "POST": + formset_params.update( + { + "data": request.POST.copy(), + "files": request.FILES, + "save_as_new": "_saveasnew" in request.POST, + } + ) + return formset_params + + def _create_formsets(self, request, obj, change): + "Helper function to generate formsets for add/change_view." + formsets = [] + inline_instances = [] + prefixes = {} + get_formsets_args = [request] + if change: + get_formsets_args.append(obj) + for FormSet, inline in self.get_formsets_with_inlines(*get_formsets_args): + prefix = FormSet.get_default_prefix() + prefixes[prefix] = prefixes.get(prefix, 0) + 1 + if prefixes[prefix] != 1 or not prefix: + prefix = "%s-%s" % (prefix, prefixes[prefix]) + formset_params = self.get_formset_kwargs(request, obj, inline, prefix) + formset = FormSet(**formset_params) + + def user_deleted_form(request, obj, formset, index, inline): + """Return whether or not the user deleted the form.""" + return ( + inline.has_delete_permission(request, obj) + and "{}-{}-DELETE".format(formset.prefix, index) in request.POST + ) + + # Bypass validation of each view-only inline form (since the form's + # data won't be in request.POST), unless the form was deleted. + if not inline.has_change_permission(request, obj if change else None): + for index, form in enumerate(formset.initial_forms): + if user_deleted_form(request, obj, formset, index, inline): + continue + form._errors = {} + form.cleaned_data = form.initial + formsets.append(formset) + inline_instances.append(inline) + return formsets, inline_instances + + +class InlineModelAdmin(BaseModelAdmin): + """ + Options for inline editing of ``model`` instances. + + Provide ``fk_name`` to specify the attribute name of the ``ForeignKey`` + from ``model`` to its parent. This is required if ``model`` has more than + one ``ForeignKey`` to its parent. + """ + + model = None + fk_name = None + formset = BaseInlineFormSet + extra = 3 + min_num = None + max_num = None + template = None + verbose_name = None + verbose_name_plural = None + can_delete = True + show_change_link = False + checks_class = InlineModelAdminChecks + classes = None + + def __init__(self, parent_model, admin_site): + self.admin_site = admin_site + self.parent_model = parent_model + self.opts = self.model._meta + self.has_registered_model = admin_site.is_registered(self.model) + super().__init__() + if self.verbose_name_plural is None: + if self.verbose_name is None: + self.verbose_name_plural = self.opts.verbose_name_plural + else: + self.verbose_name_plural = format_lazy("{}s", self.verbose_name) + if self.verbose_name is None: + self.verbose_name = self.opts.verbose_name + + @property + def media(self): + extra = "" if settings.DEBUG else ".min" + js = ["vendor/jquery/jquery%s.js" % extra, "jquery.init.js", "inlines.js"] + if self.filter_vertical or self.filter_horizontal: + js.extend(["SelectBox.js", "SelectFilter2.js"]) + if self.classes and "collapse" in self.classes: + js.append("collapse.js") + return forms.Media(js=["admin/js/%s" % url for url in js]) + + def get_extra(self, request, obj=None, **kwargs): + """Hook for customizing the number of extra inline forms.""" + return self.extra + + def get_min_num(self, request, obj=None, **kwargs): + """Hook for customizing the min number of inline forms.""" + return self.min_num + + def get_max_num(self, request, obj=None, **kwargs): + """Hook for customizing the max number of extra inline forms.""" + return self.max_num + + def get_formset(self, request, obj=None, **kwargs): + """Return a BaseInlineFormSet class for use in admin add/change views.""" + if "fields" in kwargs: + fields = kwargs.pop("fields") + else: + fields = flatten_fieldsets(self.get_fieldsets(request, obj)) + excluded = self.get_exclude(request, obj) + exclude = [] if excluded is None else list(excluded) + exclude.extend(self.get_readonly_fields(request, obj)) + if excluded is None and hasattr(self.form, "_meta") and self.form._meta.exclude: + # Take the custom ModelForm's Meta.exclude into account only if the + # InlineModelAdmin doesn't define its own. + exclude.extend(self.form._meta.exclude) + # If exclude is an empty list we use None, since that's the actual + # default. + exclude = exclude or None + can_delete = self.can_delete and self.has_delete_permission(request, obj) + defaults = { + "form": self.form, + "formset": self.formset, + "fk_name": self.fk_name, + "fields": fields, + "exclude": exclude, + "formfield_callback": partial(self.formfield_for_dbfield, request=request), + "extra": self.get_extra(request, obj, **kwargs), + "min_num": self.get_min_num(request, obj, **kwargs), + "max_num": self.get_max_num(request, obj, **kwargs), + "can_delete": can_delete, + **kwargs, + } + + base_model_form = defaults["form"] + can_change = self.has_change_permission(request, obj) if request else True + can_add = self.has_add_permission(request, obj) if request else True + + class DeleteProtectedModelForm(base_model_form): + def hand_clean_DELETE(self): + """ + We don't validate the 'DELETE' field itself because on + templates it's not rendered using the field information, but + just using a generic "deletion_field" of the InlineModelAdmin. + """ + if self.cleaned_data.get(DELETION_FIELD_NAME, False): + using = router.db_for_write(self._meta.model) + collector = NestedObjects(using=using) + if self.instance._state.adding: + return + collector.collect([self.instance]) + if collector.protected: + objs = [] + for p in collector.protected: + objs.append( + # Translators: Model verbose name and instance + # representation, suitable to be an item in a + # list. + _("%(class_name)s %(instance)s") + % {"class_name": p._meta.verbose_name, "instance": p} + ) + params = { + "class_name": self._meta.model._meta.verbose_name, + "instance": self.instance, + "related_objects": get_text_list(objs, _("and")), + } + msg = _( + "Deleting %(class_name)s %(instance)s would require " + "deleting the following protected related objects: " + "%(related_objects)s" + ) + raise ValidationError( + msg, code="deleting_protected", params=params + ) + + def is_valid(self): + result = super().is_valid() + self.hand_clean_DELETE() + return result + + def has_changed(self): + # Protect against unauthorized edits. + if not can_change and not self.instance._state.adding: + return False + if not can_add and self.instance._state.adding: + return False + return super().has_changed() + + defaults["form"] = DeleteProtectedModelForm + + if defaults["fields"] is None and not modelform_defines_fields( + defaults["form"] + ): + defaults["fields"] = forms.ALL_FIELDS + + return inlineformset_factory(self.parent_model, self.model, **defaults) + + def _get_form_for_get_fields(self, request, obj=None): + return self.get_formset(request, obj, fields=None).form + + def get_queryset(self, request): + queryset = super().get_queryset(request) + if not self.has_view_or_change_permission(request): + queryset = queryset.none() + return queryset + + def _has_any_perms_for_target_model(self, request, perms): + """ + This method is called only when the ModelAdmin's model is for an + ManyToManyField's implicit through model (if self.opts.auto_created). + Return True if the user has any of the given permissions ('add', + 'change', etc.) for the model that points to the through model. + """ + opts = self.opts + # Find the target model of an auto-created many-to-many relationship. + for field in opts.fields: + if field.remote_field and field.remote_field.model != self.parent_model: + opts = field.remote_field.model._meta + break + return any( + request.user.has_perm( + "%s.%s" % (opts.app_label, get_permission_codename(perm, opts)) + ) + for perm in perms + ) + + def has_add_permission(self, request, obj): + if self.opts.auto_created: + # Auto-created intermediate models don't have their own + # permissions. The user needs to have the change permission for the + # related model in order to be able to do anything with the + # intermediate model. + return self._has_any_perms_for_target_model(request, ["change"]) + return super().has_add_permission(request) + + def has_change_permission(self, request, obj=None): + if self.opts.auto_created: + # Same comment as has_add_permission(). + return self._has_any_perms_for_target_model(request, ["change"]) + return super().has_change_permission(request) + + def has_delete_permission(self, request, obj=None): + if self.opts.auto_created: + # Same comment as has_add_permission(). + return self._has_any_perms_for_target_model(request, ["change"]) + return super().has_delete_permission(request, obj) + + def has_view_permission(self, request, obj=None): + if self.opts.auto_created: + # Same comment as has_add_permission(). The 'change' permission + # also implies the 'view' permission. + return self._has_any_perms_for_target_model(request, ["view", "change"]) + return super().has_view_permission(request) + + +class StackedInline(InlineModelAdmin): + template = "admin/edit_inline/stacked.html" + + +class TabularInline(InlineModelAdmin): + template = "admin/edit_inline/tabular.html" diff --git a/virt/lib/python3.9/site-packages/django/contrib/admin/sites 3.py b/virt/lib/python3.9/site-packages/django/contrib/admin/sites 3.py new file mode 100644 index 00000000..729d59f5 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/admin/sites 3.py @@ -0,0 +1,606 @@ +import re +from functools import update_wrapper +from weakref import WeakSet + +from django.apps import apps +from django.conf import settings +from django.contrib.admin import ModelAdmin, actions +from django.contrib.admin.views.autocomplete import AutocompleteJsonView +from django.contrib.auth import REDIRECT_FIELD_NAME +from django.core.exceptions import ImproperlyConfigured +from django.db.models.base import ModelBase +from django.http import Http404, HttpResponsePermanentRedirect, HttpResponseRedirect +from django.template.response import TemplateResponse +from django.urls import NoReverseMatch, Resolver404, resolve, reverse +from django.utils.decorators import method_decorator +from django.utils.functional import LazyObject +from django.utils.module_loading import import_string +from django.utils.text import capfirst +from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy +from django.views.decorators.cache import never_cache +from django.views.decorators.common import no_append_slash +from django.views.decorators.csrf import csrf_protect +from django.views.i18n import JavaScriptCatalog + +all_sites = WeakSet() + + +class AlreadyRegistered(Exception): + pass + + +class NotRegistered(Exception): + pass + + +class AdminSite: + """ + An AdminSite object encapsulates an instance of the Django admin application, ready + to be hooked in to your URLconf. Models are registered with the AdminSite using the + register() method, and the get_urls() method can then be used to access Django view + functions that present a full admin interface for the collection of registered + models. + """ + + # Text to put at the end of each page's . + site_title = gettext_lazy("Django site admin") + + # Text to put in each page's <h1>. + site_header = gettext_lazy("Django administration") + + # Text to put at the top of the admin index page. + index_title = gettext_lazy("Site administration") + + # URL for the "View site" link at the top of each admin page. + site_url = "/" + + enable_nav_sidebar = True + + empty_value_display = "-" + + login_form = None + index_template = None + app_index_template = None + login_template = None + logout_template = None + password_change_template = None + password_change_done_template = None + + final_catch_all_view = True + + def __init__(self, name="admin"): + self._registry = {} # model_class class -> admin_class instance + self.name = name + self._actions = {"delete_selected": actions.delete_selected} + self._global_actions = self._actions.copy() + all_sites.add(self) + + def __repr__(self): + return f"{self.__class__.__name__}(name={self.name!r})" + + def check(self, app_configs): + """ + Run the system checks on all ModelAdmins, except if they aren't + customized at all. + """ + if app_configs is None: + app_configs = apps.get_app_configs() + app_configs = set(app_configs) # Speed up lookups below + + errors = [] + modeladmins = ( + o for o in self._registry.values() if o.__class__ is not ModelAdmin + ) + for modeladmin in modeladmins: + if modeladmin.model._meta.app_config in app_configs: + errors.extend(modeladmin.check()) + return errors + + def register(self, model_or_iterable, admin_class=None, **options): + """ + Register the given model(s) with the given admin class. + + The model(s) should be Model classes, not instances. + + If an admin class isn't given, use ModelAdmin (the default admin + options). If keyword arguments are given -- e.g., list_display -- + apply them as options to the admin class. + + If a model is already registered, raise AlreadyRegistered. + + If a model is abstract, raise ImproperlyConfigured. + """ + admin_class = admin_class or ModelAdmin + if isinstance(model_or_iterable, ModelBase): + model_or_iterable = [model_or_iterable] + for model in model_or_iterable: + if model._meta.abstract: + raise ImproperlyConfigured( + "The model %s is abstract, so it cannot be registered with admin." + % model.__name__ + ) + + if model in self._registry: + registered_admin = str(self._registry[model]) + msg = "The model %s is already registered " % model.__name__ + if registered_admin.endswith(".ModelAdmin"): + # Most likely registered without a ModelAdmin subclass. + msg += "in app %r." % re.sub(r"\.ModelAdmin$", "", registered_admin) + else: + msg += "with %r." % registered_admin + raise AlreadyRegistered(msg) + + # Ignore the registration if the model has been + # swapped out. + if not model._meta.swapped: + # If we got **options then dynamically construct a subclass of + # admin_class with those **options. + if options: + # For reasons I don't quite understand, without a __module__ + # the created class appears to "live" in the wrong place, + # which causes issues later on. + options["__module__"] = __name__ + admin_class = type( + "%sAdmin" % model.__name__, (admin_class,), options + ) + + # Instantiate the admin class to save in the registry + self._registry[model] = admin_class(model, self) + + def unregister(self, model_or_iterable): + """ + Unregister the given model(s). + + If a model isn't already registered, raise NotRegistered. + """ + if isinstance(model_or_iterable, ModelBase): + model_or_iterable = [model_or_iterable] + for model in model_or_iterable: + if model not in self._registry: + raise NotRegistered("The model %s is not registered" % model.__name__) + del self._registry[model] + + def is_registered(self, model): + """ + Check if a model class is registered with this `AdminSite`. + """ + return model in self._registry + + def add_action(self, action, name=None): + """ + Register an action to be available globally. + """ + name = name or action.__name__ + self._actions[name] = action + self._global_actions[name] = action + + def disable_action(self, name): + """ + Disable a globally-registered action. Raise KeyError for invalid names. + """ + del self._actions[name] + + def get_action(self, name): + """ + Explicitly get a registered global action whether it's enabled or + not. Raise KeyError for invalid names. + """ + return self._global_actions[name] + + @property + def actions(self): + """ + Get all the enabled actions as an iterable of (name, func). + """ + return self._actions.items() + + def has_permission(self, request): + """ + Return True if the given HttpRequest has permission to view + *at least one* page in the admin site. + """ + return request.user.is_active and request.user.is_staff + + def admin_view(self, view, cacheable=False): + """ + Decorator to create an admin view attached to this ``AdminSite``. This + wraps the view and provides permission checking by calling + ``self.has_permission``. + + You'll want to use this from within ``AdminSite.get_urls()``: + + class MyAdminSite(AdminSite): + + def get_urls(self): + from django.urls import path + + urls = super().get_urls() + urls += [ + path('my_view/', self.admin_view(some_view)) + ] + return urls + + By default, admin_views are marked non-cacheable using the + ``never_cache`` decorator. If the view can be safely cached, set + cacheable=True. + """ + + def inner(request, *args, **kwargs): + if not self.has_permission(request): + if request.path == reverse("admin:logout", current_app=self.name): + index_path = reverse("admin:index", current_app=self.name) + return HttpResponseRedirect(index_path) + # Inner import to prevent django.contrib.admin (app) from + # importing django.contrib.auth.models.User (unrelated model). + from django.contrib.auth.views import redirect_to_login + + return redirect_to_login( + request.get_full_path(), + reverse("admin:login", current_app=self.name), + ) + return view(request, *args, **kwargs) + + if not cacheable: + inner = never_cache(inner) + # We add csrf_protect here so this function can be used as a utility + # function for any view, without having to repeat 'csrf_protect'. + if not getattr(view, "csrf_exempt", False): + inner = csrf_protect(inner) + return update_wrapper(inner, view) + + def get_urls(self): + # Since this module gets imported in the application's root package, + # it cannot import models from other applications at the module level, + # and django.contrib.contenttypes.views imports ContentType. + from django.contrib.contenttypes import views as contenttype_views + from django.urls import include, path, re_path + + def wrap(view, cacheable=False): + def wrapper(*args, **kwargs): + return self.admin_view(view, cacheable)(*args, **kwargs) + + wrapper.admin_site = self + return update_wrapper(wrapper, view) + + # Admin-site-wide views. + urlpatterns = [ + path("", wrap(self.index), name="index"), + path("login/", self.login, name="login"), + path("logout/", wrap(self.logout), name="logout"), + path( + "password_change/", + wrap(self.password_change, cacheable=True), + name="password_change", + ), + path( + "password_change/done/", + wrap(self.password_change_done, cacheable=True), + name="password_change_done", + ), + path("autocomplete/", wrap(self.autocomplete_view), name="autocomplete"), + path("jsi18n/", wrap(self.i18n_javascript, cacheable=True), name="jsi18n"), + path( + "r/<int:content_type_id>/<path:object_id>/", + wrap(contenttype_views.shortcut), + name="view_on_site", + ), + ] + + # Add in each model's views, and create a list of valid URLS for the + # app_index + valid_app_labels = [] + for model, model_admin in self._registry.items(): + urlpatterns += [ + path( + "%s/%s/" % (model._meta.app_label, model._meta.model_name), + include(model_admin.urls), + ), + ] + if model._meta.app_label not in valid_app_labels: + valid_app_labels.append(model._meta.app_label) + + # If there were ModelAdmins registered, we should have a list of app + # labels for which we need to allow access to the app_index view, + if valid_app_labels: + regex = r"^(?P<app_label>" + "|".join(valid_app_labels) + ")/$" + urlpatterns += [ + re_path(regex, wrap(self.app_index), name="app_list"), + ] + + if self.final_catch_all_view: + urlpatterns.append(re_path(r"(?P<url>.*)$", wrap(self.catch_all_view))) + + return urlpatterns + + @property + def urls(self): + return self.get_urls(), "admin", self.name + + def each_context(self, request): + """ + Return a dictionary of variables to put in the template context for + *every* page in the admin site. + + For sites running on a subpath, use the SCRIPT_NAME value if site_url + hasn't been customized. + """ + script_name = request.META["SCRIPT_NAME"] + site_url = ( + script_name if self.site_url == "/" and script_name else self.site_url + ) + return { + "site_title": self.site_title, + "site_header": self.site_header, + "site_url": site_url, + "has_permission": self.has_permission(request), + "available_apps": self.get_app_list(request), + "is_popup": False, + "is_nav_sidebar_enabled": self.enable_nav_sidebar, + } + + def password_change(self, request, extra_context=None): + """ + Handle the "change password" task -- both form display and validation. + """ + from django.contrib.admin.forms import AdminPasswordChangeForm + from django.contrib.auth.views import PasswordChangeView + + url = reverse("admin:password_change_done", current_app=self.name) + defaults = { + "form_class": AdminPasswordChangeForm, + "success_url": url, + "extra_context": {**self.each_context(request), **(extra_context or {})}, + } + if self.password_change_template is not None: + defaults["template_name"] = self.password_change_template + request.current_app = self.name + return PasswordChangeView.as_view(**defaults)(request) + + def password_change_done(self, request, extra_context=None): + """ + Display the "success" page after a password change. + """ + from django.contrib.auth.views import PasswordChangeDoneView + + defaults = { + "extra_context": {**self.each_context(request), **(extra_context or {})}, + } + if self.password_change_done_template is not None: + defaults["template_name"] = self.password_change_done_template + request.current_app = self.name + return PasswordChangeDoneView.as_view(**defaults)(request) + + def i18n_javascript(self, request, extra_context=None): + """ + Display the i18n JavaScript that the Django admin requires. + + `extra_context` is unused but present for consistency with the other + admin views. + """ + return JavaScriptCatalog.as_view(packages=["django.contrib.admin"])(request) + + def logout(self, request, extra_context=None): + """ + Log out the user for the given HttpRequest. + + This should *not* assume the user is already logged in. + """ + from django.contrib.auth.views import LogoutView + + defaults = { + "extra_context": { + **self.each_context(request), + # Since the user isn't logged out at this point, the value of + # has_permission must be overridden. + "has_permission": False, + **(extra_context or {}), + }, + } + if self.logout_template is not None: + defaults["template_name"] = self.logout_template + request.current_app = self.name + return LogoutView.as_view(**defaults)(request) + + @method_decorator(never_cache) + def login(self, request, extra_context=None): + """ + Display the login form for the given HttpRequest. + """ + if request.method == "GET" and self.has_permission(request): + # Already logged-in, redirect to admin index + index_path = reverse("admin:index", current_app=self.name) + return HttpResponseRedirect(index_path) + + # Since this module gets imported in the application's root package, + # it cannot import models from other applications at the module level, + # and django.contrib.admin.forms eventually imports User. + from django.contrib.admin.forms import AdminAuthenticationForm + from django.contrib.auth.views import LoginView + + context = { + **self.each_context(request), + "title": _("Log in"), + "subtitle": None, + "app_path": request.get_full_path(), + "username": request.user.get_username(), + } + if ( + REDIRECT_FIELD_NAME not in request.GET + and REDIRECT_FIELD_NAME not in request.POST + ): + context[REDIRECT_FIELD_NAME] = reverse("admin:index", current_app=self.name) + context.update(extra_context or {}) + + defaults = { + "extra_context": context, + "authentication_form": self.login_form or AdminAuthenticationForm, + "template_name": self.login_template or "admin/login.html", + } + request.current_app = self.name + return LoginView.as_view(**defaults)(request) + + def autocomplete_view(self, request): + return AutocompleteJsonView.as_view(admin_site=self)(request) + + @no_append_slash + def catch_all_view(self, request, url): + if settings.APPEND_SLASH and not url.endswith("/"): + urlconf = getattr(request, "urlconf", None) + try: + match = resolve("%s/" % request.path_info, urlconf) + except Resolver404: + pass + else: + if getattr(match.func, "should_append_slash", True): + return HttpResponsePermanentRedirect("%s/" % request.path) + raise Http404 + + def _build_app_dict(self, request, label=None): + """ + Build the app dictionary. The optional `label` parameter filters models + of a specific app. + """ + app_dict = {} + + if label: + models = { + m: m_a + for m, m_a in self._registry.items() + if m._meta.app_label == label + } + else: + models = self._registry + + for model, model_admin in models.items(): + app_label = model._meta.app_label + + has_module_perms = model_admin.has_module_permission(request) + if not has_module_perms: + continue + + perms = model_admin.get_model_perms(request) + + # Check whether user has any perm for this module. + # If so, add the module to the model_list. + if True not in perms.values(): + continue + + info = (app_label, model._meta.model_name) + model_dict = { + "model": model, + "name": capfirst(model._meta.verbose_name_plural), + "object_name": model._meta.object_name, + "perms": perms, + "admin_url": None, + "add_url": None, + } + if perms.get("change") or perms.get("view"): + model_dict["view_only"] = not perms.get("change") + try: + model_dict["admin_url"] = reverse( + "admin:%s_%s_changelist" % info, current_app=self.name + ) + except NoReverseMatch: + pass + if perms.get("add"): + try: + model_dict["add_url"] = reverse( + "admin:%s_%s_add" % info, current_app=self.name + ) + except NoReverseMatch: + pass + + if app_label in app_dict: + app_dict[app_label]["models"].append(model_dict) + else: + app_dict[app_label] = { + "name": apps.get_app_config(app_label).verbose_name, + "app_label": app_label, + "app_url": reverse( + "admin:app_list", + kwargs={"app_label": app_label}, + current_app=self.name, + ), + "has_module_perms": has_module_perms, + "models": [model_dict], + } + + return app_dict + + def get_app_list(self, request, app_label=None): + """ + Return a sorted list of all the installed apps that have been + registered in this site. + """ + app_dict = self._build_app_dict(request, app_label) + + # Sort the apps alphabetically. + app_list = sorted(app_dict.values(), key=lambda x: x["name"].lower()) + + # Sort the models alphabetically within each app. + for app in app_list: + app["models"].sort(key=lambda x: x["name"]) + + return app_list + + def index(self, request, extra_context=None): + """ + Display the main admin index page, which lists all of the installed + apps that have been registered in this site. + """ + app_list = self.get_app_list(request) + + context = { + **self.each_context(request), + "title": self.index_title, + "subtitle": None, + "app_list": app_list, + **(extra_context or {}), + } + + request.current_app = self.name + + return TemplateResponse( + request, self.index_template or "admin/index.html", context + ) + + def app_index(self, request, app_label, extra_context=None): + app_list = self.get_app_list(request, app_label) + + if not app_list: + raise Http404("The requested admin page does not exist.") + + context = { + **self.each_context(request), + "title": _("%(app)s administration") % {"app": app_list[0]["name"]}, + "subtitle": None, + "app_list": app_list, + "app_label": app_label, + **(extra_context or {}), + } + + request.current_app = self.name + + return TemplateResponse( + request, + self.app_index_template + or ["admin/%s/app_index.html" % app_label, "admin/app_index.html"], + context, + ) + + +class DefaultAdminSite(LazyObject): + def _setup(self): + AdminSiteClass = import_string(apps.get_app_config("admin").default_site) + self._wrapped = AdminSiteClass() + + def __repr__(self): + return repr(self._wrapped) + + +# This global object represents the default admin site, for the common case. +# You can provide your own AdminSite using the (Simple)AdminConfig.default_site +# attribute. You can also instantiate AdminSite in your own code to create a +# custom admin site. +site = DefaultAdminSite() diff --git a/virt/lib/python3.9/site-packages/django/contrib/auth/forms 3.py b/virt/lib/python3.9/site-packages/django/contrib/auth/forms 3.py new file mode 100644 index 00000000..172bc3cf --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/auth/forms 3.py @@ -0,0 +1,510 @@ +import unicodedata + +from django import forms +from django.contrib.auth import authenticate, get_user_model, password_validation +from django.contrib.auth.hashers import UNUSABLE_PASSWORD_PREFIX, identify_hasher +from django.contrib.auth.models import User +from django.contrib.auth.tokens import default_token_generator +from django.contrib.sites.shortcuts import get_current_site +from django.core.exceptions import ValidationError +from django.core.mail import EmailMultiAlternatives +from django.template import loader +from django.utils.encoding import force_bytes +from django.utils.http import urlsafe_base64_encode +from django.utils.text import capfirst +from django.utils.translation import gettext +from django.utils.translation import gettext_lazy as _ + +UserModel = get_user_model() + + +def _unicode_ci_compare(s1, s2): + """ + Perform case-insensitive comparison of two identifiers, using the + recommended algorithm from Unicode Technical Report 36, section + 2.11.2(B)(2). + """ + return ( + unicodedata.normalize("NFKC", s1).casefold() + == unicodedata.normalize("NFKC", s2).casefold() + ) + + +class ReadOnlyPasswordHashWidget(forms.Widget): + template_name = "auth/widgets/read_only_password_hash.html" + read_only = True + + def get_context(self, name, value, attrs): + context = super().get_context(name, value, attrs) + summary = [] + if not value or value.startswith(UNUSABLE_PASSWORD_PREFIX): + summary.append({"label": gettext("No password set.")}) + else: + try: + hasher = identify_hasher(value) + except ValueError: + summary.append( + { + "label": gettext( + "Invalid password format or unknown hashing algorithm." + ) + } + ) + else: + for key, value_ in hasher.safe_summary(value).items(): + summary.append({"label": gettext(key), "value": value_}) + context["summary"] = summary + return context + + def id_for_label(self, id_): + return None + + +class ReadOnlyPasswordHashField(forms.Field): + widget = ReadOnlyPasswordHashWidget + + def __init__(self, *args, **kwargs): + kwargs.setdefault("required", False) + kwargs.setdefault("disabled", True) + super().__init__(*args, **kwargs) + + +class UsernameField(forms.CharField): + def to_python(self, value): + return unicodedata.normalize("NFKC", super().to_python(value)) + + def widget_attrs(self, widget): + return { + **super().widget_attrs(widget), + "autocapitalize": "none", + "autocomplete": "username", + } + + +class BaseUserCreationForm(forms.ModelForm): + """ + A form that creates a user, with no privileges, from the given username and + password. + """ + + error_messages = { + "password_mismatch": _("The two password fields didn’t match."), + } + password1 = forms.CharField( + label=_("Password"), + strip=False, + widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), + help_text=password_validation.password_validators_help_text_html(), + ) + password2 = forms.CharField( + label=_("Password confirmation"), + widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), + strip=False, + help_text=_("Enter the same password as before, for verification."), + ) + + class Meta: + model = User + fields = ("username",) + field_classes = {"username": UsernameField} + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self._meta.model.USERNAME_FIELD in self.fields: + self.fields[self._meta.model.USERNAME_FIELD].widget.attrs[ + "autofocus" + ] = True + + def clean_password2(self): + password1 = self.cleaned_data.get("password1") + password2 = self.cleaned_data.get("password2") + if password1 and password2 and password1 != password2: + raise ValidationError( + self.error_messages["password_mismatch"], + code="password_mismatch", + ) + return password2 + + def _post_clean(self): + super()._post_clean() + # Validate the password after self.instance is updated with form data + # by super(). + password = self.cleaned_data.get("password2") + if password: + try: + password_validation.validate_password(password, self.instance) + except ValidationError as error: + self.add_error("password2", error) + + def save(self, commit=True): + user = super().save(commit=False) + user.set_password(self.cleaned_data["password1"]) + if commit: + user.save() + if hasattr(self, "save_m2m"): + self.save_m2m() + return user + + +class UserCreationForm(BaseUserCreationForm): + def clean_username(self): + """Reject usernames that differ only in case.""" + username = self.cleaned_data.get("username") + if ( + username + and self._meta.model.objects.filter(username__iexact=username).exists() + ): + self._update_errors( + ValidationError( + { + "username": self.instance.unique_error_message( + self._meta.model, ["username"] + ) + } + ) + ) + else: + return username + + +class UserChangeForm(forms.ModelForm): + password = ReadOnlyPasswordHashField( + label=_("Password"), + help_text=_( + "Raw passwords are not stored, so there is no way to see this " + "user’s password, but you can change the password using " + '<a href="{}">this form</a>.' + ), + ) + + class Meta: + model = User + fields = "__all__" + field_classes = {"username": UsernameField} + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + password = self.fields.get("password") + if password: + password.help_text = password.help_text.format( + f"../../{self.instance.pk}/password/" + ) + user_permissions = self.fields.get("user_permissions") + if user_permissions: + user_permissions.queryset = user_permissions.queryset.select_related( + "content_type" + ) + + +class AuthenticationForm(forms.Form): + """ + Base class for authenticating users. Extend this to get a form that accepts + username/password logins. + """ + + username = UsernameField(widget=forms.TextInput(attrs={"autofocus": True})) + password = forms.CharField( + label=_("Password"), + strip=False, + widget=forms.PasswordInput(attrs={"autocomplete": "current-password"}), + ) + + error_messages = { + "invalid_login": _( + "Please enter a correct %(username)s and password. Note that both " + "fields may be case-sensitive." + ), + "inactive": _("This account is inactive."), + } + + def __init__(self, request=None, *args, **kwargs): + """ + The 'request' parameter is set for custom auth use by subclasses. + The form data comes in via the standard 'data' kwarg. + """ + self.request = request + self.user_cache = None + super().__init__(*args, **kwargs) + + # Set the max length and label for the "username" field. + self.username_field = UserModel._meta.get_field(UserModel.USERNAME_FIELD) + username_max_length = self.username_field.max_length or 254 + self.fields["username"].max_length = username_max_length + self.fields["username"].widget.attrs["maxlength"] = username_max_length + if self.fields["username"].label is None: + self.fields["username"].label = capfirst(self.username_field.verbose_name) + + def clean(self): + username = self.cleaned_data.get("username") + password = self.cleaned_data.get("password") + + if username is not None and password: + self.user_cache = authenticate( + self.request, username=username, password=password + ) + if self.user_cache is None: + raise self.get_invalid_login_error() + else: + self.confirm_login_allowed(self.user_cache) + + return self.cleaned_data + + def confirm_login_allowed(self, user): + """ + Controls whether the given User may log in. This is a policy setting, + independent of end-user authentication. This default behavior is to + allow login by active users, and reject login by inactive users. + + If the given user cannot log in, this method should raise a + ``ValidationError``. + + If the given user may log in, this method should return None. + """ + if not user.is_active: + raise ValidationError( + self.error_messages["inactive"], + code="inactive", + ) + + def get_user(self): + return self.user_cache + + def get_invalid_login_error(self): + return ValidationError( + self.error_messages["invalid_login"], + code="invalid_login", + params={"username": self.username_field.verbose_name}, + ) + + +class PasswordResetForm(forms.Form): + email = forms.EmailField( + label=_("Email"), + max_length=254, + widget=forms.EmailInput(attrs={"autocomplete": "email"}), + ) + + def send_mail( + self, + subject_template_name, + email_template_name, + context, + from_email, + to_email, + html_email_template_name=None, + ): + """ + Send a django.core.mail.EmailMultiAlternatives to `to_email`. + """ + subject = loader.render_to_string(subject_template_name, context) + # Email subject *must not* contain newlines + subject = "".join(subject.splitlines()) + body = loader.render_to_string(email_template_name, context) + + email_message = EmailMultiAlternatives(subject, body, from_email, [to_email]) + if html_email_template_name is not None: + html_email = loader.render_to_string(html_email_template_name, context) + email_message.attach_alternative(html_email, "text/html") + + email_message.send() + + def get_users(self, email): + """Given an email, return matching user(s) who should receive a reset. + + This allows subclasses to more easily customize the default policies + that prevent inactive users and users with unusable passwords from + resetting their password. + """ + email_field_name = UserModel.get_email_field_name() + active_users = UserModel._default_manager.filter( + **{ + "%s__iexact" % email_field_name: email, + "is_active": True, + } + ) + return ( + u + for u in active_users + if u.has_usable_password() + and _unicode_ci_compare(email, getattr(u, email_field_name)) + ) + + def save( + self, + domain_override=None, + subject_template_name="registration/password_reset_subject.txt", + email_template_name="registration/password_reset_email.html", + use_https=False, + token_generator=default_token_generator, + from_email=None, + request=None, + html_email_template_name=None, + extra_email_context=None, + ): + """ + Generate a one-use only link for resetting password and send it to the + user. + """ + email = self.cleaned_data["email"] + if not domain_override: + current_site = get_current_site(request) + site_name = current_site.name + domain = current_site.domain + else: + site_name = domain = domain_override + email_field_name = UserModel.get_email_field_name() + for user in self.get_users(email): + user_email = getattr(user, email_field_name) + context = { + "email": user_email, + "domain": domain, + "site_name": site_name, + "uid": urlsafe_base64_encode(force_bytes(user.pk)), + "user": user, + "token": token_generator.make_token(user), + "protocol": "https" if use_https else "http", + **(extra_email_context or {}), + } + self.send_mail( + subject_template_name, + email_template_name, + context, + from_email, + user_email, + html_email_template_name=html_email_template_name, + ) + + +class SetPasswordForm(forms.Form): + """ + A form that lets a user set their password without entering the old + password + """ + + error_messages = { + "password_mismatch": _("The two password fields didn’t match."), + } + new_password1 = forms.CharField( + label=_("New password"), + widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), + strip=False, + help_text=password_validation.password_validators_help_text_html(), + ) + new_password2 = forms.CharField( + label=_("New password confirmation"), + strip=False, + widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), + ) + + def __init__(self, user, *args, **kwargs): + self.user = user + super().__init__(*args, **kwargs) + + def clean_new_password2(self): + password1 = self.cleaned_data.get("new_password1") + password2 = self.cleaned_data.get("new_password2") + if password1 and password2 and password1 != password2: + raise ValidationError( + self.error_messages["password_mismatch"], + code="password_mismatch", + ) + password_validation.validate_password(password2, self.user) + return password2 + + def save(self, commit=True): + password = self.cleaned_data["new_password1"] + self.user.set_password(password) + if commit: + self.user.save() + return self.user + + +class PasswordChangeForm(SetPasswordForm): + """ + A form that lets a user change their password by entering their old + password. + """ + + error_messages = { + **SetPasswordForm.error_messages, + "password_incorrect": _( + "Your old password was entered incorrectly. Please enter it again." + ), + } + old_password = forms.CharField( + label=_("Old password"), + strip=False, + widget=forms.PasswordInput( + attrs={"autocomplete": "current-password", "autofocus": True} + ), + ) + + field_order = ["old_password", "new_password1", "new_password2"] + + def clean_old_password(self): + """ + Validate that the old_password field is correct. + """ + old_password = self.cleaned_data["old_password"] + if not self.user.check_password(old_password): + raise ValidationError( + self.error_messages["password_incorrect"], + code="password_incorrect", + ) + return old_password + + +class AdminPasswordChangeForm(forms.Form): + """ + A form used to change the password of a user in the admin interface. + """ + + error_messages = { + "password_mismatch": _("The two password fields didn’t match."), + } + required_css_class = "required" + password1 = forms.CharField( + label=_("Password"), + widget=forms.PasswordInput( + attrs={"autocomplete": "new-password", "autofocus": True} + ), + strip=False, + help_text=password_validation.password_validators_help_text_html(), + ) + password2 = forms.CharField( + label=_("Password (again)"), + widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), + strip=False, + help_text=_("Enter the same password as before, for verification."), + ) + + def __init__(self, user, *args, **kwargs): + self.user = user + super().__init__(*args, **kwargs) + + def clean_password2(self): + password1 = self.cleaned_data.get("password1") + password2 = self.cleaned_data.get("password2") + if password1 and password2 and password1 != password2: + raise ValidationError( + self.error_messages["password_mismatch"], + code="password_mismatch", + ) + password_validation.validate_password(password2, self.user) + return password2 + + def save(self, commit=True): + """Save the new password.""" + password = self.cleaned_data["password1"] + self.user.set_password(password) + if commit: + self.user.save() + return self.user + + @property + def changed_data(self): + data = super().changed_data + for name in self.fields: + if name not in data: + return [] + return ["password"] diff --git a/virt/lib/python3.9/site-packages/django/contrib/auth/models 3.py b/virt/lib/python3.9/site-packages/django/contrib/auth/models 3.py new file mode 100644 index 00000000..1ffa0a10 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/auth/models 3.py @@ -0,0 +1,499 @@ +from django.apps import apps +from django.contrib import auth +from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager +from django.contrib.auth.hashers import make_password +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import PermissionDenied +from django.core.mail import send_mail +from django.db import models +from django.db.models.manager import EmptyManager +from django.utils import timezone +from django.utils.itercompat import is_iterable +from django.utils.translation import gettext_lazy as _ + +from .validators import UnicodeUsernameValidator + + +def update_last_login(sender, user, **kwargs): + """ + A signal receiver which updates the last_login date for + the user logging in. + """ + user.last_login = timezone.now() + user.save(update_fields=["last_login"]) + + +class PermissionManager(models.Manager): + use_in_migrations = True + + def get_by_natural_key(self, codename, app_label, model): + return self.get( + codename=codename, + content_type=ContentType.objects.db_manager(self.db).get_by_natural_key( + app_label, model + ), + ) + + +class Permission(models.Model): + """ + The permissions system provides a way to assign permissions to specific + users and groups of users. + + The permission system is used by the Django admin site, but may also be + useful in your own code. The Django admin site uses permissions as follows: + + - The "add" permission limits the user's ability to view the "add" form + and add an object. + - The "change" permission limits a user's ability to view the change + list, view the "change" form and change an object. + - The "delete" permission limits the ability to delete an object. + - The "view" permission limits the ability to view an object. + + Permissions are set globally per type of object, not per specific object + instance. It is possible to say "Mary may change news stories," but it's + not currently possible to say "Mary may change news stories, but only the + ones she created herself" or "Mary may only change news stories that have a + certain status or publication date." + + The permissions listed above are automatically created for each model. + """ + + name = models.CharField(_("name"), max_length=255) + content_type = models.ForeignKey( + ContentType, + models.CASCADE, + verbose_name=_("content type"), + ) + codename = models.CharField(_("codename"), max_length=100) + + objects = PermissionManager() + + class Meta: + verbose_name = _("permission") + verbose_name_plural = _("permissions") + unique_together = [["content_type", "codename"]] + ordering = ["content_type__app_label", "content_type__model", "codename"] + + def __str__(self): + return "%s | %s" % (self.content_type, self.name) + + def natural_key(self): + return (self.codename,) + self.content_type.natural_key() + + natural_key.dependencies = ["contenttypes.contenttype"] + + +class GroupManager(models.Manager): + """ + The manager for the auth's Group model. + """ + + use_in_migrations = True + + def get_by_natural_key(self, name): + return self.get(name=name) + + +class Group(models.Model): + """ + Groups are a generic way of categorizing users to apply permissions, or + some other label, to those users. A user can belong to any number of + groups. + + A user in a group automatically has all the permissions granted to that + group. For example, if the group 'Site editors' has the permission + can_edit_home_page, any user in that group will have that permission. + + Beyond permissions, groups are a convenient way to categorize users to + apply some label, or extended functionality, to them. For example, you + could create a group 'Special users', and you could write code that would + do special things to those users -- such as giving them access to a + members-only portion of your site, or sending them members-only email + messages. + """ + + name = models.CharField(_("name"), max_length=150, unique=True) + permissions = models.ManyToManyField( + Permission, + verbose_name=_("permissions"), + blank=True, + ) + + objects = GroupManager() + + class Meta: + verbose_name = _("group") + verbose_name_plural = _("groups") + + def __str__(self): + return self.name + + def natural_key(self): + return (self.name,) + + +class UserManager(BaseUserManager): + use_in_migrations = True + + def _create_user(self, username, email, password, **extra_fields): + """ + Create and save a user with the given username, email, and password. + """ + if not username: + raise ValueError("The given username must be set") + email = self.normalize_email(email) + # Lookup the real model class from the global app registry so this + # manager method can be used in migrations. This is fine because + # managers are by definition working on the real model. + GlobalUserModel = apps.get_model( + self.model._meta.app_label, self.model._meta.object_name + ) + username = GlobalUserModel.normalize_username(username) + user = self.model(username=username, email=email, **extra_fields) + user.password = make_password(password) + user.save(using=self._db) + return user + + def create_user(self, username, email=None, password=None, **extra_fields): + extra_fields.setdefault("is_staff", False) + extra_fields.setdefault("is_superuser", False) + return self._create_user(username, email, password, **extra_fields) + + def create_superuser(self, username, email=None, password=None, **extra_fields): + extra_fields.setdefault("is_staff", True) + extra_fields.setdefault("is_superuser", True) + + if extra_fields.get("is_staff") is not True: + raise ValueError("Superuser must have is_staff=True.") + if extra_fields.get("is_superuser") is not True: + raise ValueError("Superuser must have is_superuser=True.") + + return self._create_user(username, email, password, **extra_fields) + + def with_perm( + self, perm, is_active=True, include_superusers=True, backend=None, obj=None + ): + if backend is None: + backends = auth._get_backends(return_tuples=True) + if len(backends) == 1: + backend, _ = backends[0] + else: + raise ValueError( + "You have multiple authentication backends configured and " + "therefore must provide the `backend` argument." + ) + elif not isinstance(backend, str): + raise TypeError( + "backend must be a dotted import path string (got %r)." % backend + ) + else: + backend = auth.load_backend(backend) + if hasattr(backend, "with_perm"): + return backend.with_perm( + perm, + is_active=is_active, + include_superusers=include_superusers, + obj=obj, + ) + return self.none() + + +# A few helper functions for common logic between User and AnonymousUser. +def _user_get_permissions(user, obj, from_name): + permissions = set() + name = "get_%s_permissions" % from_name + for backend in auth.get_backends(): + if hasattr(backend, name): + permissions.update(getattr(backend, name)(user, obj)) + return permissions + + +def _user_has_perm(user, perm, obj): + """ + A backend can raise `PermissionDenied` to short-circuit permission checking. + """ + for backend in auth.get_backends(): + if not hasattr(backend, "has_perm"): + continue + try: + if backend.has_perm(user, perm, obj): + return True + except PermissionDenied: + return False + return False + + +def _user_has_module_perms(user, app_label): + """ + A backend can raise `PermissionDenied` to short-circuit permission checking. + """ + for backend in auth.get_backends(): + if not hasattr(backend, "has_module_perms"): + continue + try: + if backend.has_module_perms(user, app_label): + return True + except PermissionDenied: + return False + return False + + +class PermissionsMixin(models.Model): + """ + Add the fields and methods necessary to support the Group and Permission + models using the ModelBackend. + """ + + is_superuser = models.BooleanField( + _("superuser status"), + default=False, + help_text=_( + "Designates that this user has all permissions without " + "explicitly assigning them." + ), + ) + groups = models.ManyToManyField( + Group, + verbose_name=_("groups"), + blank=True, + help_text=_( + "The groups this user belongs to. A user will get all permissions " + "granted to each of their groups." + ), + related_name="user_set", + related_query_name="user", + ) + user_permissions = models.ManyToManyField( + Permission, + verbose_name=_("user permissions"), + blank=True, + help_text=_("Specific permissions for this user."), + related_name="user_set", + related_query_name="user", + ) + + class Meta: + abstract = True + + def get_user_permissions(self, obj=None): + """ + Return a list of permission strings that this user has directly. + Query all available auth backends. If an object is passed in, + return only permissions matching this object. + """ + return _user_get_permissions(self, obj, "user") + + def get_group_permissions(self, obj=None): + """ + Return a list of permission strings that this user has through their + groups. Query all available auth backends. If an object is passed in, + return only permissions matching this object. + """ + return _user_get_permissions(self, obj, "group") + + def get_all_permissions(self, obj=None): + return _user_get_permissions(self, obj, "all") + + def has_perm(self, perm, obj=None): + """ + Return True if the user has the specified permission. Query all + available auth backends, but return immediately if any backend returns + True. Thus, a user who has permission from a single auth backend is + assumed to have permission in general. If an object is provided, check + permissions for that object. + """ + # Active superusers have all permissions. + if self.is_active and self.is_superuser: + return True + + # Otherwise we need to check the backends. + return _user_has_perm(self, perm, obj) + + def has_perms(self, perm_list, obj=None): + """ + Return True if the user has each of the specified permissions. If + object is passed, check if the user has all required perms for it. + """ + if not is_iterable(perm_list) or isinstance(perm_list, str): + raise ValueError("perm_list must be an iterable of permissions.") + return all(self.has_perm(perm, obj) for perm in perm_list) + + def has_module_perms(self, app_label): + """ + Return True if the user has any permissions in the given app label. + Use similar logic as has_perm(), above. + """ + # Active superusers have all permissions. + if self.is_active and self.is_superuser: + return True + + return _user_has_module_perms(self, app_label) + + +class AbstractUser(AbstractBaseUser, PermissionsMixin): + """ + An abstract base class implementing a fully featured User model with + admin-compliant permissions. + + Username and password are required. Other fields are optional. + """ + + username_validator = UnicodeUsernameValidator() + + username = models.CharField( + _("username"), + max_length=150, + unique=True, + help_text=_( + "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." + ), + validators=[username_validator], + error_messages={ + "unique": _("A user with that username already exists."), + }, + ) + first_name = models.CharField(_("first name"), max_length=150, blank=True) + last_name = models.CharField(_("last name"), max_length=150, blank=True) + email = models.EmailField(_("email address"), blank=True) + is_staff = models.BooleanField( + _("staff status"), + default=False, + help_text=_("Designates whether the user can log into this admin site."), + ) + is_active = models.BooleanField( + _("active"), + default=True, + help_text=_( + "Designates whether this user should be treated as active. " + "Unselect this instead of deleting accounts." + ), + ) + date_joined = models.DateTimeField(_("date joined"), default=timezone.now) + + objects = UserManager() + + EMAIL_FIELD = "email" + USERNAME_FIELD = "username" + REQUIRED_FIELDS = ["email"] + + class Meta: + verbose_name = _("user") + verbose_name_plural = _("users") + abstract = True + + def clean(self): + super().clean() + self.email = self.__class__.objects.normalize_email(self.email) + + def get_full_name(self): + """ + Return the first_name plus the last_name, with a space in between. + """ + full_name = "%s %s" % (self.first_name, self.last_name) + return full_name.strip() + + def get_short_name(self): + """Return the short name for the user.""" + return self.first_name + + def email_user(self, subject, message, from_email=None, **kwargs): + """Send an email to this user.""" + send_mail(subject, message, from_email, [self.email], **kwargs) + + +class User(AbstractUser): + """ + Users within the Django authentication system are represented by this + model. + + Username and password are required. Other fields are optional. + """ + + class Meta(AbstractUser.Meta): + swappable = "AUTH_USER_MODEL" + + +class AnonymousUser: + id = None + pk = None + username = "" + is_staff = False + is_active = False + is_superuser = False + _groups = EmptyManager(Group) + _user_permissions = EmptyManager(Permission) + + def __str__(self): + return "AnonymousUser" + + def __eq__(self, other): + return isinstance(other, self.__class__) + + def __hash__(self): + return 1 # instances always return the same hash value + + def __int__(self): + raise TypeError( + "Cannot cast AnonymousUser to int. Are you trying to use it in place of " + "User?" + ) + + def save(self): + raise NotImplementedError( + "Django doesn't provide a DB representation for AnonymousUser." + ) + + def delete(self): + raise NotImplementedError( + "Django doesn't provide a DB representation for AnonymousUser." + ) + + def set_password(self, raw_password): + raise NotImplementedError( + "Django doesn't provide a DB representation for AnonymousUser." + ) + + def check_password(self, raw_password): + raise NotImplementedError( + "Django doesn't provide a DB representation for AnonymousUser." + ) + + @property + def groups(self): + return self._groups + + @property + def user_permissions(self): + return self._user_permissions + + def get_user_permissions(self, obj=None): + return _user_get_permissions(self, obj, "user") + + def get_group_permissions(self, obj=None): + return set() + + def get_all_permissions(self, obj=None): + return _user_get_permissions(self, obj, "all") + + def has_perm(self, perm, obj=None): + return _user_has_perm(self, perm, obj=obj) + + def has_perms(self, perm_list, obj=None): + if not is_iterable(perm_list) or isinstance(perm_list, str): + raise ValueError("perm_list must be an iterable of permissions.") + return all(self.has_perm(perm, obj) for perm in perm_list) + + def has_module_perms(self, module): + return _user_has_module_perms(self, module) + + @property + def is_anonymous(self): + return True + + @property + def is_authenticated(self): + return False + + def get_username(self): + return self.username diff --git a/virt/lib/python3.9/site-packages/django/contrib/gis/db/models/functions 3.py b/virt/lib/python3.9/site-packages/django/contrib/gis/db/models/functions 3.py new file mode 100644 index 00000000..635e9e85 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/contrib/gis/db/models/functions 3.py @@ -0,0 +1,564 @@ +from decimal import Decimal + +from django.contrib.gis.db.models.fields import BaseSpatialField, GeometryField +from django.contrib.gis.db.models.sql import AreaField, DistanceField +from django.contrib.gis.geos import GEOSGeometry +from django.core.exceptions import FieldError +from django.db import NotSupportedError +from django.db.models import ( + BinaryField, + BooleanField, + FloatField, + Func, + IntegerField, + TextField, + Transform, + Value, +) +from django.db.models.functions import Cast +from django.utils.functional import cached_property + +NUMERIC_TYPES = (int, float, Decimal) + + +class GeoFuncMixin: + function = None + geom_param_pos = (0,) + + def __init__(self, *expressions, **extra): + super().__init__(*expressions, **extra) + + # Ensure that value expressions are geometric. + for pos in self.geom_param_pos: + expr = self.source_expressions[pos] + if not isinstance(expr, Value): + continue + try: + output_field = expr.output_field + except FieldError: + output_field = None + geom = expr.value + if ( + not isinstance(geom, GEOSGeometry) + or output_field + and not isinstance(output_field, GeometryField) + ): + raise TypeError( + "%s function requires a geometric argument in position %d." + % (self.name, pos + 1) + ) + if not geom.srid and not output_field: + raise ValueError("SRID is required for all geometries.") + if not output_field: + self.source_expressions[pos] = Value( + geom, output_field=GeometryField(srid=geom.srid) + ) + + @property + def name(self): + return self.__class__.__name__ + + @cached_property + def geo_field(self): + return self.source_expressions[self.geom_param_pos[0]].field + + def as_sql(self, compiler, connection, function=None, **extra_context): + if self.function is None and function is None: + function = connection.ops.spatial_function_name(self.name) + return super().as_sql(compiler, connection, function=function, **extra_context) + + def resolve_expression(self, *args, **kwargs): + res = super().resolve_expression(*args, **kwargs) + if not self.geom_param_pos: + return res + + # Ensure that expressions are geometric. + source_fields = res.get_source_fields() + for pos in self.geom_param_pos: + field = source_fields[pos] + if not isinstance(field, GeometryField): + raise TypeError( + "%s function requires a GeometryField in position %s, got %s." + % ( + self.name, + pos + 1, + type(field).__name__, + ) + ) + + base_srid = res.geo_field.srid + for pos in self.geom_param_pos[1:]: + expr = res.source_expressions[pos] + expr_srid = expr.output_field.srid + if expr_srid != base_srid: + # Automatic SRID conversion so objects are comparable. + res.source_expressions[pos] = Transform( + expr, base_srid + ).resolve_expression(*args, **kwargs) + return res + + def _handle_param(self, value, param_name="", check_types=None): + if not hasattr(value, "resolve_expression"): + if check_types and not isinstance(value, check_types): + raise TypeError( + "The %s parameter has the wrong type: should be %s." + % (param_name, check_types) + ) + return value + + +class GeoFunc(GeoFuncMixin, Func): + pass + + +class GeomOutputGeoFunc(GeoFunc): + @cached_property + def output_field(self): + return GeometryField(srid=self.geo_field.srid) + + +class SQLiteDecimalToFloatMixin: + """ + By default, Decimal values are converted to str by the SQLite backend, which + is not acceptable by the GIS functions expecting numeric values. + """ + + def as_sqlite(self, compiler, connection, **extra_context): + copy = self.copy() + copy.set_source_expressions( + [ + Value(float(expr.value)) + if hasattr(expr, "value") and isinstance(expr.value, Decimal) + else expr + for expr in copy.get_source_expressions() + ] + ) + return copy.as_sql(compiler, connection, **extra_context) + + +class OracleToleranceMixin: + tolerance = 0.05 + + def as_oracle(self, compiler, connection, **extra_context): + tolerance = Value( + self._handle_param( + self.extra.get("tolerance", self.tolerance), + "tolerance", + NUMERIC_TYPES, + ) + ) + clone = self.copy() + clone.set_source_expressions([*self.get_source_expressions(), tolerance]) + return clone.as_sql(compiler, connection, **extra_context) + + +class Area(OracleToleranceMixin, GeoFunc): + arity = 1 + + @cached_property + def output_field(self): + return AreaField(self.geo_field) + + def as_sql(self, compiler, connection, **extra_context): + if not connection.features.supports_area_geodetic and self.geo_field.geodetic( + connection + ): + raise NotSupportedError( + "Area on geodetic coordinate systems not supported." + ) + return super().as_sql(compiler, connection, **extra_context) + + def as_sqlite(self, compiler, connection, **extra_context): + if self.geo_field.geodetic(connection): + extra_context["template"] = "%(function)s(%(expressions)s, %(spheroid)d)" + extra_context["spheroid"] = True + return self.as_sql(compiler, connection, **extra_context) + + +class Azimuth(GeoFunc): + output_field = FloatField() + arity = 2 + geom_param_pos = (0, 1) + + +class AsGeoJSON(GeoFunc): + output_field = TextField() + + def __init__(self, expression, bbox=False, crs=False, precision=8, **extra): + expressions = [expression] + if precision is not None: + expressions.append(self._handle_param(precision, "precision", int)) + options = 0 + if crs and bbox: + options = 3 + elif bbox: + options = 1 + elif crs: + options = 2 + if options: + expressions.append(options) + super().__init__(*expressions, **extra) + + def as_oracle(self, compiler, connection, **extra_context): + source_expressions = self.get_source_expressions() + clone = self.copy() + clone.set_source_expressions(source_expressions[:1]) + return super(AsGeoJSON, clone).as_sql(compiler, connection, **extra_context) + + +class AsGML(GeoFunc): + geom_param_pos = (1,) + output_field = TextField() + + def __init__(self, expression, version=2, precision=8, **extra): + expressions = [version, expression] + if precision is not None: + expressions.append(self._handle_param(precision, "precision", int)) + super().__init__(*expressions, **extra) + + def as_oracle(self, compiler, connection, **extra_context): + source_expressions = self.get_source_expressions() + version = source_expressions[0] + clone = self.copy() + clone.set_source_expressions([source_expressions[1]]) + extra_context["function"] = ( + "SDO_UTIL.TO_GML311GEOMETRY" + if version.value == 3 + else "SDO_UTIL.TO_GMLGEOMETRY" + ) + return super(AsGML, clone).as_sql(compiler, connection, **extra_context) + + +class AsKML(GeoFunc): + output_field = TextField() + + def __init__(self, expression, precision=8, **extra): + expressions = [expression] + if precision is not None: + expressions.append(self._handle_param(precision, "precision", int)) + super().__init__(*expressions, **extra) + + +class AsSVG(GeoFunc): + output_field = TextField() + + def __init__(self, expression, relative=False, precision=8, **extra): + relative = ( + relative if hasattr(relative, "resolve_expression") else int(relative) + ) + expressions = [ + expression, + relative, + self._handle_param(precision, "precision", int), + ] + super().__init__(*expressions, **extra) + + +class AsWKB(GeoFunc): + output_field = BinaryField() + arity = 1 + + +class AsWKT(GeoFunc): + output_field = TextField() + arity = 1 + + +class BoundingCircle(OracleToleranceMixin, GeomOutputGeoFunc): + def __init__(self, expression, num_seg=48, **extra): + super().__init__(expression, num_seg, **extra) + + def as_oracle(self, compiler, connection, **extra_context): + clone = self.copy() + clone.set_source_expressions([self.get_source_expressions()[0]]) + return super(BoundingCircle, clone).as_oracle( + compiler, connection, **extra_context + ) + + +class Centroid(OracleToleranceMixin, GeomOutputGeoFunc): + arity = 1 + + +class Difference(OracleToleranceMixin, GeomOutputGeoFunc): + arity = 2 + geom_param_pos = (0, 1) + + +class DistanceResultMixin: + @cached_property + def output_field(self): + return DistanceField(self.geo_field) + + def source_is_geography(self): + return self.geo_field.geography and self.geo_field.srid == 4326 + + +class Distance(DistanceResultMixin, OracleToleranceMixin, GeoFunc): + geom_param_pos = (0, 1) + spheroid = None + + def __init__(self, expr1, expr2, spheroid=None, **extra): + expressions = [expr1, expr2] + if spheroid is not None: + self.spheroid = self._handle_param(spheroid, "spheroid", bool) + super().__init__(*expressions, **extra) + + def as_postgresql(self, compiler, connection, **extra_context): + clone = self.copy() + function = None + expr2 = clone.source_expressions[1] + geography = self.source_is_geography() + if expr2.output_field.geography != geography: + if isinstance(expr2, Value): + expr2.output_field.geography = geography + else: + clone.source_expressions[1] = Cast( + expr2, + GeometryField(srid=expr2.output_field.srid, geography=geography), + ) + + if not geography and self.geo_field.geodetic(connection): + # Geometry fields with geodetic (lon/lat) coordinates need special + # distance functions. + if self.spheroid: + # DistanceSpheroid is more accurate and resource intensive than + # DistanceSphere. + function = connection.ops.spatial_function_name("DistanceSpheroid") + # Replace boolean param by the real spheroid of the base field + clone.source_expressions.append( + Value(self.geo_field.spheroid(connection)) + ) + else: + function = connection.ops.spatial_function_name("DistanceSphere") + return super(Distance, clone).as_sql( + compiler, connection, function=function, **extra_context + ) + + def as_sqlite(self, compiler, connection, **extra_context): + if self.geo_field.geodetic(connection): + # SpatiaLite returns NULL instead of zero on geodetic coordinates + extra_context[ + "template" + ] = "COALESCE(%(function)s(%(expressions)s, %(spheroid)s), 0)" + extra_context["spheroid"] = int(bool(self.spheroid)) + return super().as_sql(compiler, connection, **extra_context) + + +class Envelope(GeomOutputGeoFunc): + arity = 1 + + +class ForcePolygonCW(GeomOutputGeoFunc): + arity = 1 + + +class FromWKB(GeoFunc): + output_field = GeometryField(srid=0) + arity = 1 + geom_param_pos = () + + +class FromWKT(GeoFunc): + output_field = GeometryField(srid=0) + arity = 1 + geom_param_pos = () + + +class GeoHash(GeoFunc): + output_field = TextField() + + def __init__(self, expression, precision=None, **extra): + expressions = [expression] + if precision is not None: + expressions.append(self._handle_param(precision, "precision", int)) + super().__init__(*expressions, **extra) + + def as_mysql(self, compiler, connection, **extra_context): + clone = self.copy() + # If no precision is provided, set it to the maximum. + if len(clone.source_expressions) < 2: + clone.source_expressions.append(Value(100)) + return clone.as_sql(compiler, connection, **extra_context) + + +class GeometryDistance(GeoFunc): + output_field = FloatField() + arity = 2 + function = "" + arg_joiner = " <-> " + geom_param_pos = (0, 1) + + +class Intersection(OracleToleranceMixin, GeomOutputGeoFunc): + arity = 2 + geom_param_pos = (0, 1) + + +@BaseSpatialField.register_lookup +class IsEmpty(GeoFuncMixin, Transform): + lookup_name = "isempty" + output_field = BooleanField() + + +@BaseSpatialField.register_lookup +class IsValid(OracleToleranceMixin, GeoFuncMixin, Transform): + lookup_name = "isvalid" + output_field = BooleanField() + + def as_oracle(self, compiler, connection, **extra_context): + sql, params = super().as_oracle(compiler, connection, **extra_context) + return "CASE %s WHEN 'TRUE' THEN 1 ELSE 0 END" % sql, params + + +class Length(DistanceResultMixin, OracleToleranceMixin, GeoFunc): + def __init__(self, expr1, spheroid=True, **extra): + self.spheroid = spheroid + super().__init__(expr1, **extra) + + def as_sql(self, compiler, connection, **extra_context): + if ( + self.geo_field.geodetic(connection) + and not connection.features.supports_length_geodetic + ): + raise NotSupportedError( + "This backend doesn't support Length on geodetic fields" + ) + return super().as_sql(compiler, connection, **extra_context) + + def as_postgresql(self, compiler, connection, **extra_context): + clone = self.copy() + function = None + if self.source_is_geography(): + clone.source_expressions.append(Value(self.spheroid)) + elif self.geo_field.geodetic(connection): + # Geometry fields with geodetic (lon/lat) coordinates need length_spheroid + function = connection.ops.spatial_function_name("LengthSpheroid") + clone.source_expressions.append(Value(self.geo_field.spheroid(connection))) + else: + dim = min(f.dim for f in self.get_source_fields() if f) + if dim > 2: + function = connection.ops.length3d + return super(Length, clone).as_sql( + compiler, connection, function=function, **extra_context + ) + + def as_sqlite(self, compiler, connection, **extra_context): + function = None + if self.geo_field.geodetic(connection): + function = "GeodesicLength" if self.spheroid else "GreatCircleLength" + return super().as_sql(compiler, connection, function=function, **extra_context) + + +class LineLocatePoint(GeoFunc): + output_field = FloatField() + arity = 2 + geom_param_pos = (0, 1) + + +class MakeValid(GeomOutputGeoFunc): + pass + + +class MemSize(GeoFunc): + output_field = IntegerField() + arity = 1 + + +class NumGeometries(GeoFunc): + output_field = IntegerField() + arity = 1 + + +class NumPoints(GeoFunc): + output_field = IntegerField() + arity = 1 + + +class Perimeter(DistanceResultMixin, OracleToleranceMixin, GeoFunc): + arity = 1 + + def as_postgresql(self, compiler, connection, **extra_context): + function = None + if self.geo_field.geodetic(connection) and not self.source_is_geography(): + raise NotSupportedError( + "ST_Perimeter cannot use a non-projected non-geography field." + ) + dim = min(f.dim for f in self.get_source_fields()) + if dim > 2: + function = connection.ops.perimeter3d + return super().as_sql(compiler, connection, function=function, **extra_context) + + def as_sqlite(self, compiler, connection, **extra_context): + if self.geo_field.geodetic(connection): + raise NotSupportedError("Perimeter cannot use a non-projected field.") + return super().as_sql(compiler, connection, **extra_context) + + +class PointOnSurface(OracleToleranceMixin, GeomOutputGeoFunc): + arity = 1 + + +class Reverse(GeoFunc): + arity = 1 + + +class Scale(SQLiteDecimalToFloatMixin, GeomOutputGeoFunc): + def __init__(self, expression, x, y, z=0.0, **extra): + expressions = [ + expression, + self._handle_param(x, "x", NUMERIC_TYPES), + self._handle_param(y, "y", NUMERIC_TYPES), + ] + if z != 0.0: + expressions.append(self._handle_param(z, "z", NUMERIC_TYPES)) + super().__init__(*expressions, **extra) + + +class SnapToGrid(SQLiteDecimalToFloatMixin, GeomOutputGeoFunc): + def __init__(self, expression, *args, **extra): + nargs = len(args) + expressions = [expression] + if nargs in (1, 2): + expressions.extend( + [self._handle_param(arg, "", NUMERIC_TYPES) for arg in args] + ) + elif nargs == 4: + # Reverse origin and size param ordering + expressions += [ + *(self._handle_param(arg, "", NUMERIC_TYPES) for arg in args[2:]), + *(self._handle_param(arg, "", NUMERIC_TYPES) for arg in args[0:2]), + ] + else: + raise ValueError("Must provide 1, 2, or 4 arguments to `SnapToGrid`.") + super().__init__(*expressions, **extra) + + +class SymDifference(OracleToleranceMixin, GeomOutputGeoFunc): + arity = 2 + geom_param_pos = (0, 1) + + +class Transform(GeomOutputGeoFunc): + def __init__(self, expression, srid, **extra): + expressions = [ + expression, + self._handle_param(srid, "srid", int), + ] + if "output_field" not in extra: + extra["output_field"] = GeometryField(srid=srid) + super().__init__(*expressions, **extra) + + +class Translate(Scale): + def as_sqlite(self, compiler, connection, **extra_context): + clone = self.copy() + if len(self.source_expressions) < 4: + # Always provide the z parameter for ST_Translate + clone.source_expressions.append(Value(0)) + return super(Translate, clone).as_sqlite(compiler, connection, **extra_context) + + +class Union(OracleToleranceMixin, GeomOutputGeoFunc): + arity = 2 + geom_param_pos = (0, 1) diff --git a/virt/lib/python3.9/site-packages/django/core/mail/message 3.py b/virt/lib/python3.9/site-packages/django/core/mail/message 3.py new file mode 100644 index 00000000..7eb27c59 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/core/mail/message 3.py @@ -0,0 +1,493 @@ +import mimetypes +from email import charset as Charset +from email import encoders as Encoders +from email import generator, message_from_string +from email.errors import HeaderParseError +from email.header import Header +from email.headerregistry import Address, parser +from email.message import Message +from email.mime.base import MIMEBase +from email.mime.message import MIMEMessage +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from email.utils import formataddr, formatdate, getaddresses, make_msgid +from io import BytesIO, StringIO +from pathlib import Path + +from django.conf import settings +from django.core.mail.utils import DNS_NAME +from django.utils.encoding import force_str, punycode + +# Don't BASE64-encode UTF-8 messages so that we avoid unwanted attention from +# some spam filters. +utf8_charset = Charset.Charset("utf-8") +utf8_charset.body_encoding = None # Python defaults to BASE64 +utf8_charset_qp = Charset.Charset("utf-8") +utf8_charset_qp.body_encoding = Charset.QP + +# Default MIME type to use on attachments (if it is not explicitly given +# and cannot be guessed). +DEFAULT_ATTACHMENT_MIME_TYPE = "application/octet-stream" + +RFC5322_EMAIL_LINE_LENGTH_LIMIT = 998 + + +class BadHeaderError(ValueError): + pass + + +# Header names that contain structured address data (RFC 5322). +ADDRESS_HEADERS = { + "from", + "sender", + "reply-to", + "to", + "cc", + "bcc", + "resent-from", + "resent-sender", + "resent-to", + "resent-cc", + "resent-bcc", +} + + +def forbid_multi_line_headers(name, val, encoding): + """Forbid multi-line headers to prevent header injection.""" + encoding = encoding or settings.DEFAULT_CHARSET + val = str(val) # val may be lazy + if "\n" in val or "\r" in val: + raise BadHeaderError( + "Header values can't contain newlines (got %r for header %r)" % (val, name) + ) + try: + val.encode("ascii") + except UnicodeEncodeError: + if name.lower() in ADDRESS_HEADERS: + val = ", ".join( + sanitize_address(addr, encoding) for addr in getaddresses((val,)) + ) + else: + val = Header(val, encoding).encode() + else: + if name.lower() == "subject": + val = Header(val).encode() + return name, val + + +def sanitize_address(addr, encoding): + """ + Format a pair of (name, address) or an email address string. + """ + address = None + if not isinstance(addr, tuple): + addr = force_str(addr) + try: + token, rest = parser.get_mailbox(addr) + except (HeaderParseError, ValueError, IndexError): + raise ValueError('Invalid address "%s"' % addr) + else: + if rest: + # The entire email address must be parsed. + raise ValueError( + 'Invalid address; only %s could be parsed from "%s"' % (token, addr) + ) + nm = token.display_name or "" + localpart = token.local_part + domain = token.domain or "" + else: + nm, address = addr + localpart, domain = address.rsplit("@", 1) + + address_parts = nm + localpart + domain + if "\n" in address_parts or "\r" in address_parts: + raise ValueError("Invalid address; address parts cannot contain newlines.") + + # Avoid UTF-8 encode, if it's possible. + try: + nm.encode("ascii") + nm = Header(nm).encode() + except UnicodeEncodeError: + nm = Header(nm, encoding).encode() + try: + localpart.encode("ascii") + except UnicodeEncodeError: + localpart = Header(localpart, encoding).encode() + domain = punycode(domain) + + parsed_address = Address(username=localpart, domain=domain) + return formataddr((nm, parsed_address.addr_spec)) + + +class MIMEMixin: + def as_string(self, unixfrom=False, linesep="\n"): + """Return the entire formatted message as a string. + Optional `unixfrom' when True, means include the Unix From_ envelope + header. + + This overrides the default as_string() implementation to not mangle + lines that begin with 'From '. See bug #13433 for details. + """ + fp = StringIO() + g = generator.Generator(fp, mangle_from_=False) + g.flatten(self, unixfrom=unixfrom, linesep=linesep) + return fp.getvalue() + + def as_bytes(self, unixfrom=False, linesep="\n"): + """Return the entire formatted message as bytes. + Optional `unixfrom' when True, means include the Unix From_ envelope + header. + + This overrides the default as_bytes() implementation to not mangle + lines that begin with 'From '. See bug #13433 for details. + """ + fp = BytesIO() + g = generator.BytesGenerator(fp, mangle_from_=False) + g.flatten(self, unixfrom=unixfrom, linesep=linesep) + return fp.getvalue() + + +class SafeMIMEMessage(MIMEMixin, MIMEMessage): + def __setitem__(self, name, val): + # message/rfc822 attachments must be ASCII + name, val = forbid_multi_line_headers(name, val, "ascii") + MIMEMessage.__setitem__(self, name, val) + + +class SafeMIMEText(MIMEMixin, MIMEText): + def __init__(self, _text, _subtype="plain", _charset=None): + self.encoding = _charset + MIMEText.__init__(self, _text, _subtype=_subtype, _charset=_charset) + + def __setitem__(self, name, val): + name, val = forbid_multi_line_headers(name, val, self.encoding) + MIMEText.__setitem__(self, name, val) + + def set_payload(self, payload, charset=None): + if charset == "utf-8" and not isinstance(charset, Charset.Charset): + has_long_lines = any( + len(line.encode()) > RFC5322_EMAIL_LINE_LENGTH_LIMIT + for line in payload.splitlines() + ) + # Quoted-Printable encoding has the side effect of shortening long + # lines, if any (#22561). + charset = utf8_charset_qp if has_long_lines else utf8_charset + MIMEText.set_payload(self, payload, charset=charset) + + +class SafeMIMEMultipart(MIMEMixin, MIMEMultipart): + def __init__( + self, _subtype="mixed", boundary=None, _subparts=None, encoding=None, **_params + ): + self.encoding = encoding + MIMEMultipart.__init__(self, _subtype, boundary, _subparts, **_params) + + def __setitem__(self, name, val): + name, val = forbid_multi_line_headers(name, val, self.encoding) + MIMEMultipart.__setitem__(self, name, val) + + +class EmailMessage: + """A container for email information.""" + + content_subtype = "plain" + mixed_subtype = "mixed" + encoding = None # None => use settings default + + def __init__( + self, + subject="", + body="", + from_email=None, + to=None, + bcc=None, + connection=None, + attachments=None, + headers=None, + cc=None, + reply_to=None, + ): + """ + Initialize a single email message (which can be sent to multiple + recipients). + """ + if to: + if isinstance(to, str): + raise TypeError('"to" argument must be a list or tuple') + self.to = list(to) + else: + self.to = [] + if cc: + if isinstance(cc, str): + raise TypeError('"cc" argument must be a list or tuple') + self.cc = list(cc) + else: + self.cc = [] + if bcc: + if isinstance(bcc, str): + raise TypeError('"bcc" argument must be a list or tuple') + self.bcc = list(bcc) + else: + self.bcc = [] + if reply_to: + if isinstance(reply_to, str): + raise TypeError('"reply_to" argument must be a list or tuple') + self.reply_to = list(reply_to) + else: + self.reply_to = [] + self.from_email = from_email or settings.DEFAULT_FROM_EMAIL + self.subject = subject + self.body = body or "" + self.attachments = [] + if attachments: + for attachment in attachments: + if isinstance(attachment, MIMEBase): + self.attach(attachment) + else: + self.attach(*attachment) + self.extra_headers = headers or {} + self.connection = connection + + def get_connection(self, fail_silently=False): + from django.core.mail import get_connection + + if not self.connection: + self.connection = get_connection(fail_silently=fail_silently) + return self.connection + + def message(self): + encoding = self.encoding or settings.DEFAULT_CHARSET + msg = SafeMIMEText(self.body, self.content_subtype, encoding) + msg = self._create_message(msg) + msg["Subject"] = self.subject + msg["From"] = self.extra_headers.get("From", self.from_email) + self._set_list_header_if_not_empty(msg, "To", self.to) + self._set_list_header_if_not_empty(msg, "Cc", self.cc) + self._set_list_header_if_not_empty(msg, "Reply-To", self.reply_to) + + # Email header names are case-insensitive (RFC 2045), so we have to + # accommodate that when doing comparisons. + header_names = [key.lower() for key in self.extra_headers] + if "date" not in header_names: + # formatdate() uses stdlib methods to format the date, which use + # the stdlib/OS concept of a timezone, however, Django sets the + # TZ environment variable based on the TIME_ZONE setting which + # will get picked up by formatdate(). + msg["Date"] = formatdate(localtime=settings.EMAIL_USE_LOCALTIME) + if "message-id" not in header_names: + # Use cached DNS_NAME for performance + msg["Message-ID"] = make_msgid(domain=DNS_NAME) + for name, value in self.extra_headers.items(): + if name.lower() != "from": # From is already handled + msg[name] = value + return msg + + def recipients(self): + """ + Return a list of all recipients of the email (includes direct + addressees as well as Cc and Bcc entries). + """ + return [email for email in (self.to + self.cc + self.bcc) if email] + + def send(self, fail_silently=False): + """Send the email message.""" + if not self.recipients(): + # Don't bother creating the network connection if there's nobody to + # send to. + return 0 + return self.get_connection(fail_silently).send_messages([self]) + + def attach(self, filename=None, content=None, mimetype=None): + """ + Attach a file with the given filename and content. The filename can + be omitted and the mimetype is guessed, if not provided. + + If the first parameter is a MIMEBase subclass, insert it directly + into the resulting message attachments. + + For a text/* mimetype (guessed or specified), when a bytes object is + specified as content, decode it as UTF-8. If that fails, set the + mimetype to DEFAULT_ATTACHMENT_MIME_TYPE and don't decode the content. + """ + if isinstance(filename, MIMEBase): + if content is not None or mimetype is not None: + raise ValueError( + "content and mimetype must not be given when a MIMEBase " + "instance is provided." + ) + self.attachments.append(filename) + elif content is None: + raise ValueError("content must be provided.") + else: + mimetype = ( + mimetype + or mimetypes.guess_type(filename)[0] + or DEFAULT_ATTACHMENT_MIME_TYPE + ) + basetype, subtype = mimetype.split("/", 1) + + if basetype == "text": + if isinstance(content, bytes): + try: + content = content.decode() + except UnicodeDecodeError: + # If mimetype suggests the file is text but it's + # actually binary, read() raises a UnicodeDecodeError. + mimetype = DEFAULT_ATTACHMENT_MIME_TYPE + + self.attachments.append((filename, content, mimetype)) + + def attach_file(self, path, mimetype=None): + """ + Attach a file from the filesystem. + + Set the mimetype to DEFAULT_ATTACHMENT_MIME_TYPE if it isn't specified + and cannot be guessed. + + For a text/* mimetype (guessed or specified), decode the file's content + as UTF-8. If that fails, set the mimetype to + DEFAULT_ATTACHMENT_MIME_TYPE and don't decode the content. + """ + path = Path(path) + with path.open("rb") as file: + content = file.read() + self.attach(path.name, content, mimetype) + + def _create_message(self, msg): + return self._create_attachments(msg) + + def _create_attachments(self, msg): + if self.attachments: + encoding = self.encoding or settings.DEFAULT_CHARSET + body_msg = msg + msg = SafeMIMEMultipart(_subtype=self.mixed_subtype, encoding=encoding) + if self.body or body_msg.is_multipart(): + msg.attach(body_msg) + for attachment in self.attachments: + if isinstance(attachment, MIMEBase): + msg.attach(attachment) + else: + msg.attach(self._create_attachment(*attachment)) + return msg + + def _create_mime_attachment(self, content, mimetype): + """ + Convert the content, mimetype pair into a MIME attachment object. + + If the mimetype is message/rfc822, content may be an + email.Message or EmailMessage object, as well as a str. + """ + basetype, subtype = mimetype.split("/", 1) + if basetype == "text": + encoding = self.encoding or settings.DEFAULT_CHARSET + attachment = SafeMIMEText(content, subtype, encoding) + elif basetype == "message" and subtype == "rfc822": + # Bug #18967: Per RFC 2046 Section 5.2.1, message/rfc822 + # attachments must not be base64 encoded. + if isinstance(content, EmailMessage): + # convert content into an email.Message first + content = content.message() + elif not isinstance(content, Message): + # For compatibility with existing code, parse the message + # into an email.Message object if it is not one already. + content = message_from_string(force_str(content)) + + attachment = SafeMIMEMessage(content, subtype) + else: + # Encode non-text attachments with base64. + attachment = MIMEBase(basetype, subtype) + attachment.set_payload(content) + Encoders.encode_base64(attachment) + return attachment + + def _create_attachment(self, filename, content, mimetype=None): + """ + Convert the filename, content, mimetype triple into a MIME attachment + object. + """ + attachment = self._create_mime_attachment(content, mimetype) + if filename: + try: + filename.encode("ascii") + except UnicodeEncodeError: + filename = ("utf-8", "", filename) + attachment.add_header( + "Content-Disposition", "attachment", filename=filename + ) + return attachment + + def _set_list_header_if_not_empty(self, msg, header, values): + """ + Set msg's header, either from self.extra_headers, if present, or from + the values argument. + """ + if values: + try: + value = self.extra_headers[header] + except KeyError: + value = ", ".join(str(v) for v in values) + msg[header] = value + + +class EmailMultiAlternatives(EmailMessage): + """ + A version of EmailMessage that makes it easy to send multipart/alternative + messages. For example, including text and HTML versions of the text is + made easier. + """ + + alternative_subtype = "alternative" + + def __init__( + self, + subject="", + body="", + from_email=None, + to=None, + bcc=None, + connection=None, + attachments=None, + headers=None, + alternatives=None, + cc=None, + reply_to=None, + ): + """ + Initialize a single email message (which can be sent to multiple + recipients). + """ + super().__init__( + subject, + body, + from_email, + to, + bcc, + connection, + attachments, + headers, + cc, + reply_to, + ) + self.alternatives = alternatives or [] + + def attach_alternative(self, content, mimetype): + """Attach an alternative content representation.""" + if content is None or mimetype is None: + raise ValueError("Both content and mimetype must be provided.") + self.alternatives.append((content, mimetype)) + + def _create_message(self, msg): + return self._create_attachments(self._create_alternatives(msg)) + + def _create_alternatives(self, msg): + encoding = self.encoding or settings.DEFAULT_CHARSET + if self.alternatives: + body_msg = msg + msg = SafeMIMEMultipart( + _subtype=self.alternative_subtype, encoding=encoding + ) + if self.body: + msg.attach(body_msg) + for alternative in self.alternatives: + msg.attach(self._create_mime_attachment(*alternative)) + return msg diff --git a/virt/lib/python3.9/site-packages/django/core/validators 3.py b/virt/lib/python3.9/site-packages/django/core/validators 3.py new file mode 100644 index 00000000..82cd3c1a --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/core/validators 3.py @@ -0,0 +1,623 @@ +import ipaddress +import math +import re +from pathlib import Path +from urllib.parse import urlsplit, urlunsplit + +from django.core.exceptions import ValidationError +from django.utils.deconstruct import deconstructible +from django.utils.encoding import punycode +from django.utils.ipv6 import is_valid_ipv6_address +from django.utils.regex_helper import _lazy_re_compile +from django.utils.translation import gettext_lazy as _ +from django.utils.translation import ngettext_lazy + +# These values, if given to validate(), will trigger the self.required check. +EMPTY_VALUES = (None, "", [], (), {}) + + +@deconstructible +class RegexValidator: + regex = "" + message = _("Enter a valid value.") + code = "invalid" + inverse_match = False + flags = 0 + + def __init__( + self, regex=None, message=None, code=None, inverse_match=None, flags=None + ): + if regex is not None: + self.regex = regex + if message is not None: + self.message = message + if code is not None: + self.code = code + if inverse_match is not None: + self.inverse_match = inverse_match + if flags is not None: + self.flags = flags + if self.flags and not isinstance(self.regex, str): + raise TypeError( + "If the flags are set, regex must be a regular expression string." + ) + + self.regex = _lazy_re_compile(self.regex, self.flags) + + def __call__(self, value): + """ + Validate that the input contains (or does *not* contain, if + inverse_match is True) a match for the regular expression. + """ + regex_matches = self.regex.search(str(value)) + invalid_input = regex_matches if self.inverse_match else not regex_matches + if invalid_input: + raise ValidationError(self.message, code=self.code, params={"value": value}) + + def __eq__(self, other): + return ( + isinstance(other, RegexValidator) + and self.regex.pattern == other.regex.pattern + and self.regex.flags == other.regex.flags + and (self.message == other.message) + and (self.code == other.code) + and (self.inverse_match == other.inverse_match) + ) + + +@deconstructible +class URLValidator(RegexValidator): + ul = "\u00a1-\uffff" # Unicode letters range (must not be a raw string). + + # IP patterns + ipv4_re = ( + r"(?:0|25[0-5]|2[0-4][0-9]|1[0-9]?[0-9]?|[1-9][0-9]?)" + r"(?:\.(?:0|25[0-5]|2[0-4][0-9]|1[0-9]?[0-9]?|[1-9][0-9]?)){3}" + ) + ipv6_re = r"\[[0-9a-f:.]+\]" # (simple regex, validated later) + + # Host patterns + hostname_re = ( + r"[a-z" + ul + r"0-9](?:[a-z" + ul + r"0-9-]{0,61}[a-z" + ul + r"0-9])?" + ) + # Max length for domain name labels is 63 characters per RFC 1034 sec. 3.1 + domain_re = r"(?:\.(?!-)[a-z" + ul + r"0-9-]{1,63}(?<!-))*" + tld_re = ( + r"\." # dot + r"(?!-)" # can't start with a dash + r"(?:[a-z" + ul + "-]{2,63}" # domain label + r"|xn--[a-z0-9]{1,59})" # or punycode label + r"(?<!-)" # can't end with a dash + r"\.?" # may have a trailing dot + ) + host_re = "(" + hostname_re + domain_re + tld_re + "|localhost)" + + regex = _lazy_re_compile( + r"^(?:[a-z0-9.+-]*)://" # scheme is validated separately + r"(?:[^\s:@/]+(?::[^\s:@/]*)?@)?" # user:pass authentication + r"(?:" + ipv4_re + "|" + ipv6_re + "|" + host_re + ")" + r"(?::[0-9]{1,5})?" # port + r"(?:[/?#][^\s]*)?" # resource path + r"\Z", + re.IGNORECASE, + ) + message = _("Enter a valid URL.") + schemes = ["http", "https", "ftp", "ftps"] + unsafe_chars = frozenset("\t\r\n") + max_length = 2048 + + def __init__(self, schemes=None, **kwargs): + super().__init__(**kwargs) + if schemes is not None: + self.schemes = schemes + + def __call__(self, value): + if not isinstance(value, str) or len(value) > self.max_length: + raise ValidationError(self.message, code=self.code, params={"value": value}) + if self.unsafe_chars.intersection(value): + raise ValidationError(self.message, code=self.code, params={"value": value}) + # Check if the scheme is valid. + scheme = value.split("://")[0].lower() + if scheme not in self.schemes: + raise ValidationError(self.message, code=self.code, params={"value": value}) + + # Then check full URL + try: + splitted_url = urlsplit(value) + except ValueError: + raise ValidationError(self.message, code=self.code, params={"value": value}) + try: + super().__call__(value) + except ValidationError as e: + # Trivial case failed. Try for possible IDN domain + if value: + scheme, netloc, path, query, fragment = splitted_url + try: + netloc = punycode(netloc) # IDN -> ACE + except UnicodeError: # invalid domain part + raise e + url = urlunsplit((scheme, netloc, path, query, fragment)) + super().__call__(url) + else: + raise + else: + # Now verify IPv6 in the netloc part + host_match = re.search(r"^\[(.+)\](?::[0-9]{1,5})?$", splitted_url.netloc) + if host_match: + potential_ip = host_match[1] + try: + validate_ipv6_address(potential_ip) + except ValidationError: + raise ValidationError( + self.message, code=self.code, params={"value": value} + ) + + # The maximum length of a full host name is 253 characters per RFC 1034 + # section 3.1. It's defined to be 255 bytes or less, but this includes + # one byte for the length of the name and one byte for the trailing dot + # that's used to indicate absolute names in DNS. + if splitted_url.hostname is None or len(splitted_url.hostname) > 253: + raise ValidationError(self.message, code=self.code, params={"value": value}) + + +integer_validator = RegexValidator( + _lazy_re_compile(r"^-?\d+\Z"), + message=_("Enter a valid integer."), + code="invalid", +) + + +def validate_integer(value): + return integer_validator(value) + + +@deconstructible +class EmailValidator: + message = _("Enter a valid email address.") + code = "invalid" + user_regex = _lazy_re_compile( + # dot-atom + r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z" + # quoted-string + r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])' + r'*"\Z)', + re.IGNORECASE, + ) + domain_regex = _lazy_re_compile( + # max length for domain name labels is 63 characters per RFC 1034 + r"((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+)(?:[A-Z0-9-]{2,63}(?<!-))\Z", + re.IGNORECASE, + ) + literal_regex = _lazy_re_compile( + # literal form, ipv4 or ipv6 address (SMTP 4.1.3) + r"\[([A-F0-9:.]+)\]\Z", + re.IGNORECASE, + ) + domain_allowlist = ["localhost"] + + def __init__(self, message=None, code=None, allowlist=None): + if message is not None: + self.message = message + if code is not None: + self.code = code + if allowlist is not None: + self.domain_allowlist = allowlist + + def __call__(self, value): + # The maximum length of an email is 320 characters per RFC 3696 + # section 3. + if not value or "@" not in value or len(value) > 320: + raise ValidationError(self.message, code=self.code, params={"value": value}) + + user_part, domain_part = value.rsplit("@", 1) + + if not self.user_regex.match(user_part): + raise ValidationError(self.message, code=self.code, params={"value": value}) + + if domain_part not in self.domain_allowlist and not self.validate_domain_part( + domain_part + ): + # Try for possible IDN domain-part + try: + domain_part = punycode(domain_part) + except UnicodeError: + pass + else: + if self.validate_domain_part(domain_part): + return + raise ValidationError(self.message, code=self.code, params={"value": value}) + + def validate_domain_part(self, domain_part): + if self.domain_regex.match(domain_part): + return True + + literal_match = self.literal_regex.match(domain_part) + if literal_match: + ip_address = literal_match[1] + try: + validate_ipv46_address(ip_address) + return True + except ValidationError: + pass + return False + + def __eq__(self, other): + return ( + isinstance(other, EmailValidator) + and (self.domain_allowlist == other.domain_allowlist) + and (self.message == other.message) + and (self.code == other.code) + ) + + +validate_email = EmailValidator() + +slug_re = _lazy_re_compile(r"^[-a-zA-Z0-9_]+\Z") +validate_slug = RegexValidator( + slug_re, + # Translators: "letters" means latin letters: a-z and A-Z. + _("Enter a valid “slug” consisting of letters, numbers, underscores or hyphens."), + "invalid", +) + +slug_unicode_re = _lazy_re_compile(r"^[-\w]+\Z") +validate_unicode_slug = RegexValidator( + slug_unicode_re, + _( + "Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " + "hyphens." + ), + "invalid", +) + + +def validate_ipv4_address(value): + try: + ipaddress.IPv4Address(value) + except ValueError: + raise ValidationError( + _("Enter a valid IPv4 address."), code="invalid", params={"value": value} + ) + else: + # Leading zeros are forbidden to avoid ambiguity with the octal + # notation. This restriction is included in Python 3.9.5+. + # TODO: Remove when dropping support for PY39. + if any(octet != "0" and octet[0] == "0" for octet in value.split(".")): + raise ValidationError( + _("Enter a valid IPv4 address."), + code="invalid", + params={"value": value}, + ) + + +def validate_ipv6_address(value): + if not is_valid_ipv6_address(value): + raise ValidationError( + _("Enter a valid IPv6 address."), code="invalid", params={"value": value} + ) + + +def validate_ipv46_address(value): + try: + validate_ipv4_address(value) + except ValidationError: + try: + validate_ipv6_address(value) + except ValidationError: + raise ValidationError( + _("Enter a valid IPv4 or IPv6 address."), + code="invalid", + params={"value": value}, + ) + + +ip_address_validator_map = { + "both": ([validate_ipv46_address], _("Enter a valid IPv4 or IPv6 address.")), + "ipv4": ([validate_ipv4_address], _("Enter a valid IPv4 address.")), + "ipv6": ([validate_ipv6_address], _("Enter a valid IPv6 address.")), +} + + +def ip_address_validators(protocol, unpack_ipv4): + """ + Depending on the given parameters, return the appropriate validators for + the GenericIPAddressField. + """ + if protocol != "both" and unpack_ipv4: + raise ValueError( + "You can only use `unpack_ipv4` if `protocol` is set to 'both'" + ) + try: + return ip_address_validator_map[protocol.lower()] + except KeyError: + raise ValueError( + "The protocol '%s' is unknown. Supported: %s" + % (protocol, list(ip_address_validator_map)) + ) + + +def int_list_validator(sep=",", message=None, code="invalid", allow_negative=False): + regexp = _lazy_re_compile( + r"^%(neg)s\d+(?:%(sep)s%(neg)s\d+)*\Z" + % { + "neg": "(-)?" if allow_negative else "", + "sep": re.escape(sep), + } + ) + return RegexValidator(regexp, message=message, code=code) + + +validate_comma_separated_integer_list = int_list_validator( + message=_("Enter only digits separated by commas."), +) + + +@deconstructible +class BaseValidator: + message = _("Ensure this value is %(limit_value)s (it is %(show_value)s).") + code = "limit_value" + + def __init__(self, limit_value, message=None): + self.limit_value = limit_value + if message: + self.message = message + + def __call__(self, value): + cleaned = self.clean(value) + limit_value = ( + self.limit_value() if callable(self.limit_value) else self.limit_value + ) + params = {"limit_value": limit_value, "show_value": cleaned, "value": value} + if self.compare(cleaned, limit_value): + raise ValidationError(self.message, code=self.code, params=params) + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return ( + self.limit_value == other.limit_value + and self.message == other.message + and self.code == other.code + ) + + def compare(self, a, b): + return a is not b + + def clean(self, x): + return x + + +@deconstructible +class MaxValueValidator(BaseValidator): + message = _("Ensure this value is less than or equal to %(limit_value)s.") + code = "max_value" + + def compare(self, a, b): + return a > b + + +@deconstructible +class MinValueValidator(BaseValidator): + message = _("Ensure this value is greater than or equal to %(limit_value)s.") + code = "min_value" + + def compare(self, a, b): + return a < b + + +@deconstructible +class StepValueValidator(BaseValidator): + message = _("Ensure this value is a multiple of step size %(limit_value)s.") + code = "step_size" + + def compare(self, a, b): + return not math.isclose(math.remainder(a, b), 0, abs_tol=1e-9) + + +@deconstructible +class MinLengthValidator(BaseValidator): + message = ngettext_lazy( + "Ensure this value has at least %(limit_value)d character (it has " + "%(show_value)d).", + "Ensure this value has at least %(limit_value)d characters (it has " + "%(show_value)d).", + "limit_value", + ) + code = "min_length" + + def compare(self, a, b): + return a < b + + def clean(self, x): + return len(x) + + +@deconstructible +class MaxLengthValidator(BaseValidator): + message = ngettext_lazy( + "Ensure this value has at most %(limit_value)d character (it has " + "%(show_value)d).", + "Ensure this value has at most %(limit_value)d characters (it has " + "%(show_value)d).", + "limit_value", + ) + code = "max_length" + + def compare(self, a, b): + return a > b + + def clean(self, x): + return len(x) + + +@deconstructible +class DecimalValidator: + """ + Validate that the input does not exceed the maximum number of digits + expected, otherwise raise ValidationError. + """ + + messages = { + "invalid": _("Enter a number."), + "max_digits": ngettext_lazy( + "Ensure that there are no more than %(max)s digit in total.", + "Ensure that there are no more than %(max)s digits in total.", + "max", + ), + "max_decimal_places": ngettext_lazy( + "Ensure that there are no more than %(max)s decimal place.", + "Ensure that there are no more than %(max)s decimal places.", + "max", + ), + "max_whole_digits": ngettext_lazy( + "Ensure that there are no more than %(max)s digit before the decimal " + "point.", + "Ensure that there are no more than %(max)s digits before the decimal " + "point.", + "max", + ), + } + + def __init__(self, max_digits, decimal_places): + self.max_digits = max_digits + self.decimal_places = decimal_places + + def __call__(self, value): + digit_tuple, exponent = value.as_tuple()[1:] + if exponent in {"F", "n", "N"}: + raise ValidationError( + self.messages["invalid"], code="invalid", params={"value": value} + ) + if exponent >= 0: + digits = len(digit_tuple) + if digit_tuple != (0,): + # A positive exponent adds that many trailing zeros. + digits += exponent + decimals = 0 + else: + # If the absolute value of the negative exponent is larger than the + # number of digits, then it's the same as the number of digits, + # because it'll consume all of the digits in digit_tuple and then + # add abs(exponent) - len(digit_tuple) leading zeros after the + # decimal point. + if abs(exponent) > len(digit_tuple): + digits = decimals = abs(exponent) + else: + digits = len(digit_tuple) + decimals = abs(exponent) + whole_digits = digits - decimals + + if self.max_digits is not None and digits > self.max_digits: + raise ValidationError( + self.messages["max_digits"], + code="max_digits", + params={"max": self.max_digits, "value": value}, + ) + if self.decimal_places is not None and decimals > self.decimal_places: + raise ValidationError( + self.messages["max_decimal_places"], + code="max_decimal_places", + params={"max": self.decimal_places, "value": value}, + ) + if ( + self.max_digits is not None + and self.decimal_places is not None + and whole_digits > (self.max_digits - self.decimal_places) + ): + raise ValidationError( + self.messages["max_whole_digits"], + code="max_whole_digits", + params={"max": (self.max_digits - self.decimal_places), "value": value}, + ) + + def __eq__(self, other): + return ( + isinstance(other, self.__class__) + and self.max_digits == other.max_digits + and self.decimal_places == other.decimal_places + ) + + +@deconstructible +class FileExtensionValidator: + message = _( + "File extension “%(extension)s” is not allowed. " + "Allowed extensions are: %(allowed_extensions)s." + ) + code = "invalid_extension" + + def __init__(self, allowed_extensions=None, message=None, code=None): + if allowed_extensions is not None: + allowed_extensions = [ + allowed_extension.lower() for allowed_extension in allowed_extensions + ] + self.allowed_extensions = allowed_extensions + if message is not None: + self.message = message + if code is not None: + self.code = code + + def __call__(self, value): + extension = Path(value.name).suffix[1:].lower() + if ( + self.allowed_extensions is not None + and extension not in self.allowed_extensions + ): + raise ValidationError( + self.message, + code=self.code, + params={ + "extension": extension, + "allowed_extensions": ", ".join(self.allowed_extensions), + "value": value, + }, + ) + + def __eq__(self, other): + return ( + isinstance(other, self.__class__) + and self.allowed_extensions == other.allowed_extensions + and self.message == other.message + and self.code == other.code + ) + + +def get_available_image_extensions(): + try: + from PIL import Image + except ImportError: + return [] + else: + Image.init() + return [ext.lower()[1:] for ext in Image.EXTENSION] + + +def validate_image_file_extension(value): + return FileExtensionValidator(allowed_extensions=get_available_image_extensions())( + value + ) + + +@deconstructible +class ProhibitNullCharactersValidator: + """Validate that the string doesn't contain the null character.""" + + message = _("Null characters are not allowed.") + code = "null_characters_not_allowed" + + def __init__(self, message=None, code=None): + if message is not None: + self.message = message + if code is not None: + self.code = code + + def __call__(self, value): + if "\x00" in str(value): + raise ValidationError(self.message, code=self.code, params={"value": value}) + + def __eq__(self, other): + return ( + isinstance(other, self.__class__) + and self.message == other.message + and self.code == other.code + ) diff --git a/virt/lib/python3.9/site-packages/django/db/backends/base/schema 3.py b/virt/lib/python3.9/site-packages/django/db/backends/base/schema 3.py new file mode 100644 index 00000000..3acd16eb --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/db/backends/base/schema 3.py @@ -0,0 +1,1829 @@ +import logging +import operator +from datetime import datetime + +from django.conf import settings +from django.db.backends.ddl_references import ( + Columns, + Expressions, + ForeignKeyName, + IndexName, + Statement, + Table, +) +from django.db.backends.utils import names_digest, split_identifier, truncate_name +from django.db.models import Deferrable, Index +from django.db.models.sql import Query +from django.db.transaction import TransactionManagementError, atomic +from django.utils import timezone + +logger = logging.getLogger("django.db.backends.schema") + + +def _is_relevant_relation(relation, altered_field): + """ + When altering the given field, must constraints on its model from the given + relation be temporarily dropped? + """ + field = relation.field + if field.many_to_many: + # M2M reverse field + return False + if altered_field.primary_key and field.to_fields == [None]: + # Foreign key constraint on the primary key, which is being altered. + return True + # Is the constraint targeting the field being altered? + return altered_field.name in field.to_fields + + +def _all_related_fields(model): + # Related fields must be returned in a deterministic order. + return sorted( + model._meta._get_fields( + forward=False, + reverse=True, + include_hidden=True, + include_parents=False, + ), + key=operator.attrgetter("name"), + ) + + +def _related_non_m2m_objects(old_field, new_field): + # Filter out m2m objects from reverse relations. + # Return (old_relation, new_relation) tuples. + related_fields = zip( + ( + obj + for obj in _all_related_fields(old_field.model) + if _is_relevant_relation(obj, old_field) + ), + ( + obj + for obj in _all_related_fields(new_field.model) + if _is_relevant_relation(obj, new_field) + ), + ) + for old_rel, new_rel in related_fields: + yield old_rel, new_rel + yield from _related_non_m2m_objects( + old_rel.remote_field, + new_rel.remote_field, + ) + + +class BaseDatabaseSchemaEditor: + """ + This class and its subclasses are responsible for emitting schema-changing + statements to the databases - model creation/removal/alteration, field + renaming, index fiddling, and so on. + """ + + # Overrideable SQL templates + sql_create_table = "CREATE TABLE %(table)s (%(definition)s)" + sql_rename_table = "ALTER TABLE %(old_table)s RENAME TO %(new_table)s" + sql_retablespace_table = "ALTER TABLE %(table)s SET TABLESPACE %(new_tablespace)s" + sql_delete_table = "DROP TABLE %(table)s CASCADE" + + sql_create_column = "ALTER TABLE %(table)s ADD COLUMN %(column)s %(definition)s" + sql_alter_column = "ALTER TABLE %(table)s %(changes)s" + sql_alter_column_type = "ALTER COLUMN %(column)s TYPE %(type)s%(collation)s" + sql_alter_column_null = "ALTER COLUMN %(column)s DROP NOT NULL" + sql_alter_column_not_null = "ALTER COLUMN %(column)s SET NOT NULL" + sql_alter_column_default = "ALTER COLUMN %(column)s SET DEFAULT %(default)s" + sql_alter_column_no_default = "ALTER COLUMN %(column)s DROP DEFAULT" + sql_alter_column_no_default_null = sql_alter_column_no_default + sql_delete_column = "ALTER TABLE %(table)s DROP COLUMN %(column)s CASCADE" + sql_rename_column = ( + "ALTER TABLE %(table)s RENAME COLUMN %(old_column)s TO %(new_column)s" + ) + sql_update_with_default = ( + "UPDATE %(table)s SET %(column)s = %(default)s WHERE %(column)s IS NULL" + ) + + sql_unique_constraint = "UNIQUE (%(columns)s)%(deferrable)s" + sql_check_constraint = "CHECK (%(check)s)" + sql_delete_constraint = "ALTER TABLE %(table)s DROP CONSTRAINT %(name)s" + sql_constraint = "CONSTRAINT %(name)s %(constraint)s" + + sql_create_check = "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s CHECK (%(check)s)" + sql_delete_check = sql_delete_constraint + + sql_create_unique = ( + "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s " + "UNIQUE (%(columns)s)%(deferrable)s" + ) + sql_delete_unique = sql_delete_constraint + + sql_create_fk = ( + "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s FOREIGN KEY (%(column)s) " + "REFERENCES %(to_table)s (%(to_column)s)%(deferrable)s" + ) + sql_create_inline_fk = None + sql_create_column_inline_fk = None + sql_delete_fk = sql_delete_constraint + + sql_create_index = ( + "CREATE INDEX %(name)s ON %(table)s " + "(%(columns)s)%(include)s%(extra)s%(condition)s" + ) + sql_create_unique_index = ( + "CREATE UNIQUE INDEX %(name)s ON %(table)s " + "(%(columns)s)%(include)s%(condition)s" + ) + sql_rename_index = "ALTER INDEX %(old_name)s RENAME TO %(new_name)s" + sql_delete_index = "DROP INDEX %(name)s" + + sql_create_pk = ( + "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s PRIMARY KEY (%(columns)s)" + ) + sql_delete_pk = sql_delete_constraint + + sql_delete_procedure = "DROP PROCEDURE %(procedure)s" + + sql_alter_table_comment = "COMMENT ON TABLE %(table)s IS %(comment)s" + sql_alter_column_comment = "COMMENT ON COLUMN %(table)s.%(column)s IS %(comment)s" + + def __init__(self, connection, collect_sql=False, atomic=True): + self.connection = connection + self.collect_sql = collect_sql + if self.collect_sql: + self.collected_sql = [] + self.atomic_migration = self.connection.features.can_rollback_ddl and atomic + + # State-managing methods + + def __enter__(self): + self.deferred_sql = [] + if self.atomic_migration: + self.atomic = atomic(self.connection.alias) + self.atomic.__enter__() + return self + + def __exit__(self, exc_type, exc_value, traceback): + if exc_type is None: + for sql in self.deferred_sql: + self.execute(sql) + if self.atomic_migration: + self.atomic.__exit__(exc_type, exc_value, traceback) + + # Core utility functions + + def execute(self, sql, params=()): + """Execute the given SQL statement, with optional parameters.""" + # Don't perform the transactional DDL check if SQL is being collected + # as it's not going to be executed anyway. + if ( + not self.collect_sql + and self.connection.in_atomic_block + and not self.connection.features.can_rollback_ddl + ): + raise TransactionManagementError( + "Executing DDL statements while in a transaction on databases " + "that can't perform a rollback is prohibited." + ) + # Account for non-string statement objects. + sql = str(sql) + # Log the command we're running, then run it + logger.debug( + "%s; (params %r)", sql, params, extra={"params": params, "sql": sql} + ) + if self.collect_sql: + ending = "" if sql.rstrip().endswith(";") else ";" + if params is not None: + self.collected_sql.append( + (sql % tuple(map(self.quote_value, params))) + ending + ) + else: + self.collected_sql.append(sql + ending) + else: + with self.connection.cursor() as cursor: + cursor.execute(sql, params) + + def quote_name(self, name): + return self.connection.ops.quote_name(name) + + def table_sql(self, model): + """Take a model and return its table definition.""" + # Add any unique_togethers (always deferred, as some fields might be + # created afterward, like geometry fields with some backends). + for field_names in model._meta.unique_together: + fields = [model._meta.get_field(field) for field in field_names] + self.deferred_sql.append(self._create_unique_sql(model, fields)) + # Create column SQL, add FK deferreds if needed. + column_sqls = [] + params = [] + for field in model._meta.local_fields: + # SQL. + definition, extra_params = self.column_sql(model, field) + if definition is None: + continue + # Check constraints can go on the column SQL here. + db_params = field.db_parameters(connection=self.connection) + if db_params["check"]: + definition += " " + self.sql_check_constraint % db_params + # Autoincrement SQL (for backends with inline variant). + col_type_suffix = field.db_type_suffix(connection=self.connection) + if col_type_suffix: + definition += " %s" % col_type_suffix + params.extend(extra_params) + # FK. + if field.remote_field and field.db_constraint: + to_table = field.remote_field.model._meta.db_table + to_column = field.remote_field.model._meta.get_field( + field.remote_field.field_name + ).column + if self.sql_create_inline_fk: + definition += " " + self.sql_create_inline_fk % { + "to_table": self.quote_name(to_table), + "to_column": self.quote_name(to_column), + } + elif self.connection.features.supports_foreign_keys: + self.deferred_sql.append( + self._create_fk_sql( + model, field, "_fk_%(to_table)s_%(to_column)s" + ) + ) + # Add the SQL to our big list. + column_sqls.append( + "%s %s" + % ( + self.quote_name(field.column), + definition, + ) + ) + # Autoincrement SQL (for backends with post table definition + # variant). + if field.get_internal_type() in ( + "AutoField", + "BigAutoField", + "SmallAutoField", + ): + autoinc_sql = self.connection.ops.autoinc_sql( + model._meta.db_table, field.column + ) + if autoinc_sql: + self.deferred_sql.extend(autoinc_sql) + constraints = [ + constraint.constraint_sql(model, self) + for constraint in model._meta.constraints + ] + sql = self.sql_create_table % { + "table": self.quote_name(model._meta.db_table), + "definition": ", ".join( + str(constraint) + for constraint in (*column_sqls, *constraints) + if constraint + ), + } + if model._meta.db_tablespace: + tablespace_sql = self.connection.ops.tablespace_sql( + model._meta.db_tablespace + ) + if tablespace_sql: + sql += " " + tablespace_sql + return sql, params + + # Field <-> database mapping functions + + def _iter_column_sql( + self, column_db_type, params, model, field, field_db_params, include_default + ): + yield column_db_type + if collation := field_db_params.get("collation"): + yield self._collate_sql(collation) + if self.connection.features.supports_comments_inline and field.db_comment: + yield self._comment_sql(field.db_comment) + # Work out nullability. + null = field.null + # Include a default value, if requested. + include_default = ( + include_default + and not self.skip_default(field) + and + # Don't include a default value if it's a nullable field and the + # default cannot be dropped in the ALTER COLUMN statement (e.g. + # MySQL longtext and longblob). + not (null and self.skip_default_on_alter(field)) + ) + if include_default: + default_value = self.effective_default(field) + if default_value is not None: + column_default = "DEFAULT " + self._column_default_sql(field) + if self.connection.features.requires_literal_defaults: + # Some databases can't take defaults as a parameter (Oracle). + # If this is the case, the individual schema backend should + # implement prepare_default(). + yield column_default % self.prepare_default(default_value) + else: + yield column_default + params.append(default_value) + # Oracle treats the empty string ('') as null, so coerce the null + # option whenever '' is a possible value. + if ( + field.empty_strings_allowed + and not field.primary_key + and self.connection.features.interprets_empty_strings_as_nulls + ): + null = True + if not null: + yield "NOT NULL" + elif not self.connection.features.implied_column_null: + yield "NULL" + if field.primary_key: + yield "PRIMARY KEY" + elif field.unique: + yield "UNIQUE" + # Optionally add the tablespace if it's an implicitly indexed column. + tablespace = field.db_tablespace or model._meta.db_tablespace + if ( + tablespace + and self.connection.features.supports_tablespaces + and field.unique + ): + yield self.connection.ops.tablespace_sql(tablespace, inline=True) + + def column_sql(self, model, field, include_default=False): + """ + Return the column definition for a field. The field must already have + had set_attributes_from_name() called. + """ + # Get the column's type and use that as the basis of the SQL. + field_db_params = field.db_parameters(connection=self.connection) + column_db_type = field_db_params["type"] + # Check for fields that aren't actually columns (e.g. M2M). + if column_db_type is None: + return None, None + params = [] + return ( + " ".join( + # This appends to the params being returned. + self._iter_column_sql( + column_db_type, + params, + model, + field, + field_db_params, + include_default, + ) + ), + params, + ) + + def skip_default(self, field): + """ + Some backends don't accept default values for certain columns types + (i.e. MySQL longtext and longblob). + """ + return False + + def skip_default_on_alter(self, field): + """ + Some backends don't accept default values for certain columns types + (i.e. MySQL longtext and longblob) in the ALTER COLUMN statement. + """ + return False + + def prepare_default(self, value): + """ + Only used for backends which have requires_literal_defaults feature + """ + raise NotImplementedError( + "subclasses of BaseDatabaseSchemaEditor for backends which have " + "requires_literal_defaults must provide a prepare_default() method" + ) + + def _column_default_sql(self, field): + """ + Return the SQL to use in a DEFAULT clause. The resulting string should + contain a '%s' placeholder for a default value. + """ + return "%s" + + @staticmethod + def _effective_default(field): + # This method allows testing its logic without a connection. + if field.has_default(): + default = field.get_default() + elif not field.null and field.blank and field.empty_strings_allowed: + if field.get_internal_type() == "BinaryField": + default = b"" + else: + default = "" + elif getattr(field, "auto_now", False) or getattr(field, "auto_now_add", False): + internal_type = field.get_internal_type() + if internal_type == "DateTimeField": + default = timezone.now() + else: + default = datetime.now() + if internal_type == "DateField": + default = default.date() + elif internal_type == "TimeField": + default = default.time() + else: + default = None + return default + + def effective_default(self, field): + """Return a field's effective database default value.""" + return field.get_db_prep_save(self._effective_default(field), self.connection) + + def quote_value(self, value): + """ + Return a quoted version of the value so it's safe to use in an SQL + string. This is not safe against injection from user code; it is + intended only for use in making SQL scripts or preparing default values + for particularly tricky backends (defaults are not user-defined, though, + so this is safe). + """ + raise NotImplementedError() + + # Actions + + def create_model(self, model): + """ + Create a table and any accompanying indexes or unique constraints for + the given `model`. + """ + sql, params = self.table_sql(model) + # Prevent using [] as params, in the case a literal '%' is used in the + # definition. + self.execute(sql, params or None) + + if self.connection.features.supports_comments: + # Add table comment. + if model._meta.db_table_comment: + self.alter_db_table_comment(model, None, model._meta.db_table_comment) + # Add column comments. + if not self.connection.features.supports_comments_inline: + for field in model._meta.local_fields: + if field.db_comment: + field_db_params = field.db_parameters( + connection=self.connection + ) + field_type = field_db_params["type"] + self.execute( + *self._alter_column_comment_sql( + model, field, field_type, field.db_comment + ) + ) + # Add any field index and index_together's (deferred as SQLite + # _remake_table needs it). + self.deferred_sql.extend(self._model_indexes_sql(model)) + + # Make M2M tables + for field in model._meta.local_many_to_many: + if field.remote_field.through._meta.auto_created: + self.create_model(field.remote_field.through) + + def delete_model(self, model): + """Delete a model from the database.""" + # Handle auto-created intermediary models + for field in model._meta.local_many_to_many: + if field.remote_field.through._meta.auto_created: + self.delete_model(field.remote_field.through) + + # Delete the table + self.execute( + self.sql_delete_table + % { + "table": self.quote_name(model._meta.db_table), + } + ) + # Remove all deferred statements referencing the deleted table. + for sql in list(self.deferred_sql): + if isinstance(sql, Statement) and sql.references_table( + model._meta.db_table + ): + self.deferred_sql.remove(sql) + + def add_index(self, model, index): + """Add an index on a model.""" + if ( + index.contains_expressions + and not self.connection.features.supports_expression_indexes + ): + return None + # Index.create_sql returns interpolated SQL which makes params=None a + # necessity to avoid escaping attempts on execution. + self.execute(index.create_sql(model, self), params=None) + + def remove_index(self, model, index): + """Remove an index from a model.""" + if ( + index.contains_expressions + and not self.connection.features.supports_expression_indexes + ): + return None + self.execute(index.remove_sql(model, self)) + + def rename_index(self, model, old_index, new_index): + if self.connection.features.can_rename_index: + self.execute( + self._rename_index_sql(model, old_index.name, new_index.name), + params=None, + ) + else: + self.remove_index(model, old_index) + self.add_index(model, new_index) + + def add_constraint(self, model, constraint): + """Add a constraint to a model.""" + sql = constraint.create_sql(model, self) + if sql: + # Constraint.create_sql returns interpolated SQL which makes + # params=None a necessity to avoid escaping attempts on execution. + self.execute(sql, params=None) + + def remove_constraint(self, model, constraint): + """Remove a constraint from a model.""" + sql = constraint.remove_sql(model, self) + if sql: + self.execute(sql) + + def alter_unique_together(self, model, old_unique_together, new_unique_together): + """ + Deal with a model changing its unique_together. The input + unique_togethers must be doubly-nested, not the single-nested + ["foo", "bar"] format. + """ + olds = {tuple(fields) for fields in old_unique_together} + news = {tuple(fields) for fields in new_unique_together} + # Deleted uniques + for fields in olds.difference(news): + self._delete_composed_index( + model, + fields, + {"unique": True, "primary_key": False}, + self.sql_delete_unique, + ) + # Created uniques + for field_names in news.difference(olds): + fields = [model._meta.get_field(field) for field in field_names] + self.execute(self._create_unique_sql(model, fields)) + + def alter_index_together(self, model, old_index_together, new_index_together): + """ + Deal with a model changing its index_together. The input + index_togethers must be doubly-nested, not the single-nested + ["foo", "bar"] format. + """ + olds = {tuple(fields) for fields in old_index_together} + news = {tuple(fields) for fields in new_index_together} + # Deleted indexes + for fields in olds.difference(news): + self._delete_composed_index( + model, + fields, + {"index": True, "unique": False}, + self.sql_delete_index, + ) + # Created indexes + for field_names in news.difference(olds): + fields = [model._meta.get_field(field) for field in field_names] + self.execute(self._create_index_sql(model, fields=fields, suffix="_idx")) + + def _delete_composed_index(self, model, fields, constraint_kwargs, sql): + meta_constraint_names = { + constraint.name for constraint in model._meta.constraints + } + meta_index_names = {constraint.name for constraint in model._meta.indexes} + columns = [model._meta.get_field(field).column for field in fields] + constraint_names = self._constraint_names( + model, + columns, + exclude=meta_constraint_names | meta_index_names, + **constraint_kwargs, + ) + if ( + constraint_kwargs.get("unique") is True + and constraint_names + and self.connection.features.allows_multiple_constraints_on_same_fields + ): + # Constraint matching the unique_together name. + default_name = str( + self._unique_constraint_name(model._meta.db_table, columns, quote=False) + ) + if default_name in constraint_names: + constraint_names = [default_name] + if len(constraint_names) != 1: + raise ValueError( + "Found wrong number (%s) of constraints for %s(%s)" + % ( + len(constraint_names), + model._meta.db_table, + ", ".join(columns), + ) + ) + self.execute(self._delete_constraint_sql(sql, model, constraint_names[0])) + + def alter_db_table(self, model, old_db_table, new_db_table): + """Rename the table a model points to.""" + if old_db_table == new_db_table or ( + self.connection.features.ignores_table_name_case + and old_db_table.lower() == new_db_table.lower() + ): + return + self.execute( + self.sql_rename_table + % { + "old_table": self.quote_name(old_db_table), + "new_table": self.quote_name(new_db_table), + } + ) + # Rename all references to the old table name. + for sql in self.deferred_sql: + if isinstance(sql, Statement): + sql.rename_table_references(old_db_table, new_db_table) + + def alter_db_table_comment(self, model, old_db_table_comment, new_db_table_comment): + self.execute( + self.sql_alter_table_comment + % { + "table": self.quote_name(model._meta.db_table), + "comment": self.quote_value(new_db_table_comment or ""), + } + ) + + def alter_db_tablespace(self, model, old_db_tablespace, new_db_tablespace): + """Move a model's table between tablespaces.""" + self.execute( + self.sql_retablespace_table + % { + "table": self.quote_name(model._meta.db_table), + "old_tablespace": self.quote_name(old_db_tablespace), + "new_tablespace": self.quote_name(new_db_tablespace), + } + ) + + def add_field(self, model, field): + """ + Create a field on a model. Usually involves adding a column, but may + involve adding a table instead (for M2M fields). + """ + # Special-case implicit M2M tables + if field.many_to_many and field.remote_field.through._meta.auto_created: + return self.create_model(field.remote_field.through) + # Get the column's definition + definition, params = self.column_sql(model, field, include_default=True) + # It might not actually have a column behind it + if definition is None: + return + if col_type_suffix := field.db_type_suffix(connection=self.connection): + definition += f" {col_type_suffix}" + # Check constraints can go on the column SQL here + db_params = field.db_parameters(connection=self.connection) + if db_params["check"]: + definition += " " + self.sql_check_constraint % db_params + if ( + field.remote_field + and self.connection.features.supports_foreign_keys + and field.db_constraint + ): + constraint_suffix = "_fk_%(to_table)s_%(to_column)s" + # Add FK constraint inline, if supported. + if self.sql_create_column_inline_fk: + to_table = field.remote_field.model._meta.db_table + to_column = field.remote_field.model._meta.get_field( + field.remote_field.field_name + ).column + namespace, _ = split_identifier(model._meta.db_table) + definition += " " + self.sql_create_column_inline_fk % { + "name": self._fk_constraint_name(model, field, constraint_suffix), + "namespace": "%s." % self.quote_name(namespace) + if namespace + else "", + "column": self.quote_name(field.column), + "to_table": self.quote_name(to_table), + "to_column": self.quote_name(to_column), + "deferrable": self.connection.ops.deferrable_sql(), + } + # Otherwise, add FK constraints later. + else: + self.deferred_sql.append( + self._create_fk_sql(model, field, constraint_suffix) + ) + # Build the SQL and run it + sql = self.sql_create_column % { + "table": self.quote_name(model._meta.db_table), + "column": self.quote_name(field.column), + "definition": definition, + } + self.execute(sql, params) + # Drop the default if we need to + # (Django usually does not use in-database defaults) + if ( + not self.skip_default_on_alter(field) + and self.effective_default(field) is not None + ): + changes_sql, params = self._alter_column_default_sql( + model, None, field, drop=True + ) + sql = self.sql_alter_column % { + "table": self.quote_name(model._meta.db_table), + "changes": changes_sql, + } + self.execute(sql, params) + # Add field comment, if required. + if ( + field.db_comment + and self.connection.features.supports_comments + and not self.connection.features.supports_comments_inline + ): + field_type = db_params["type"] + self.execute( + *self._alter_column_comment_sql( + model, field, field_type, field.db_comment + ) + ) + # Add an index, if required + self.deferred_sql.extend(self._field_indexes_sql(model, field)) + # Reset connection if required + if self.connection.features.connection_persists_old_columns: + self.connection.close() + + def remove_field(self, model, field): + """ + Remove a field from a model. Usually involves deleting a column, + but for M2Ms may involve deleting a table. + """ + # Special-case implicit M2M tables + if field.many_to_many and field.remote_field.through._meta.auto_created: + return self.delete_model(field.remote_field.through) + # It might not actually have a column behind it + if field.db_parameters(connection=self.connection)["type"] is None: + return + # Drop any FK constraints, MySQL requires explicit deletion + if field.remote_field: + fk_names = self._constraint_names(model, [field.column], foreign_key=True) + for fk_name in fk_names: + self.execute(self._delete_fk_sql(model, fk_name)) + # Delete the column + sql = self.sql_delete_column % { + "table": self.quote_name(model._meta.db_table), + "column": self.quote_name(field.column), + } + self.execute(sql) + # Reset connection if required + if self.connection.features.connection_persists_old_columns: + self.connection.close() + # Remove all deferred statements referencing the deleted column. + for sql in list(self.deferred_sql): + if isinstance(sql, Statement) and sql.references_column( + model._meta.db_table, field.column + ): + self.deferred_sql.remove(sql) + + def alter_field(self, model, old_field, new_field, strict=False): + """ + Allow a field's type, uniqueness, nullability, default, column, + constraints, etc. to be modified. + `old_field` is required to compute the necessary changes. + If `strict` is True, raise errors if the old column does not match + `old_field` precisely. + """ + if not self._field_should_be_altered(old_field, new_field): + return + # Ensure this field is even column-based + old_db_params = old_field.db_parameters(connection=self.connection) + old_type = old_db_params["type"] + new_db_params = new_field.db_parameters(connection=self.connection) + new_type = new_db_params["type"] + if (old_type is None and old_field.remote_field is None) or ( + new_type is None and new_field.remote_field is None + ): + raise ValueError( + "Cannot alter field %s into %s - they do not properly define " + "db_type (are you using a badly-written custom field?)" + % (old_field, new_field), + ) + elif ( + old_type is None + and new_type is None + and ( + old_field.remote_field.through + and new_field.remote_field.through + and old_field.remote_field.through._meta.auto_created + and new_field.remote_field.through._meta.auto_created + ) + ): + return self._alter_many_to_many(model, old_field, new_field, strict) + elif ( + old_type is None + and new_type is None + and ( + old_field.remote_field.through + and new_field.remote_field.through + and not old_field.remote_field.through._meta.auto_created + and not new_field.remote_field.through._meta.auto_created + ) + ): + # Both sides have through models; this is a no-op. + return + elif old_type is None or new_type is None: + raise ValueError( + "Cannot alter field %s into %s - they are not compatible types " + "(you cannot alter to or from M2M fields, or add or remove " + "through= on M2M fields)" % (old_field, new_field) + ) + + self._alter_field( + model, + old_field, + new_field, + old_type, + new_type, + old_db_params, + new_db_params, + strict, + ) + + def _alter_field( + self, + model, + old_field, + new_field, + old_type, + new_type, + old_db_params, + new_db_params, + strict=False, + ): + """Perform a "physical" (non-ManyToMany) field update.""" + # Drop any FK constraints, we'll remake them later + fks_dropped = set() + if ( + self.connection.features.supports_foreign_keys + and old_field.remote_field + and old_field.db_constraint + and self._field_should_be_altered( + old_field, + new_field, + ignore={"db_comment"}, + ) + ): + fk_names = self._constraint_names( + model, [old_field.column], foreign_key=True + ) + if strict and len(fk_names) != 1: + raise ValueError( + "Found wrong number (%s) of foreign key constraints for %s.%s" + % ( + len(fk_names), + model._meta.db_table, + old_field.column, + ) + ) + for fk_name in fk_names: + fks_dropped.add((old_field.column,)) + self.execute(self._delete_fk_sql(model, fk_name)) + # Has unique been removed? + if old_field.unique and ( + not new_field.unique or self._field_became_primary_key(old_field, new_field) + ): + # Find the unique constraint for this field + meta_constraint_names = { + constraint.name for constraint in model._meta.constraints + } + constraint_names = self._constraint_names( + model, + [old_field.column], + unique=True, + primary_key=False, + exclude=meta_constraint_names, + ) + if strict and len(constraint_names) != 1: + raise ValueError( + "Found wrong number (%s) of unique constraints for %s.%s" + % ( + len(constraint_names), + model._meta.db_table, + old_field.column, + ) + ) + for constraint_name in constraint_names: + self.execute(self._delete_unique_sql(model, constraint_name)) + # Drop incoming FK constraints if the field is a primary key or unique, + # which might be a to_field target, and things are going to change. + old_collation = old_db_params.get("collation") + new_collation = new_db_params.get("collation") + drop_foreign_keys = ( + self.connection.features.supports_foreign_keys + and ( + (old_field.primary_key and new_field.primary_key) + or (old_field.unique and new_field.unique) + ) + and ((old_type != new_type) or (old_collation != new_collation)) + ) + if drop_foreign_keys: + # '_meta.related_field' also contains M2M reverse fields, these + # will be filtered out + for _old_rel, new_rel in _related_non_m2m_objects(old_field, new_field): + rel_fk_names = self._constraint_names( + new_rel.related_model, [new_rel.field.column], foreign_key=True + ) + for fk_name in rel_fk_names: + self.execute(self._delete_fk_sql(new_rel.related_model, fk_name)) + # Removed an index? (no strict check, as multiple indexes are possible) + # Remove indexes if db_index switched to False or a unique constraint + # will now be used in lieu of an index. The following lines from the + # truth table show all True cases; the rest are False: + # + # old_field.db_index | old_field.unique | new_field.db_index | new_field.unique + # ------------------------------------------------------------------------------ + # True | False | False | False + # True | False | False | True + # True | False | True | True + if ( + old_field.db_index + and not old_field.unique + and (not new_field.db_index or new_field.unique) + ): + # Find the index for this field + meta_index_names = {index.name for index in model._meta.indexes} + # Retrieve only BTREE indexes since this is what's created with + # db_index=True. + index_names = self._constraint_names( + model, + [old_field.column], + index=True, + type_=Index.suffix, + exclude=meta_index_names, + ) + for index_name in index_names: + # The only way to check if an index was created with + # db_index=True or with Index(['field'], name='foo') + # is to look at its name (refs #28053). + self.execute(self._delete_index_sql(model, index_name)) + # Change check constraints? + if old_db_params["check"] != new_db_params["check"] and old_db_params["check"]: + meta_constraint_names = { + constraint.name for constraint in model._meta.constraints + } + constraint_names = self._constraint_names( + model, + [old_field.column], + check=True, + exclude=meta_constraint_names, + ) + if strict and len(constraint_names) != 1: + raise ValueError( + "Found wrong number (%s) of check constraints for %s.%s" + % ( + len(constraint_names), + model._meta.db_table, + old_field.column, + ) + ) + for constraint_name in constraint_names: + self.execute(self._delete_check_sql(model, constraint_name)) + # Have they renamed the column? + if old_field.column != new_field.column: + self.execute( + self._rename_field_sql( + model._meta.db_table, old_field, new_field, new_type + ) + ) + # Rename all references to the renamed column. + for sql in self.deferred_sql: + if isinstance(sql, Statement): + sql.rename_column_references( + model._meta.db_table, old_field.column, new_field.column + ) + # Next, start accumulating actions to do + actions = [] + null_actions = [] + post_actions = [] + # Type suffix change? (e.g. auto increment). + old_type_suffix = old_field.db_type_suffix(connection=self.connection) + new_type_suffix = new_field.db_type_suffix(connection=self.connection) + # Type, collation, or comment change? + if ( + old_type != new_type + or old_type_suffix != new_type_suffix + or old_collation != new_collation + or ( + self.connection.features.supports_comments + and old_field.db_comment != new_field.db_comment + ) + ): + fragment, other_actions = self._alter_column_type_sql( + model, old_field, new_field, new_type, old_collation, new_collation + ) + actions.append(fragment) + post_actions.extend(other_actions) + # When changing a column NULL constraint to NOT NULL with a given + # default value, we need to perform 4 steps: + # 1. Add a default for new incoming writes + # 2. Update existing NULL rows with new default + # 3. Replace NULL constraint with NOT NULL + # 4. Drop the default again. + # Default change? + needs_database_default = False + if old_field.null and not new_field.null: + old_default = self.effective_default(old_field) + new_default = self.effective_default(new_field) + if ( + not self.skip_default_on_alter(new_field) + and old_default != new_default + and new_default is not None + ): + needs_database_default = True + actions.append( + self._alter_column_default_sql(model, old_field, new_field) + ) + # Nullability change? + if old_field.null != new_field.null: + fragment = self._alter_column_null_sql(model, old_field, new_field) + if fragment: + null_actions.append(fragment) + # Only if we have a default and there is a change from NULL to NOT NULL + four_way_default_alteration = new_field.has_default() and ( + old_field.null and not new_field.null + ) + if actions or null_actions: + if not four_way_default_alteration: + # If we don't have to do a 4-way default alteration we can + # directly run a (NOT) NULL alteration + actions += null_actions + # Combine actions together if we can (e.g. postgres) + if self.connection.features.supports_combined_alters and actions: + sql, params = tuple(zip(*actions)) + actions = [(", ".join(sql), sum(params, []))] + # Apply those actions + for sql, params in actions: + self.execute( + self.sql_alter_column + % { + "table": self.quote_name(model._meta.db_table), + "changes": sql, + }, + params, + ) + if four_way_default_alteration: + # Update existing rows with default value + self.execute( + self.sql_update_with_default + % { + "table": self.quote_name(model._meta.db_table), + "column": self.quote_name(new_field.column), + "default": "%s", + }, + [new_default], + ) + # Since we didn't run a NOT NULL change before we need to do it + # now + for sql, params in null_actions: + self.execute( + self.sql_alter_column + % { + "table": self.quote_name(model._meta.db_table), + "changes": sql, + }, + params, + ) + if post_actions: + for sql, params in post_actions: + self.execute(sql, params) + # If primary_key changed to False, delete the primary key constraint. + if old_field.primary_key and not new_field.primary_key: + self._delete_primary_key(model, strict) + # Added a unique? + if self._unique_should_be_added(old_field, new_field): + self.execute(self._create_unique_sql(model, [new_field])) + # Added an index? Add an index if db_index switched to True or a unique + # constraint will no longer be used in lieu of an index. The following + # lines from the truth table show all True cases; the rest are False: + # + # old_field.db_index | old_field.unique | new_field.db_index | new_field.unique + # ------------------------------------------------------------------------------ + # False | False | True | False + # False | True | True | False + # True | True | True | False + if ( + (not old_field.db_index or old_field.unique) + and new_field.db_index + and not new_field.unique + ): + self.execute(self._create_index_sql(model, fields=[new_field])) + # Type alteration on primary key? Then we need to alter the column + # referring to us. + rels_to_update = [] + if drop_foreign_keys: + rels_to_update.extend(_related_non_m2m_objects(old_field, new_field)) + # Changed to become primary key? + if self._field_became_primary_key(old_field, new_field): + # Make the new one + self.execute(self._create_primary_key_sql(model, new_field)) + # Update all referencing columns + rels_to_update.extend(_related_non_m2m_objects(old_field, new_field)) + # Handle our type alters on the other end of rels from the PK stuff above + for old_rel, new_rel in rels_to_update: + rel_db_params = new_rel.field.db_parameters(connection=self.connection) + rel_type = rel_db_params["type"] + rel_collation = rel_db_params.get("collation") + old_rel_db_params = old_rel.field.db_parameters(connection=self.connection) + old_rel_collation = old_rel_db_params.get("collation") + fragment, other_actions = self._alter_column_type_sql( + new_rel.related_model, + old_rel.field, + new_rel.field, + rel_type, + old_rel_collation, + rel_collation, + ) + self.execute( + self.sql_alter_column + % { + "table": self.quote_name(new_rel.related_model._meta.db_table), + "changes": fragment[0], + }, + fragment[1], + ) + for sql, params in other_actions: + self.execute(sql, params) + # Does it have a foreign key? + if ( + self.connection.features.supports_foreign_keys + and new_field.remote_field + and ( + fks_dropped or not old_field.remote_field or not old_field.db_constraint + ) + and new_field.db_constraint + ): + self.execute( + self._create_fk_sql(model, new_field, "_fk_%(to_table)s_%(to_column)s") + ) + # Rebuild FKs that pointed to us if we previously had to drop them + if drop_foreign_keys: + for _, rel in rels_to_update: + if rel.field.db_constraint: + self.execute( + self._create_fk_sql(rel.related_model, rel.field, "_fk") + ) + # Does it have check constraints we need to add? + if old_db_params["check"] != new_db_params["check"] and new_db_params["check"]: + constraint_name = self._create_index_name( + model._meta.db_table, [new_field.column], suffix="_check" + ) + self.execute( + self._create_check_sql(model, constraint_name, new_db_params["check"]) + ) + # Drop the default if we need to + # (Django usually does not use in-database defaults) + if needs_database_default: + changes_sql, params = self._alter_column_default_sql( + model, old_field, new_field, drop=True + ) + sql = self.sql_alter_column % { + "table": self.quote_name(model._meta.db_table), + "changes": changes_sql, + } + self.execute(sql, params) + # Reset connection if required + if self.connection.features.connection_persists_old_columns: + self.connection.close() + + def _alter_column_null_sql(self, model, old_field, new_field): + """ + Hook to specialize column null alteration. + + Return a (sql, params) fragment to set a column to null or non-null + as required by new_field, or None if no changes are required. + """ + if ( + self.connection.features.interprets_empty_strings_as_nulls + and new_field.empty_strings_allowed + ): + # The field is nullable in the database anyway, leave it alone. + return + else: + new_db_params = new_field.db_parameters(connection=self.connection) + sql = ( + self.sql_alter_column_null + if new_field.null + else self.sql_alter_column_not_null + ) + return ( + sql + % { + "column": self.quote_name(new_field.column), + "type": new_db_params["type"], + }, + [], + ) + + def _alter_column_default_sql(self, model, old_field, new_field, drop=False): + """ + Hook to specialize column default alteration. + + Return a (sql, params) fragment to add or drop (depending on the drop + argument) a default to new_field's column. + """ + new_default = self.effective_default(new_field) + default = self._column_default_sql(new_field) + params = [new_default] + + if drop: + params = [] + elif self.connection.features.requires_literal_defaults: + # Some databases (Oracle) can't take defaults as a parameter + # If this is the case, the SchemaEditor for that database should + # implement prepare_default(). + default = self.prepare_default(new_default) + params = [] + + new_db_params = new_field.db_parameters(connection=self.connection) + if drop: + if new_field.null: + sql = self.sql_alter_column_no_default_null + else: + sql = self.sql_alter_column_no_default + else: + sql = self.sql_alter_column_default + return ( + sql + % { + "column": self.quote_name(new_field.column), + "type": new_db_params["type"], + "default": default, + }, + params, + ) + + def _alter_column_type_sql( + self, model, old_field, new_field, new_type, old_collation, new_collation + ): + """ + Hook to specialize column type alteration for different backends, + for cases when a creation type is different to an alteration type + (e.g. SERIAL in PostgreSQL, PostGIS fields). + + Return a two-tuple of: an SQL fragment of (sql, params) to insert into + an ALTER TABLE statement and a list of extra (sql, params) tuples to + run once the field is altered. + """ + other_actions = [] + if collate_sql := self._collate_sql( + new_collation, old_collation, model._meta.db_table + ): + collate_sql = f" {collate_sql}" + else: + collate_sql = "" + # Comment change? + comment_sql = "" + if self.connection.features.supports_comments and not new_field.many_to_many: + if old_field.db_comment != new_field.db_comment: + # PostgreSQL and Oracle can't execute 'ALTER COLUMN ...' and + # 'COMMENT ON ...' at the same time. + sql, params = self._alter_column_comment_sql( + model, new_field, new_type, new_field.db_comment + ) + if sql: + other_actions.append((sql, params)) + if new_field.db_comment: + comment_sql = self._comment_sql(new_field.db_comment) + return ( + ( + self.sql_alter_column_type + % { + "column": self.quote_name(new_field.column), + "type": new_type, + "collation": collate_sql, + "comment": comment_sql, + }, + [], + ), + other_actions, + ) + + def _alter_column_comment_sql(self, model, new_field, new_type, new_db_comment): + return ( + self.sql_alter_column_comment + % { + "table": self.quote_name(model._meta.db_table), + "column": self.quote_name(new_field.column), + "comment": self._comment_sql(new_db_comment), + }, + [], + ) + + def _comment_sql(self, comment): + return self.quote_value(comment or "") + + def _alter_many_to_many(self, model, old_field, new_field, strict): + """Alter M2Ms to repoint their to= endpoints.""" + # Rename the through table + if ( + old_field.remote_field.through._meta.db_table + != new_field.remote_field.through._meta.db_table + ): + self.alter_db_table( + old_field.remote_field.through, + old_field.remote_field.through._meta.db_table, + new_field.remote_field.through._meta.db_table, + ) + # Repoint the FK to the other side + self.alter_field( + new_field.remote_field.through, + # The field that points to the target model is needed, so we can + # tell alter_field to change it - this is m2m_reverse_field_name() + # (as opposed to m2m_field_name(), which points to our model). + old_field.remote_field.through._meta.get_field( + old_field.m2m_reverse_field_name() + ), + new_field.remote_field.through._meta.get_field( + new_field.m2m_reverse_field_name() + ), + ) + self.alter_field( + new_field.remote_field.through, + # for self-referential models we need to alter field from the other end too + old_field.remote_field.through._meta.get_field(old_field.m2m_field_name()), + new_field.remote_field.through._meta.get_field(new_field.m2m_field_name()), + ) + + def _create_index_name(self, table_name, column_names, suffix=""): + """ + Generate a unique name for an index/unique constraint. + + The name is divided into 3 parts: the table name, the column names, + and a unique digest and suffix. + """ + _, table_name = split_identifier(table_name) + hash_suffix_part = "%s%s" % ( + names_digest(table_name, *column_names, length=8), + suffix, + ) + max_length = self.connection.ops.max_name_length() or 200 + # If everything fits into max_length, use that name. + index_name = "%s_%s_%s" % (table_name, "_".join(column_names), hash_suffix_part) + if len(index_name) <= max_length: + return index_name + # Shorten a long suffix. + if len(hash_suffix_part) > max_length / 3: + hash_suffix_part = hash_suffix_part[: max_length // 3] + other_length = (max_length - len(hash_suffix_part)) // 2 - 1 + index_name = "%s_%s_%s" % ( + table_name[:other_length], + "_".join(column_names)[:other_length], + hash_suffix_part, + ) + # Prepend D if needed to prevent the name from starting with an + # underscore or a number (not permitted on Oracle). + if index_name[0] == "_" or index_name[0].isdigit(): + index_name = "D%s" % index_name[:-1] + return index_name + + def _get_index_tablespace_sql(self, model, fields, db_tablespace=None): + if db_tablespace is None: + if len(fields) == 1 and fields[0].db_tablespace: + db_tablespace = fields[0].db_tablespace + elif settings.DEFAULT_INDEX_TABLESPACE: + db_tablespace = settings.DEFAULT_INDEX_TABLESPACE + elif model._meta.db_tablespace: + db_tablespace = model._meta.db_tablespace + if db_tablespace is not None: + return " " + self.connection.ops.tablespace_sql(db_tablespace) + return "" + + def _index_condition_sql(self, condition): + if condition: + return " WHERE " + condition + return "" + + def _index_include_sql(self, model, columns): + if not columns or not self.connection.features.supports_covering_indexes: + return "" + return Statement( + " INCLUDE (%(columns)s)", + columns=Columns(model._meta.db_table, columns, self.quote_name), + ) + + def _create_index_sql( + self, + model, + *, + fields=None, + name=None, + suffix="", + using="", + db_tablespace=None, + col_suffixes=(), + sql=None, + opclasses=(), + condition=None, + include=None, + expressions=None, + ): + """ + Return the SQL statement to create the index for one or several fields + or expressions. `sql` can be specified if the syntax differs from the + standard (GIS indexes, ...). + """ + fields = fields or [] + expressions = expressions or [] + compiler = Query(model, alias_cols=False).get_compiler( + connection=self.connection, + ) + tablespace_sql = self._get_index_tablespace_sql( + model, fields, db_tablespace=db_tablespace + ) + columns = [field.column for field in fields] + sql_create_index = sql or self.sql_create_index + table = model._meta.db_table + + def create_index_name(*args, **kwargs): + nonlocal name + if name is None: + name = self._create_index_name(*args, **kwargs) + return self.quote_name(name) + + return Statement( + sql_create_index, + table=Table(table, self.quote_name), + name=IndexName(table, columns, suffix, create_index_name), + using=using, + columns=( + self._index_columns(table, columns, col_suffixes, opclasses) + if columns + else Expressions(table, expressions, compiler, self.quote_value) + ), + extra=tablespace_sql, + condition=self._index_condition_sql(condition), + include=self._index_include_sql(model, include), + ) + + def _delete_index_sql(self, model, name, sql=None): + return Statement( + sql or self.sql_delete_index, + table=Table(model._meta.db_table, self.quote_name), + name=self.quote_name(name), + ) + + def _rename_index_sql(self, model, old_name, new_name): + return Statement( + self.sql_rename_index, + table=Table(model._meta.db_table, self.quote_name), + old_name=self.quote_name(old_name), + new_name=self.quote_name(new_name), + ) + + def _index_columns(self, table, columns, col_suffixes, opclasses): + return Columns(table, columns, self.quote_name, col_suffixes=col_suffixes) + + def _model_indexes_sql(self, model): + """ + Return a list of all index SQL statements (field indexes, + index_together, Meta.indexes) for the specified model. + """ + if not model._meta.managed or model._meta.proxy or model._meta.swapped: + return [] + output = [] + for field in model._meta.local_fields: + output.extend(self._field_indexes_sql(model, field)) + + for field_names in model._meta.index_together: + fields = [model._meta.get_field(field) for field in field_names] + output.append(self._create_index_sql(model, fields=fields, suffix="_idx")) + + for index in model._meta.indexes: + if ( + not index.contains_expressions + or self.connection.features.supports_expression_indexes + ): + output.append(index.create_sql(model, self)) + return output + + def _field_indexes_sql(self, model, field): + """ + Return a list of all index SQL statements for the specified field. + """ + output = [] + if self._field_should_be_indexed(model, field): + output.append(self._create_index_sql(model, fields=[field])) + return output + + def _field_should_be_altered(self, old_field, new_field, ignore=None): + ignore = ignore or set() + _, old_path, old_args, old_kwargs = old_field.deconstruct() + _, new_path, new_args, new_kwargs = new_field.deconstruct() + # Don't alter when: + # - changing only a field name + # - changing an attribute that doesn't affect the schema + # - changing an attribute in the provided set of ignored attributes + # - adding only a db_column and the column name is not changed + for attr in ignore.union(old_field.non_db_attrs): + old_kwargs.pop(attr, None) + for attr in ignore.union(new_field.non_db_attrs): + new_kwargs.pop(attr, None) + return self.quote_name(old_field.column) != self.quote_name( + new_field.column + ) or (old_path, old_args, old_kwargs) != (new_path, new_args, new_kwargs) + + def _field_should_be_indexed(self, model, field): + return field.db_index and not field.unique + + def _field_became_primary_key(self, old_field, new_field): + return not old_field.primary_key and new_field.primary_key + + def _unique_should_be_added(self, old_field, new_field): + return ( + not new_field.primary_key + and new_field.unique + and (not old_field.unique or old_field.primary_key) + ) + + def _rename_field_sql(self, table, old_field, new_field, new_type): + return self.sql_rename_column % { + "table": self.quote_name(table), + "old_column": self.quote_name(old_field.column), + "new_column": self.quote_name(new_field.column), + "type": new_type, + } + + def _create_fk_sql(self, model, field, suffix): + table = Table(model._meta.db_table, self.quote_name) + name = self._fk_constraint_name(model, field, suffix) + column = Columns(model._meta.db_table, [field.column], self.quote_name) + to_table = Table(field.target_field.model._meta.db_table, self.quote_name) + to_column = Columns( + field.target_field.model._meta.db_table, + [field.target_field.column], + self.quote_name, + ) + deferrable = self.connection.ops.deferrable_sql() + return Statement( + self.sql_create_fk, + table=table, + name=name, + column=column, + to_table=to_table, + to_column=to_column, + deferrable=deferrable, + ) + + def _fk_constraint_name(self, model, field, suffix): + def create_fk_name(*args, **kwargs): + return self.quote_name(self._create_index_name(*args, **kwargs)) + + return ForeignKeyName( + model._meta.db_table, + [field.column], + split_identifier(field.target_field.model._meta.db_table)[1], + [field.target_field.column], + suffix, + create_fk_name, + ) + + def _delete_fk_sql(self, model, name): + return self._delete_constraint_sql(self.sql_delete_fk, model, name) + + def _deferrable_constraint_sql(self, deferrable): + if deferrable is None: + return "" + if deferrable == Deferrable.DEFERRED: + return " DEFERRABLE INITIALLY DEFERRED" + if deferrable == Deferrable.IMMEDIATE: + return " DEFERRABLE INITIALLY IMMEDIATE" + + def _unique_sql( + self, + model, + fields, + name, + condition=None, + deferrable=None, + include=None, + opclasses=None, + expressions=None, + ): + if ( + deferrable + and not self.connection.features.supports_deferrable_unique_constraints + ): + return None + if condition or include or opclasses or expressions: + # Databases support conditional, covering, and functional unique + # constraints via a unique index. + sql = self._create_unique_sql( + model, + fields, + name=name, + condition=condition, + include=include, + opclasses=opclasses, + expressions=expressions, + ) + if sql: + self.deferred_sql.append(sql) + return None + constraint = self.sql_unique_constraint % { + "columns": ", ".join([self.quote_name(field.column) for field in fields]), + "deferrable": self._deferrable_constraint_sql(deferrable), + } + return self.sql_constraint % { + "name": self.quote_name(name), + "constraint": constraint, + } + + def _create_unique_sql( + self, + model, + fields, + name=None, + condition=None, + deferrable=None, + include=None, + opclasses=None, + expressions=None, + ): + if ( + ( + deferrable + and not self.connection.features.supports_deferrable_unique_constraints + ) + or (condition and not self.connection.features.supports_partial_indexes) + or (include and not self.connection.features.supports_covering_indexes) + or ( + expressions and not self.connection.features.supports_expression_indexes + ) + ): + return None + + compiler = Query(model, alias_cols=False).get_compiler( + connection=self.connection + ) + table = model._meta.db_table + columns = [field.column for field in fields] + if name is None: + name = self._unique_constraint_name(table, columns, quote=True) + else: + name = self.quote_name(name) + if condition or include or opclasses or expressions: + sql = self.sql_create_unique_index + else: + sql = self.sql_create_unique + if columns: + columns = self._index_columns( + table, columns, col_suffixes=(), opclasses=opclasses + ) + else: + columns = Expressions(table, expressions, compiler, self.quote_value) + return Statement( + sql, + table=Table(table, self.quote_name), + name=name, + columns=columns, + condition=self._index_condition_sql(condition), + deferrable=self._deferrable_constraint_sql(deferrable), + include=self._index_include_sql(model, include), + ) + + def _unique_constraint_name(self, table, columns, quote=True): + if quote: + + def create_unique_name(*args, **kwargs): + return self.quote_name(self._create_index_name(*args, **kwargs)) + + else: + create_unique_name = self._create_index_name + + return IndexName(table, columns, "_uniq", create_unique_name) + + def _delete_unique_sql( + self, + model, + name, + condition=None, + deferrable=None, + include=None, + opclasses=None, + expressions=None, + ): + if ( + ( + deferrable + and not self.connection.features.supports_deferrable_unique_constraints + ) + or (condition and not self.connection.features.supports_partial_indexes) + or (include and not self.connection.features.supports_covering_indexes) + or ( + expressions and not self.connection.features.supports_expression_indexes + ) + ): + return None + if condition or include or opclasses or expressions: + sql = self.sql_delete_index + else: + sql = self.sql_delete_unique + return self._delete_constraint_sql(sql, model, name) + + def _check_sql(self, name, check): + return self.sql_constraint % { + "name": self.quote_name(name), + "constraint": self.sql_check_constraint % {"check": check}, + } + + def _create_check_sql(self, model, name, check): + return Statement( + self.sql_create_check, + table=Table(model._meta.db_table, self.quote_name), + name=self.quote_name(name), + check=check, + ) + + def _delete_check_sql(self, model, name): + if not self.connection.features.supports_table_check_constraints: + return None + return self._delete_constraint_sql(self.sql_delete_check, model, name) + + def _delete_constraint_sql(self, template, model, name): + return Statement( + template, + table=Table(model._meta.db_table, self.quote_name), + name=self.quote_name(name), + ) + + def _constraint_names( + self, + model, + column_names=None, + unique=None, + primary_key=None, + index=None, + foreign_key=None, + check=None, + type_=None, + exclude=None, + ): + """Return all constraint names matching the columns and conditions.""" + if column_names is not None: + column_names = [ + self.connection.introspection.identifier_converter( + truncate_name(name, self.connection.ops.max_name_length()) + ) + if self.connection.features.truncates_names + else self.connection.introspection.identifier_converter(name) + for name in column_names + ] + with self.connection.cursor() as cursor: + constraints = self.connection.introspection.get_constraints( + cursor, model._meta.db_table + ) + result = [] + for name, infodict in constraints.items(): + if column_names is None or column_names == infodict["columns"]: + if unique is not None and infodict["unique"] != unique: + continue + if primary_key is not None and infodict["primary_key"] != primary_key: + continue + if index is not None and infodict["index"] != index: + continue + if check is not None and infodict["check"] != check: + continue + if foreign_key is not None and not infodict["foreign_key"]: + continue + if type_ is not None and infodict["type"] != type_: + continue + if not exclude or name not in exclude: + result.append(name) + return result + + def _delete_primary_key(self, model, strict=False): + constraint_names = self._constraint_names(model, primary_key=True) + if strict and len(constraint_names) != 1: + raise ValueError( + "Found wrong number (%s) of PK constraints for %s" + % ( + len(constraint_names), + model._meta.db_table, + ) + ) + for constraint_name in constraint_names: + self.execute(self._delete_primary_key_sql(model, constraint_name)) + + def _create_primary_key_sql(self, model, field): + return Statement( + self.sql_create_pk, + table=Table(model._meta.db_table, self.quote_name), + name=self.quote_name( + self._create_index_name( + model._meta.db_table, [field.column], suffix="_pk" + ) + ), + columns=Columns(model._meta.db_table, [field.column], self.quote_name), + ) + + def _delete_primary_key_sql(self, model, name): + return self._delete_constraint_sql(self.sql_delete_pk, model, name) + + def _collate_sql(self, collation, old_collation=None, table_name=None): + return "COLLATE " + self.quote_name(collation) if collation else "" + + def remove_procedure(self, procedure_name, param_types=()): + sql = self.sql_delete_procedure % { + "procedure": self.quote_name(procedure_name), + "param_types": ",".join(param_types), + } + self.execute(sql) diff --git a/virt/lib/python3.9/site-packages/django/db/backends/mysql/operations 3.py b/virt/lib/python3.9/site-packages/django/db/backends/mysql/operations 3.py new file mode 100644 index 00000000..6039b8fe --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/db/backends/mysql/operations 3.py @@ -0,0 +1,464 @@ +import uuid + +from django.conf import settings +from django.db.backends.base.operations import BaseDatabaseOperations +from django.db.backends.utils import split_tzname_delta +from django.db.models import Exists, ExpressionWrapper, Lookup +from django.db.models.constants import OnConflict +from django.utils import timezone +from django.utils.encoding import force_str +from django.utils.regex_helper import _lazy_re_compile + + +class DatabaseOperations(BaseDatabaseOperations): + compiler_module = "django.db.backends.mysql.compiler" + + # MySQL stores positive fields as UNSIGNED ints. + integer_field_ranges = { + **BaseDatabaseOperations.integer_field_ranges, + "PositiveSmallIntegerField": (0, 65535), + "PositiveIntegerField": (0, 4294967295), + "PositiveBigIntegerField": (0, 18446744073709551615), + } + cast_data_types = { + "AutoField": "signed integer", + "BigAutoField": "signed integer", + "SmallAutoField": "signed integer", + "CharField": "char(%(max_length)s)", + "DecimalField": "decimal(%(max_digits)s, %(decimal_places)s)", + "TextField": "char", + "IntegerField": "signed integer", + "BigIntegerField": "signed integer", + "SmallIntegerField": "signed integer", + "PositiveBigIntegerField": "unsigned integer", + "PositiveIntegerField": "unsigned integer", + "PositiveSmallIntegerField": "unsigned integer", + "DurationField": "signed integer", + } + cast_char_field_without_max_length = "char" + explain_prefix = "EXPLAIN" + + # EXTRACT format cannot be passed in parameters. + _extract_format_re = _lazy_re_compile(r"[A-Z_]+") + + def date_extract_sql(self, lookup_type, sql, params): + # https://dev.mysql.com/doc/mysql/en/date-and-time-functions.html + if lookup_type == "week_day": + # DAYOFWEEK() returns an integer, 1-7, Sunday=1. + return f"DAYOFWEEK({sql})", params + elif lookup_type == "iso_week_day": + # WEEKDAY() returns an integer, 0-6, Monday=0. + return f"WEEKDAY({sql}) + 1", params + elif lookup_type == "week": + # Override the value of default_week_format for consistency with + # other database backends. + # Mode 3: Monday, 1-53, with 4 or more days this year. + return f"WEEK({sql}, 3)", params + elif lookup_type == "iso_year": + # Get the year part from the YEARWEEK function, which returns a + # number as year * 100 + week. + return f"TRUNCATE(YEARWEEK({sql}, 3), -2) / 100", params + else: + # EXTRACT returns 1-53 based on ISO-8601 for the week number. + lookup_type = lookup_type.upper() + if not self._extract_format_re.fullmatch(lookup_type): + raise ValueError(f"Invalid loookup type: {lookup_type!r}") + return f"EXTRACT({lookup_type} FROM {sql})", params + + def date_trunc_sql(self, lookup_type, sql, params, tzname=None): + sql, params = self._convert_sql_to_tz(sql, params, tzname) + fields = { + "year": "%Y-01-01", + "month": "%Y-%m-01", + } + if lookup_type in fields: + format_str = fields[lookup_type] + return f"CAST(DATE_FORMAT({sql}, %s) AS DATE)", (*params, format_str) + elif lookup_type == "quarter": + return ( + f"MAKEDATE(YEAR({sql}), 1) + " + f"INTERVAL QUARTER({sql}) QUARTER - INTERVAL 1 QUARTER", + (*params, *params), + ) + elif lookup_type == "week": + return f"DATE_SUB({sql}, INTERVAL WEEKDAY({sql}) DAY)", (*params, *params) + else: + return f"DATE({sql})", params + + def _prepare_tzname_delta(self, tzname): + tzname, sign, offset = split_tzname_delta(tzname) + return f"{sign}{offset}" if offset else tzname + + def _convert_sql_to_tz(self, sql, params, tzname): + if tzname and settings.USE_TZ and self.connection.timezone_name != tzname: + return f"CONVERT_TZ({sql}, %s, %s)", ( + *params, + self.connection.timezone_name, + self._prepare_tzname_delta(tzname), + ) + return sql, params + + def datetime_cast_date_sql(self, sql, params, tzname): + sql, params = self._convert_sql_to_tz(sql, params, tzname) + return f"DATE({sql})", params + + def datetime_cast_time_sql(self, sql, params, tzname): + sql, params = self._convert_sql_to_tz(sql, params, tzname) + return f"TIME({sql})", params + + def datetime_extract_sql(self, lookup_type, sql, params, tzname): + sql, params = self._convert_sql_to_tz(sql, params, tzname) + return self.date_extract_sql(lookup_type, sql, params) + + def datetime_trunc_sql(self, lookup_type, sql, params, tzname): + sql, params = self._convert_sql_to_tz(sql, params, tzname) + fields = ["year", "month", "day", "hour", "minute", "second"] + format = ("%Y-", "%m", "-%d", " %H:", "%i", ":%s") + format_def = ("0000-", "01", "-01", " 00:", "00", ":00") + if lookup_type == "quarter": + return ( + f"CAST(DATE_FORMAT(MAKEDATE(YEAR({sql}), 1) + " + f"INTERVAL QUARTER({sql}) QUARTER - " + f"INTERVAL 1 QUARTER, %s) AS DATETIME)" + ), (*params, *params, "%Y-%m-01 00:00:00") + if lookup_type == "week": + return ( + f"CAST(DATE_FORMAT(" + f"DATE_SUB({sql}, INTERVAL WEEKDAY({sql}) DAY), %s) AS DATETIME)" + ), (*params, *params, "%Y-%m-%d 00:00:00") + try: + i = fields.index(lookup_type) + 1 + except ValueError: + pass + else: + format_str = "".join(format[:i] + format_def[i:]) + return f"CAST(DATE_FORMAT({sql}, %s) AS DATETIME)", (*params, format_str) + return sql, params + + def time_trunc_sql(self, lookup_type, sql, params, tzname=None): + sql, params = self._convert_sql_to_tz(sql, params, tzname) + fields = { + "hour": "%H:00:00", + "minute": "%H:%i:00", + "second": "%H:%i:%s", + } + if lookup_type in fields: + format_str = fields[lookup_type] + return f"CAST(DATE_FORMAT({sql}, %s) AS TIME)", (*params, format_str) + else: + return f"TIME({sql})", params + + def fetch_returned_insert_rows(self, cursor): + """ + Given a cursor object that has just performed an INSERT...RETURNING + statement into a table, return the tuple of returned data. + """ + return cursor.fetchall() + + def format_for_duration_arithmetic(self, sql): + return "INTERVAL %s MICROSECOND" % sql + + def force_no_ordering(self): + """ + "ORDER BY NULL" prevents MySQL from implicitly ordering by grouped + columns. If no ordering would otherwise be applied, we don't want any + implicit sorting going on. + """ + return [(None, ("NULL", [], False))] + + def adapt_decimalfield_value(self, value, max_digits=None, decimal_places=None): + return value + + def last_executed_query(self, cursor, sql, params): + # With MySQLdb, cursor objects have an (undocumented) "_executed" + # attribute where the exact query sent to the database is saved. + # See MySQLdb/cursors.py in the source distribution. + # MySQLdb returns string, PyMySQL bytes. + return force_str(getattr(cursor, "_executed", None), errors="replace") + + def no_limit_value(self): + # 2**64 - 1, as recommended by the MySQL documentation + return 18446744073709551615 + + def quote_name(self, name): + if name.startswith("`") and name.endswith("`"): + return name # Quoting once is enough. + return "`%s`" % name + + def return_insert_columns(self, fields): + # MySQL and MariaDB < 10.5.0 don't support an INSERT...RETURNING + # statement. + if not fields: + return "", () + columns = [ + "%s.%s" + % ( + self.quote_name(field.model._meta.db_table), + self.quote_name(field.column), + ) + for field in fields + ] + return "RETURNING %s" % ", ".join(columns), () + + def sql_flush(self, style, tables, *, reset_sequences=False, allow_cascade=False): + if not tables: + return [] + + sql = ["SET FOREIGN_KEY_CHECKS = 0;"] + if reset_sequences: + # It's faster to TRUNCATE tables that require a sequence reset + # since ALTER TABLE AUTO_INCREMENT is slower than TRUNCATE. + sql.extend( + "%s %s;" + % ( + style.SQL_KEYWORD("TRUNCATE"), + style.SQL_FIELD(self.quote_name(table_name)), + ) + for table_name in tables + ) + else: + # Otherwise issue a simple DELETE since it's faster than TRUNCATE + # and preserves sequences. + sql.extend( + "%s %s %s;" + % ( + style.SQL_KEYWORD("DELETE"), + style.SQL_KEYWORD("FROM"), + style.SQL_FIELD(self.quote_name(table_name)), + ) + for table_name in tables + ) + sql.append("SET FOREIGN_KEY_CHECKS = 1;") + return sql + + def sequence_reset_by_name_sql(self, style, sequences): + return [ + "%s %s %s %s = 1;" + % ( + style.SQL_KEYWORD("ALTER"), + style.SQL_KEYWORD("TABLE"), + style.SQL_FIELD(self.quote_name(sequence_info["table"])), + style.SQL_FIELD("AUTO_INCREMENT"), + ) + for sequence_info in sequences + ] + + def validate_autopk_value(self, value): + # Zero in AUTO_INCREMENT field does not work without the + # NO_AUTO_VALUE_ON_ZERO SQL mode. + if value == 0 and not self.connection.features.allows_auto_pk_0: + raise ValueError( + "The database backend does not accept 0 as a value for AutoField." + ) + return value + + def adapt_datetimefield_value(self, value): + if value is None: + return None + + # Expression values are adapted by the database. + if hasattr(value, "resolve_expression"): + return value + + # MySQL doesn't support tz-aware datetimes + if timezone.is_aware(value): + if settings.USE_TZ: + value = timezone.make_naive(value, self.connection.timezone) + else: + raise ValueError( + "MySQL backend does not support timezone-aware datetimes when " + "USE_TZ is False." + ) + return str(value) + + def adapt_timefield_value(self, value): + if value is None: + return None + + # Expression values are adapted by the database. + if hasattr(value, "resolve_expression"): + return value + + # MySQL doesn't support tz-aware times + if timezone.is_aware(value): + raise ValueError("MySQL backend does not support timezone-aware times.") + + return value.isoformat(timespec="microseconds") + + def max_name_length(self): + return 64 + + def pk_default_value(self): + return "NULL" + + def bulk_insert_sql(self, fields, placeholder_rows): + placeholder_rows_sql = (", ".join(row) for row in placeholder_rows) + values_sql = ", ".join("(%s)" % sql for sql in placeholder_rows_sql) + return "VALUES " + values_sql + + def combine_expression(self, connector, sub_expressions): + if connector == "^": + return "POW(%s)" % ",".join(sub_expressions) + # Convert the result to a signed integer since MySQL's binary operators + # return an unsigned integer. + elif connector in ("&", "|", "<<", "#"): + connector = "^" if connector == "#" else connector + return "CONVERT(%s, SIGNED)" % connector.join(sub_expressions) + elif connector == ">>": + lhs, rhs = sub_expressions + return "FLOOR(%(lhs)s / POW(2, %(rhs)s))" % {"lhs": lhs, "rhs": rhs} + return super().combine_expression(connector, sub_expressions) + + def get_db_converters(self, expression): + converters = super().get_db_converters(expression) + internal_type = expression.output_field.get_internal_type() + if internal_type == "BooleanField": + converters.append(self.convert_booleanfield_value) + elif internal_type == "DateTimeField": + if settings.USE_TZ: + converters.append(self.convert_datetimefield_value) + elif internal_type == "UUIDField": + converters.append(self.convert_uuidfield_value) + return converters + + def convert_booleanfield_value(self, value, expression, connection): + if value in (0, 1): + value = bool(value) + return value + + def convert_datetimefield_value(self, value, expression, connection): + if value is not None: + value = timezone.make_aware(value, self.connection.timezone) + return value + + def convert_uuidfield_value(self, value, expression, connection): + if value is not None: + value = uuid.UUID(value) + return value + + def binary_placeholder_sql(self, value): + return ( + "_binary %s" if value is not None and not hasattr(value, "as_sql") else "%s" + ) + + def subtract_temporals(self, internal_type, lhs, rhs): + lhs_sql, lhs_params = lhs + rhs_sql, rhs_params = rhs + if internal_type == "TimeField": + if self.connection.mysql_is_mariadb: + # MariaDB includes the microsecond component in TIME_TO_SEC as + # a decimal. MySQL returns an integer without microseconds. + return ( + "CAST((TIME_TO_SEC(%(lhs)s) - TIME_TO_SEC(%(rhs)s)) " + "* 1000000 AS SIGNED)" + ) % { + "lhs": lhs_sql, + "rhs": rhs_sql, + }, ( + *lhs_params, + *rhs_params, + ) + return ( + "((TIME_TO_SEC(%(lhs)s) * 1000000 + MICROSECOND(%(lhs)s)) -" + " (TIME_TO_SEC(%(rhs)s) * 1000000 + MICROSECOND(%(rhs)s)))" + ) % {"lhs": lhs_sql, "rhs": rhs_sql}, tuple(lhs_params) * 2 + tuple( + rhs_params + ) * 2 + params = (*rhs_params, *lhs_params) + return "TIMESTAMPDIFF(MICROSECOND, %s, %s)" % (rhs_sql, lhs_sql), params + + def explain_query_prefix(self, format=None, **options): + # Alias MySQL's TRADITIONAL to TEXT for consistency with other backends. + if format and format.upper() == "TEXT": + format = "TRADITIONAL" + elif ( + not format and "TREE" in self.connection.features.supported_explain_formats + ): + # Use TREE by default (if supported) as it's more informative. + format = "TREE" + analyze = options.pop("analyze", False) + prefix = super().explain_query_prefix(format, **options) + if analyze and self.connection.features.supports_explain_analyze: + # MariaDB uses ANALYZE instead of EXPLAIN ANALYZE. + prefix = ( + "ANALYZE" if self.connection.mysql_is_mariadb else prefix + " ANALYZE" + ) + if format and not (analyze and not self.connection.mysql_is_mariadb): + # Only MariaDB supports the analyze option with formats. + prefix += " FORMAT=%s" % format + return prefix + + def regex_lookup(self, lookup_type): + # REGEXP_LIKE doesn't exist in MariaDB. + if self.connection.mysql_is_mariadb: + if lookup_type == "regex": + return "%s REGEXP BINARY %s" + return "%s REGEXP %s" + + match_option = "c" if lookup_type == "regex" else "i" + return "REGEXP_LIKE(%%s, %%s, '%s')" % match_option + + def insert_statement(self, on_conflict=None): + if on_conflict == OnConflict.IGNORE: + return "INSERT IGNORE INTO" + return super().insert_statement(on_conflict=on_conflict) + + def lookup_cast(self, lookup_type, internal_type=None): + lookup = "%s" + if internal_type == "JSONField": + if self.connection.mysql_is_mariadb or lookup_type in ( + "iexact", + "contains", + "icontains", + "startswith", + "istartswith", + "endswith", + "iendswith", + "regex", + "iregex", + ): + lookup = "JSON_UNQUOTE(%s)" + return lookup + + def conditional_expression_supported_in_where_clause(self, expression): + # MySQL ignores indexes with boolean fields unless they're compared + # directly to a boolean value. + if isinstance(expression, (Exists, Lookup)): + return True + if isinstance(expression, ExpressionWrapper) and expression.conditional: + return self.conditional_expression_supported_in_where_clause( + expression.expression + ) + if getattr(expression, "conditional", False): + return False + return super().conditional_expression_supported_in_where_clause(expression) + + def on_conflict_suffix_sql(self, fields, on_conflict, update_fields, unique_fields): + if on_conflict == OnConflict.UPDATE: + conflict_suffix_sql = "ON DUPLICATE KEY UPDATE %(fields)s" + # The use of VALUES() is deprecated in MySQL 8.0.20+. Instead, use + # aliases for the new row and its columns available in MySQL + # 8.0.19+. + if not self.connection.mysql_is_mariadb: + if self.connection.mysql_version >= (8, 0, 19): + conflict_suffix_sql = f"AS new {conflict_suffix_sql}" + field_sql = "%(field)s = new.%(field)s" + else: + field_sql = "%(field)s = VALUES(%(field)s)" + # Use VALUE() on MariaDB. + else: + field_sql = "%(field)s = VALUE(%(field)s)" + + fields = ", ".join( + [ + field_sql % {"field": field} + for field in map(self.quote_name, update_fields) + ] + ) + return conflict_suffix_sql % {"fields": fields} + return super().on_conflict_suffix_sql( + fields, + on_conflict, + update_fields, + unique_fields, + ) diff --git a/virt/lib/python3.9/site-packages/django/db/backends/oracle/creation 3.py b/virt/lib/python3.9/site-packages/django/db/backends/oracle/creation 3.py new file mode 100644 index 00000000..bed980ee --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/db/backends/oracle/creation 3.py @@ -0,0 +1,464 @@ +import sys + +from django.conf import settings +from django.db import DatabaseError +from django.db.backends.base.creation import BaseDatabaseCreation +from django.utils.crypto import get_random_string +from django.utils.functional import cached_property + +TEST_DATABASE_PREFIX = "test_" + + +class DatabaseCreation(BaseDatabaseCreation): + @cached_property + def _maindb_connection(self): + """ + This is analogous to other backends' `_nodb_connection` property, + which allows access to an "administrative" connection which can + be used to manage the test databases. + For Oracle, the only connection that can be used for that purpose + is the main (non-test) connection. + """ + settings_dict = settings.DATABASES[self.connection.alias] + user = settings_dict.get("SAVED_USER") or settings_dict["USER"] + password = settings_dict.get("SAVED_PASSWORD") or settings_dict["PASSWORD"] + settings_dict = {**settings_dict, "USER": user, "PASSWORD": password} + DatabaseWrapper = type(self.connection) + return DatabaseWrapper(settings_dict, alias=self.connection.alias) + + def _create_test_db(self, verbosity=1, autoclobber=False, keepdb=False): + parameters = self._get_test_db_params() + with self._maindb_connection.cursor() as cursor: + if self._test_database_create(): + try: + self._execute_test_db_creation( + cursor, parameters, verbosity, keepdb + ) + except Exception as e: + if "ORA-01543" not in str(e): + # All errors except "tablespace already exists" cancel tests + self.log("Got an error creating the test database: %s" % e) + sys.exit(2) + if not autoclobber: + confirm = input( + "It appears the test database, %s, already exists. " + "Type 'yes' to delete it, or 'no' to cancel: " + % parameters["user"] + ) + if autoclobber or confirm == "yes": + if verbosity >= 1: + self.log( + "Destroying old test database for alias '%s'..." + % self.connection.alias + ) + try: + self._execute_test_db_destruction( + cursor, parameters, verbosity + ) + except DatabaseError as e: + if "ORA-29857" in str(e): + self._handle_objects_preventing_db_destruction( + cursor, parameters, verbosity, autoclobber + ) + else: + # Ran into a database error that isn't about + # leftover objects in the tablespace. + self.log( + "Got an error destroying the old test database: %s" + % e + ) + sys.exit(2) + except Exception as e: + self.log( + "Got an error destroying the old test database: %s" % e + ) + sys.exit(2) + try: + self._execute_test_db_creation( + cursor, parameters, verbosity, keepdb + ) + except Exception as e: + self.log( + "Got an error recreating the test database: %s" % e + ) + sys.exit(2) + else: + self.log("Tests cancelled.") + sys.exit(1) + + if self._test_user_create(): + if verbosity >= 1: + self.log("Creating test user...") + try: + self._create_test_user(cursor, parameters, verbosity, keepdb) + except Exception as e: + if "ORA-01920" not in str(e): + # All errors except "user already exists" cancel tests + self.log("Got an error creating the test user: %s" % e) + sys.exit(2) + if not autoclobber: + confirm = input( + "It appears the test user, %s, already exists. Type " + "'yes' to delete it, or 'no' to cancel: " + % parameters["user"] + ) + if autoclobber or confirm == "yes": + try: + if verbosity >= 1: + self.log("Destroying old test user...") + self._destroy_test_user(cursor, parameters, verbosity) + if verbosity >= 1: + self.log("Creating test user...") + self._create_test_user( + cursor, parameters, verbosity, keepdb + ) + except Exception as e: + self.log("Got an error recreating the test user: %s" % e) + sys.exit(2) + else: + self.log("Tests cancelled.") + sys.exit(1) + # Done with main user -- test user and tablespaces created. + self._maindb_connection.close() + self._switch_to_test_user(parameters) + return self.connection.settings_dict["NAME"] + + def _switch_to_test_user(self, parameters): + """ + Switch to the user that's used for creating the test database. + + Oracle doesn't have the concept of separate databases under the same + user, so a separate user is used; see _create_test_db(). The main user + is also needed for cleanup when testing is completed, so save its + credentials in the SAVED_USER/SAVED_PASSWORD key in the settings dict. + """ + real_settings = settings.DATABASES[self.connection.alias] + real_settings["SAVED_USER"] = self.connection.settings_dict[ + "SAVED_USER" + ] = self.connection.settings_dict["USER"] + real_settings["SAVED_PASSWORD"] = self.connection.settings_dict[ + "SAVED_PASSWORD" + ] = self.connection.settings_dict["PASSWORD"] + real_test_settings = real_settings["TEST"] + test_settings = self.connection.settings_dict["TEST"] + real_test_settings["USER"] = real_settings["USER"] = test_settings[ + "USER" + ] = self.connection.settings_dict["USER"] = parameters["user"] + real_settings["PASSWORD"] = self.connection.settings_dict[ + "PASSWORD" + ] = parameters["password"] + + def set_as_test_mirror(self, primary_settings_dict): + """ + Set this database up to be used in testing as a mirror of a primary + database whose settings are given. + """ + self.connection.settings_dict["USER"] = primary_settings_dict["USER"] + self.connection.settings_dict["PASSWORD"] = primary_settings_dict["PASSWORD"] + + def _handle_objects_preventing_db_destruction( + self, cursor, parameters, verbosity, autoclobber + ): + # There are objects in the test tablespace which prevent dropping it + # The easy fix is to drop the test user -- but are we allowed to do so? + self.log( + "There are objects in the old test database which prevent its destruction." + "\nIf they belong to the test user, deleting the user will allow the test " + "database to be recreated.\n" + "Otherwise, you will need to find and remove each of these objects, " + "or use a different tablespace.\n" + ) + if self._test_user_create(): + if not autoclobber: + confirm = input("Type 'yes' to delete user %s: " % parameters["user"]) + if autoclobber or confirm == "yes": + try: + if verbosity >= 1: + self.log("Destroying old test user...") + self._destroy_test_user(cursor, parameters, verbosity) + except Exception as e: + self.log("Got an error destroying the test user: %s" % e) + sys.exit(2) + try: + if verbosity >= 1: + self.log( + "Destroying old test database for alias '%s'..." + % self.connection.alias + ) + self._execute_test_db_destruction(cursor, parameters, verbosity) + except Exception as e: + self.log("Got an error destroying the test database: %s" % e) + sys.exit(2) + else: + self.log("Tests cancelled -- test database cannot be recreated.") + sys.exit(1) + else: + self.log( + "Django is configured to use pre-existing test user '%s'," + " and will not attempt to delete it." % parameters["user"] + ) + self.log("Tests cancelled -- test database cannot be recreated.") + sys.exit(1) + + def _destroy_test_db(self, test_database_name, verbosity=1): + """ + Destroy a test database, prompting the user for confirmation if the + database already exists. Return the name of the test database created. + """ + self.connection.settings_dict["USER"] = self.connection.settings_dict[ + "SAVED_USER" + ] + self.connection.settings_dict["PASSWORD"] = self.connection.settings_dict[ + "SAVED_PASSWORD" + ] + self.connection.close() + parameters = self._get_test_db_params() + with self._maindb_connection.cursor() as cursor: + if self._test_user_create(): + if verbosity >= 1: + self.log("Destroying test user...") + self._destroy_test_user(cursor, parameters, verbosity) + if self._test_database_create(): + if verbosity >= 1: + self.log("Destroying test database tables...") + self._execute_test_db_destruction(cursor, parameters, verbosity) + self._maindb_connection.close() + + def _execute_test_db_creation(self, cursor, parameters, verbosity, keepdb=False): + if verbosity >= 2: + self.log("_create_test_db(): dbname = %s" % parameters["user"]) + if self._test_database_oracle_managed_files(): + statements = [ + """ + CREATE TABLESPACE %(tblspace)s + DATAFILE SIZE %(size)s + AUTOEXTEND ON NEXT %(extsize)s MAXSIZE %(maxsize)s + """, + """ + CREATE TEMPORARY TABLESPACE %(tblspace_temp)s + TEMPFILE SIZE %(size_tmp)s + AUTOEXTEND ON NEXT %(extsize_tmp)s MAXSIZE %(maxsize_tmp)s + """, + ] + else: + statements = [ + """ + CREATE TABLESPACE %(tblspace)s + DATAFILE '%(datafile)s' SIZE %(size)s REUSE + AUTOEXTEND ON NEXT %(extsize)s MAXSIZE %(maxsize)s + """, + """ + CREATE TEMPORARY TABLESPACE %(tblspace_temp)s + TEMPFILE '%(datafile_tmp)s' SIZE %(size_tmp)s REUSE + AUTOEXTEND ON NEXT %(extsize_tmp)s MAXSIZE %(maxsize_tmp)s + """, + ] + # Ignore "tablespace already exists" error when keepdb is on. + acceptable_ora_err = "ORA-01543" if keepdb else None + self._execute_allow_fail_statements( + cursor, statements, parameters, verbosity, acceptable_ora_err + ) + + def _create_test_user(self, cursor, parameters, verbosity, keepdb=False): + if verbosity >= 2: + self.log("_create_test_user(): username = %s" % parameters["user"]) + statements = [ + """CREATE USER %(user)s + IDENTIFIED BY "%(password)s" + DEFAULT TABLESPACE %(tblspace)s + TEMPORARY TABLESPACE %(tblspace_temp)s + QUOTA UNLIMITED ON %(tblspace)s + """, + """GRANT CREATE SESSION, + CREATE TABLE, + CREATE SEQUENCE, + CREATE PROCEDURE, + CREATE TRIGGER + TO %(user)s""", + ] + # Ignore "user already exists" error when keepdb is on + acceptable_ora_err = "ORA-01920" if keepdb else None + success = self._execute_allow_fail_statements( + cursor, statements, parameters, verbosity, acceptable_ora_err + ) + # If the password was randomly generated, change the user accordingly. + if not success and self._test_settings_get("PASSWORD") is None: + set_password = 'ALTER USER %(user)s IDENTIFIED BY "%(password)s"' + self._execute_statements(cursor, [set_password], parameters, verbosity) + # Most test suites can be run without "create view" and + # "create materialized view" privileges. But some need it. + for object_type in ("VIEW", "MATERIALIZED VIEW"): + extra = "GRANT CREATE %(object_type)s TO %(user)s" + parameters["object_type"] = object_type + success = self._execute_allow_fail_statements( + cursor, [extra], parameters, verbosity, "ORA-01031" + ) + if not success and verbosity >= 2: + self.log( + "Failed to grant CREATE %s permission to test user. This may be ok." + % object_type + ) + + def _execute_test_db_destruction(self, cursor, parameters, verbosity): + if verbosity >= 2: + self.log("_execute_test_db_destruction(): dbname=%s" % parameters["user"]) + statements = [ + "DROP TABLESPACE %(tblspace)s " + "INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS", + "DROP TABLESPACE %(tblspace_temp)s " + "INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS", + ] + self._execute_statements(cursor, statements, parameters, verbosity) + + def _destroy_test_user(self, cursor, parameters, verbosity): + if verbosity >= 2: + self.log("_destroy_test_user(): user=%s" % parameters["user"]) + self.log("Be patient. This can take some time...") + statements = [ + "DROP USER %(user)s CASCADE", + ] + self._execute_statements(cursor, statements, parameters, verbosity) + + def _execute_statements( + self, cursor, statements, parameters, verbosity, allow_quiet_fail=False + ): + for template in statements: + stmt = template % parameters + if verbosity >= 2: + print(stmt) + try: + cursor.execute(stmt) + except Exception as err: + if (not allow_quiet_fail) or verbosity >= 2: + self.log("Failed (%s)" % (err)) + raise + + def _execute_allow_fail_statements( + self, cursor, statements, parameters, verbosity, acceptable_ora_err + ): + """ + Execute statements which are allowed to fail silently if the Oracle + error code given by `acceptable_ora_err` is raised. Return True if the + statements execute without an exception, or False otherwise. + """ + try: + # Statement can fail when acceptable_ora_err is not None + allow_quiet_fail = ( + acceptable_ora_err is not None and len(acceptable_ora_err) > 0 + ) + self._execute_statements( + cursor, + statements, + parameters, + verbosity, + allow_quiet_fail=allow_quiet_fail, + ) + return True + except DatabaseError as err: + description = str(err) + if acceptable_ora_err is None or acceptable_ora_err not in description: + raise + return False + + def _get_test_db_params(self): + return { + "dbname": self._test_database_name(), + "user": self._test_database_user(), + "password": self._test_database_passwd(), + "tblspace": self._test_database_tblspace(), + "tblspace_temp": self._test_database_tblspace_tmp(), + "datafile": self._test_database_tblspace_datafile(), + "datafile_tmp": self._test_database_tblspace_tmp_datafile(), + "maxsize": self._test_database_tblspace_maxsize(), + "maxsize_tmp": self._test_database_tblspace_tmp_maxsize(), + "size": self._test_database_tblspace_size(), + "size_tmp": self._test_database_tblspace_tmp_size(), + "extsize": self._test_database_tblspace_extsize(), + "extsize_tmp": self._test_database_tblspace_tmp_extsize(), + } + + def _test_settings_get(self, key, default=None, prefixed=None): + """ + Return a value from the test settings dict, or a given default, or a + prefixed entry from the main settings dict. + """ + settings_dict = self.connection.settings_dict + val = settings_dict["TEST"].get(key, default) + if val is None and prefixed: + val = TEST_DATABASE_PREFIX + settings_dict[prefixed] + return val + + def _test_database_name(self): + return self._test_settings_get("NAME", prefixed="NAME") + + def _test_database_create(self): + return self._test_settings_get("CREATE_DB", default=True) + + def _test_user_create(self): + return self._test_settings_get("CREATE_USER", default=True) + + def _test_database_user(self): + return self._test_settings_get("USER", prefixed="USER") + + def _test_database_passwd(self): + password = self._test_settings_get("PASSWORD") + if password is None and self._test_user_create(): + # Oracle passwords are limited to 30 chars and can't contain symbols. + password = get_random_string(30) + return password + + def _test_database_tblspace(self): + return self._test_settings_get("TBLSPACE", prefixed="USER") + + def _test_database_tblspace_tmp(self): + settings_dict = self.connection.settings_dict + return settings_dict["TEST"].get( + "TBLSPACE_TMP", TEST_DATABASE_PREFIX + settings_dict["USER"] + "_temp" + ) + + def _test_database_tblspace_datafile(self): + tblspace = "%s.dbf" % self._test_database_tblspace() + return self._test_settings_get("DATAFILE", default=tblspace) + + def _test_database_tblspace_tmp_datafile(self): + tblspace = "%s.dbf" % self._test_database_tblspace_tmp() + return self._test_settings_get("DATAFILE_TMP", default=tblspace) + + def _test_database_tblspace_maxsize(self): + return self._test_settings_get("DATAFILE_MAXSIZE", default="500M") + + def _test_database_tblspace_tmp_maxsize(self): + return self._test_settings_get("DATAFILE_TMP_MAXSIZE", default="500M") + + def _test_database_tblspace_size(self): + return self._test_settings_get("DATAFILE_SIZE", default="50M") + + def _test_database_tblspace_tmp_size(self): + return self._test_settings_get("DATAFILE_TMP_SIZE", default="50M") + + def _test_database_tblspace_extsize(self): + return self._test_settings_get("DATAFILE_EXTSIZE", default="25M") + + def _test_database_tblspace_tmp_extsize(self): + return self._test_settings_get("DATAFILE_TMP_EXTSIZE", default="25M") + + def _test_database_oracle_managed_files(self): + return self._test_settings_get("ORACLE_MANAGED_FILES", default=False) + + def _get_test_db_name(self): + """ + Return the 'production' DB name to get the test DB creation machinery + to work. This isn't a great deal in this case because DB names as + handled by Django don't have real counterparts in Oracle. + """ + return self.connection.settings_dict["NAME"] + + def test_db_signature(self): + settings_dict = self.connection.settings_dict + return ( + settings_dict["HOST"], + settings_dict["PORT"], + settings_dict["ENGINE"], + settings_dict["NAME"], + self._test_database_user(), + ) diff --git a/virt/lib/python3.9/site-packages/django/db/backends/postgresql/base 3.py b/virt/lib/python3.9/site-packages/django/db/backends/postgresql/base 3.py new file mode 100644 index 00000000..6c92678b --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/db/backends/postgresql/base 3.py @@ -0,0 +1,487 @@ +""" +PostgreSQL database backend for Django. + +Requires psycopg2 >= 2.8.4 or psycopg >= 3.1.8 +""" + +import asyncio +import threading +import warnings +from contextlib import contextmanager + +from django.conf import settings +from django.core.exceptions import ImproperlyConfigured +from django.db import DatabaseError as WrappedDatabaseError +from django.db import connections +from django.db.backends.base.base import BaseDatabaseWrapper +from django.db.backends.utils import CursorDebugWrapper as BaseCursorDebugWrapper +from django.utils.asyncio import async_unsafe +from django.utils.functional import cached_property +from django.utils.safestring import SafeString +from django.utils.version import get_version_tuple + +try: + try: + import psycopg as Database + except ImportError: + import psycopg2 as Database +except ImportError: + raise ImproperlyConfigured("Error loading psycopg2 or psycopg module") + + +def psycopg_version(): + version = Database.__version__.split(" ", 1)[0] + return get_version_tuple(version) + + +if psycopg_version() < (2, 8, 4): + raise ImproperlyConfigured( + f"psycopg2 version 2.8.4 or newer is required; you have {Database.__version__}" + ) +if (3,) <= psycopg_version() < (3, 1, 8): + raise ImproperlyConfigured( + f"psycopg version 3.1.8 or newer is required; you have {Database.__version__}" + ) + + +from .psycopg_any import IsolationLevel, is_psycopg3 # NOQA isort:skip + +if is_psycopg3: + from psycopg import adapters, sql + from psycopg.pq import Format + + from .psycopg_any import get_adapters_template, register_tzloader + + TIMESTAMPTZ_OID = adapters.types["timestamptz"].oid + +else: + import psycopg2.extensions + import psycopg2.extras + + psycopg2.extensions.register_adapter(SafeString, psycopg2.extensions.QuotedString) + psycopg2.extras.register_uuid() + + # Register support for inet[] manually so we don't have to handle the Inet() + # object on load all the time. + INETARRAY_OID = 1041 + INETARRAY = psycopg2.extensions.new_array_type( + (INETARRAY_OID,), + "INETARRAY", + psycopg2.extensions.UNICODE, + ) + psycopg2.extensions.register_type(INETARRAY) + +# Some of these import psycopg, so import them after checking if it's installed. +from .client import DatabaseClient # NOQA isort:skip +from .creation import DatabaseCreation # NOQA isort:skip +from .features import DatabaseFeatures # NOQA isort:skip +from .introspection import DatabaseIntrospection # NOQA isort:skip +from .operations import DatabaseOperations # NOQA isort:skip +from .schema import DatabaseSchemaEditor # NOQA isort:skip + + +def _get_varchar_column(data): + if data["max_length"] is None: + return "varchar" + return "varchar(%(max_length)s)" % data + + +class DatabaseWrapper(BaseDatabaseWrapper): + vendor = "postgresql" + display_name = "PostgreSQL" + # This dictionary maps Field objects to their associated PostgreSQL column + # types, as strings. Column-type strings can contain format strings; they'll + # be interpolated against the values of Field.__dict__ before being output. + # If a column type is set to None, it won't be included in the output. + data_types = { + "AutoField": "integer", + "BigAutoField": "bigint", + "BinaryField": "bytea", + "BooleanField": "boolean", + "CharField": _get_varchar_column, + "DateField": "date", + "DateTimeField": "timestamp with time zone", + "DecimalField": "numeric(%(max_digits)s, %(decimal_places)s)", + "DurationField": "interval", + "FileField": "varchar(%(max_length)s)", + "FilePathField": "varchar(%(max_length)s)", + "FloatField": "double precision", + "IntegerField": "integer", + "BigIntegerField": "bigint", + "IPAddressField": "inet", + "GenericIPAddressField": "inet", + "JSONField": "jsonb", + "OneToOneField": "integer", + "PositiveBigIntegerField": "bigint", + "PositiveIntegerField": "integer", + "PositiveSmallIntegerField": "smallint", + "SlugField": "varchar(%(max_length)s)", + "SmallAutoField": "smallint", + "SmallIntegerField": "smallint", + "TextField": "text", + "TimeField": "time", + "UUIDField": "uuid", + } + data_type_check_constraints = { + "PositiveBigIntegerField": '"%(column)s" >= 0', + "PositiveIntegerField": '"%(column)s" >= 0', + "PositiveSmallIntegerField": '"%(column)s" >= 0', + } + data_types_suffix = { + "AutoField": "GENERATED BY DEFAULT AS IDENTITY", + "BigAutoField": "GENERATED BY DEFAULT AS IDENTITY", + "SmallAutoField": "GENERATED BY DEFAULT AS IDENTITY", + } + operators = { + "exact": "= %s", + "iexact": "= UPPER(%s)", + "contains": "LIKE %s", + "icontains": "LIKE UPPER(%s)", + "regex": "~ %s", + "iregex": "~* %s", + "gt": "> %s", + "gte": ">= %s", + "lt": "< %s", + "lte": "<= %s", + "startswith": "LIKE %s", + "endswith": "LIKE %s", + "istartswith": "LIKE UPPER(%s)", + "iendswith": "LIKE UPPER(%s)", + } + + # The patterns below are used to generate SQL pattern lookup clauses when + # the right-hand side of the lookup isn't a raw string (it might be an expression + # or the result of a bilateral transformation). + # In those cases, special characters for LIKE operators (e.g. \, *, _) should be + # escaped on database side. + # + # Note: we use str.format() here for readability as '%' is used as a wildcard for + # the LIKE operator. + pattern_esc = ( + r"REPLACE(REPLACE(REPLACE({}, E'\\', E'\\\\'), E'%%', E'\\%%'), E'_', E'\\_')" + ) + pattern_ops = { + "contains": "LIKE '%%' || {} || '%%'", + "icontains": "LIKE '%%' || UPPER({}) || '%%'", + "startswith": "LIKE {} || '%%'", + "istartswith": "LIKE UPPER({}) || '%%'", + "endswith": "LIKE '%%' || {}", + "iendswith": "LIKE '%%' || UPPER({})", + } + + Database = Database + SchemaEditorClass = DatabaseSchemaEditor + # Classes instantiated in __init__(). + client_class = DatabaseClient + creation_class = DatabaseCreation + features_class = DatabaseFeatures + introspection_class = DatabaseIntrospection + ops_class = DatabaseOperations + # PostgreSQL backend-specific attributes. + _named_cursor_idx = 0 + + def get_database_version(self): + """ + Return a tuple of the database's version. + E.g. for pg_version 120004, return (12, 4). + """ + return divmod(self.pg_version, 10000) + + def get_connection_params(self): + settings_dict = self.settings_dict + # None may be used to connect to the default 'postgres' db + if settings_dict["NAME"] == "" and not settings_dict.get("OPTIONS", {}).get( + "service" + ): + raise ImproperlyConfigured( + "settings.DATABASES is improperly configured. " + "Please supply the NAME or OPTIONS['service'] value." + ) + if len(settings_dict["NAME"] or "") > self.ops.max_name_length(): + raise ImproperlyConfigured( + "The database name '%s' (%d characters) is longer than " + "PostgreSQL's limit of %d characters. Supply a shorter NAME " + "in settings.DATABASES." + % ( + settings_dict["NAME"], + len(settings_dict["NAME"]), + self.ops.max_name_length(), + ) + ) + if settings_dict["NAME"]: + conn_params = { + "dbname": settings_dict["NAME"], + **settings_dict["OPTIONS"], + } + elif settings_dict["NAME"] is None: + # Connect to the default 'postgres' db. + settings_dict.get("OPTIONS", {}).pop("service", None) + conn_params = {"dbname": "postgres", **settings_dict["OPTIONS"]} + else: + conn_params = {**settings_dict["OPTIONS"]} + conn_params["client_encoding"] = "UTF8" + + conn_params.pop("assume_role", None) + conn_params.pop("isolation_level", None) + server_side_binding = conn_params.pop("server_side_binding", None) + conn_params.setdefault( + "cursor_factory", + ServerBindingCursor + if is_psycopg3 and server_side_binding is True + else Cursor, + ) + if settings_dict["USER"]: + conn_params["user"] = settings_dict["USER"] + if settings_dict["PASSWORD"]: + conn_params["password"] = settings_dict["PASSWORD"] + if settings_dict["HOST"]: + conn_params["host"] = settings_dict["HOST"] + if settings_dict["PORT"]: + conn_params["port"] = settings_dict["PORT"] + if is_psycopg3: + conn_params["context"] = get_adapters_template( + settings.USE_TZ, self.timezone + ) + # Disable prepared statements by default to keep connection poolers + # working. Can be reenabled via OPTIONS in the settings dict. + conn_params["prepare_threshold"] = conn_params.pop( + "prepare_threshold", None + ) + return conn_params + + @async_unsafe + def get_new_connection(self, conn_params): + # self.isolation_level must be set: + # - after connecting to the database in order to obtain the database's + # default when no value is explicitly specified in options. + # - before calling _set_autocommit() because if autocommit is on, that + # will set connection.isolation_level to ISOLATION_LEVEL_AUTOCOMMIT. + options = self.settings_dict["OPTIONS"] + set_isolation_level = False + try: + isolation_level_value = options["isolation_level"] + except KeyError: + self.isolation_level = IsolationLevel.READ_COMMITTED + else: + # Set the isolation level to the value from OPTIONS. + try: + self.isolation_level = IsolationLevel(isolation_level_value) + set_isolation_level = True + except ValueError: + raise ImproperlyConfigured( + f"Invalid transaction isolation level {isolation_level_value} " + f"specified. Use one of the psycopg.IsolationLevel values." + ) + connection = self.Database.connect(**conn_params) + if set_isolation_level: + connection.isolation_level = self.isolation_level + if not is_psycopg3: + # Register dummy loads() to avoid a round trip from psycopg2's + # decode to json.dumps() to json.loads(), when using a custom + # decoder in JSONField. + psycopg2.extras.register_default_jsonb( + conn_or_curs=connection, loads=lambda x: x + ) + return connection + + def ensure_timezone(self): + if self.connection is None: + return False + conn_timezone_name = self.connection.info.parameter_status("TimeZone") + timezone_name = self.timezone_name + if timezone_name and conn_timezone_name != timezone_name: + with self.connection.cursor() as cursor: + cursor.execute(self.ops.set_time_zone_sql(), [timezone_name]) + return True + return False + + def ensure_role(self): + if self.connection is None: + return False + if new_role := self.settings_dict.get("OPTIONS", {}).get("assume_role"): + with self.connection.cursor() as cursor: + sql = self.ops.compose_sql("SET ROLE %s", [new_role]) + cursor.execute(sql) + return True + return False + + def init_connection_state(self): + super().init_connection_state() + + # Commit after setting the time zone. + commit_tz = self.ensure_timezone() + # Set the role on the connection. This is useful if the credential used + # to login is not the same as the role that owns database resources. As + # can be the case when using temporary or ephemeral credentials. + commit_role = self.ensure_role() + + if (commit_role or commit_tz) and not self.get_autocommit(): + self.connection.commit() + + @async_unsafe + def create_cursor(self, name=None): + if name: + # In autocommit mode, the cursor will be used outside of a + # transaction, hence use a holdable cursor. + cursor = self.connection.cursor( + name, scrollable=False, withhold=self.connection.autocommit + ) + else: + cursor = self.connection.cursor() + + if is_psycopg3: + # Register the cursor timezone only if the connection disagrees, to + # avoid copying the adapter map. + tzloader = self.connection.adapters.get_loader(TIMESTAMPTZ_OID, Format.TEXT) + if self.timezone != tzloader.timezone: + register_tzloader(self.timezone, cursor) + else: + cursor.tzinfo_factory = self.tzinfo_factory if settings.USE_TZ else None + return cursor + + def tzinfo_factory(self, offset): + return self.timezone + + @async_unsafe + def chunked_cursor(self): + self._named_cursor_idx += 1 + # Get the current async task + # Note that right now this is behind @async_unsafe, so this is + # unreachable, but in future we'll start loosening this restriction. + # For now, it's here so that every use of "threading" is + # also async-compatible. + try: + current_task = asyncio.current_task() + except RuntimeError: + current_task = None + # Current task can be none even if the current_task call didn't error + if current_task: + task_ident = str(id(current_task)) + else: + task_ident = "sync" + # Use that and the thread ident to get a unique name + return self._cursor( + name="_django_curs_%d_%s_%d" + % ( + # Avoid reusing name in other threads / tasks + threading.current_thread().ident, + task_ident, + self._named_cursor_idx, + ) + ) + + def _set_autocommit(self, autocommit): + with self.wrap_database_errors: + self.connection.autocommit = autocommit + + def check_constraints(self, table_names=None): + """ + Check constraints by setting them to immediate. Return them to deferred + afterward. + """ + with self.cursor() as cursor: + cursor.execute("SET CONSTRAINTS ALL IMMEDIATE") + cursor.execute("SET CONSTRAINTS ALL DEFERRED") + + def is_usable(self): + try: + # Use a psycopg cursor directly, bypassing Django's utilities. + with self.connection.cursor() as cursor: + cursor.execute("SELECT 1") + except Database.Error: + return False + else: + return True + + @contextmanager + def _nodb_cursor(self): + cursor = None + try: + with super()._nodb_cursor() as cursor: + yield cursor + except (Database.DatabaseError, WrappedDatabaseError): + if cursor is not None: + raise + warnings.warn( + "Normally Django will use a connection to the 'postgres' database " + "to avoid running initialization queries against the production " + "database when it's not needed (for example, when running tests). " + "Django was unable to create a connection to the 'postgres' database " + "and will use the first PostgreSQL database instead.", + RuntimeWarning, + ) + for connection in connections.all(): + if ( + connection.vendor == "postgresql" + and connection.settings_dict["NAME"] != "postgres" + ): + conn = self.__class__( + { + **self.settings_dict, + "NAME": connection.settings_dict["NAME"], + }, + alias=self.alias, + ) + try: + with conn.cursor() as cursor: + yield cursor + finally: + conn.close() + break + else: + raise + + @cached_property + def pg_version(self): + with self.temporary_connection(): + return self.connection.info.server_version + + def make_debug_cursor(self, cursor): + return CursorDebugWrapper(cursor, self) + + +if is_psycopg3: + + class CursorMixin: + """ + A subclass of psycopg cursor implementing callproc. + """ + + def callproc(self, name, args=None): + if not isinstance(name, sql.Identifier): + name = sql.Identifier(name) + + qparts = [sql.SQL("SELECT * FROM "), name, sql.SQL("(")] + if args: + for item in args: + qparts.append(sql.Literal(item)) + qparts.append(sql.SQL(",")) + del qparts[-1] + + qparts.append(sql.SQL(")")) + stmt = sql.Composed(qparts) + self.execute(stmt) + return args + + class ServerBindingCursor(CursorMixin, Database.Cursor): + pass + + class Cursor(CursorMixin, Database.ClientCursor): + pass + + class CursorDebugWrapper(BaseCursorDebugWrapper): + def copy(self, statement): + with self.debug_sql(statement): + return self.cursor.copy(statement) + +else: + Cursor = psycopg2.extensions.cursor + + class CursorDebugWrapper(BaseCursorDebugWrapper): + def copy_expert(self, sql, file, *args): + with self.debug_sql(sql): + return self.cursor.copy_expert(sql, file, *args) + + def copy_to(self, file, table, *args, **kwargs): + with self.debug_sql(sql="COPY %s TO STDOUT" % table): + return self.cursor.copy_to(file, table, *args, **kwargs) diff --git a/virt/lib/python3.9/site-packages/django/db/backends/sqlite3/operations 3.py b/virt/lib/python3.9/site-packages/django/db/backends/sqlite3/operations 3.py new file mode 100644 index 00000000..1db1dc7c --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/db/backends/sqlite3/operations 3.py @@ -0,0 +1,434 @@ +import datetime +import decimal +import uuid +from functools import lru_cache +from itertools import chain + +from django.conf import settings +from django.core.exceptions import FieldError +from django.db import DatabaseError, NotSupportedError, models +from django.db.backends.base.operations import BaseDatabaseOperations +from django.db.models.constants import OnConflict +from django.db.models.expressions import Col +from django.utils import timezone +from django.utils.dateparse import parse_date, parse_datetime, parse_time +from django.utils.functional import cached_property + + +class DatabaseOperations(BaseDatabaseOperations): + cast_char_field_without_max_length = "text" + cast_data_types = { + "DateField": "TEXT", + "DateTimeField": "TEXT", + } + explain_prefix = "EXPLAIN QUERY PLAN" + # List of datatypes to that cannot be extracted with JSON_EXTRACT() on + # SQLite. Use JSON_TYPE() instead. + jsonfield_datatype_values = frozenset(["null", "false", "true"]) + + def bulk_batch_size(self, fields, objs): + """ + SQLite has a compile-time default (SQLITE_LIMIT_VARIABLE_NUMBER) of + 999 variables per query. + + If there's only a single field to insert, the limit is 500 + (SQLITE_MAX_COMPOUND_SELECT). + """ + if len(fields) == 1: + return 500 + elif len(fields) > 1: + return self.connection.features.max_query_params // len(fields) + else: + return len(objs) + + def check_expression_support(self, expression): + bad_fields = (models.DateField, models.DateTimeField, models.TimeField) + bad_aggregates = (models.Sum, models.Avg, models.Variance, models.StdDev) + if isinstance(expression, bad_aggregates): + for expr in expression.get_source_expressions(): + try: + output_field = expr.output_field + except (AttributeError, FieldError): + # Not every subexpression has an output_field which is fine + # to ignore. + pass + else: + if isinstance(output_field, bad_fields): + raise NotSupportedError( + "You cannot use Sum, Avg, StdDev, and Variance " + "aggregations on date/time fields in sqlite3 " + "since date/time is saved as text." + ) + if ( + isinstance(expression, models.Aggregate) + and expression.distinct + and len(expression.source_expressions) > 1 + ): + raise NotSupportedError( + "SQLite doesn't support DISTINCT on aggregate functions " + "accepting multiple arguments." + ) + + def date_extract_sql(self, lookup_type, sql, params): + """ + Support EXTRACT with a user-defined function django_date_extract() + that's registered in connect(). Use single quotes because this is a + string and could otherwise cause a collision with a field name. + """ + return f"django_date_extract(%s, {sql})", (lookup_type.lower(), *params) + + def fetch_returned_insert_rows(self, cursor): + """ + Given a cursor object that has just performed an INSERT...RETURNING + statement into a table, return the list of returned data. + """ + return cursor.fetchall() + + def format_for_duration_arithmetic(self, sql): + """Do nothing since formatting is handled in the custom function.""" + return sql + + def date_trunc_sql(self, lookup_type, sql, params, tzname=None): + return f"django_date_trunc(%s, {sql}, %s, %s)", ( + lookup_type.lower(), + *params, + *self._convert_tznames_to_sql(tzname), + ) + + def time_trunc_sql(self, lookup_type, sql, params, tzname=None): + return f"django_time_trunc(%s, {sql}, %s, %s)", ( + lookup_type.lower(), + *params, + *self._convert_tznames_to_sql(tzname), + ) + + def _convert_tznames_to_sql(self, tzname): + if tzname and settings.USE_TZ: + return tzname, self.connection.timezone_name + return None, None + + def datetime_cast_date_sql(self, sql, params, tzname): + return f"django_datetime_cast_date({sql}, %s, %s)", ( + *params, + *self._convert_tznames_to_sql(tzname), + ) + + def datetime_cast_time_sql(self, sql, params, tzname): + return f"django_datetime_cast_time({sql}, %s, %s)", ( + *params, + *self._convert_tznames_to_sql(tzname), + ) + + def datetime_extract_sql(self, lookup_type, sql, params, tzname): + return f"django_datetime_extract(%s, {sql}, %s, %s)", ( + lookup_type.lower(), + *params, + *self._convert_tznames_to_sql(tzname), + ) + + def datetime_trunc_sql(self, lookup_type, sql, params, tzname): + return f"django_datetime_trunc(%s, {sql}, %s, %s)", ( + lookup_type.lower(), + *params, + *self._convert_tznames_to_sql(tzname), + ) + + def time_extract_sql(self, lookup_type, sql, params): + return f"django_time_extract(%s, {sql})", (lookup_type.lower(), *params) + + def pk_default_value(self): + return "NULL" + + def _quote_params_for_last_executed_query(self, params): + """ + Only for last_executed_query! Don't use this to execute SQL queries! + """ + # This function is limited both by SQLITE_LIMIT_VARIABLE_NUMBER (the + # number of parameters, default = 999) and SQLITE_MAX_COLUMN (the + # number of return values, default = 2000). Since Python's sqlite3 + # module doesn't expose the get_limit() C API, assume the default + # limits are in effect and split the work in batches if needed. + BATCH_SIZE = 999 + if len(params) > BATCH_SIZE: + results = () + for index in range(0, len(params), BATCH_SIZE): + chunk = params[index : index + BATCH_SIZE] + results += self._quote_params_for_last_executed_query(chunk) + return results + + sql = "SELECT " + ", ".join(["QUOTE(?)"] * len(params)) + # Bypass Django's wrappers and use the underlying sqlite3 connection + # to avoid logging this query - it would trigger infinite recursion. + cursor = self.connection.connection.cursor() + # Native sqlite3 cursors cannot be used as context managers. + try: + return cursor.execute(sql, params).fetchone() + finally: + cursor.close() + + def last_executed_query(self, cursor, sql, params): + # Python substitutes parameters in Modules/_sqlite/cursor.c with: + # bind_parameters(state, self->statement, parameters); + # Unfortunately there is no way to reach self->statement from Python, + # so we quote and substitute parameters manually. + if params: + if isinstance(params, (list, tuple)): + params = self._quote_params_for_last_executed_query(params) + else: + values = tuple(params.values()) + values = self._quote_params_for_last_executed_query(values) + params = dict(zip(params, values)) + return sql % params + # For consistency with SQLiteCursorWrapper.execute(), just return sql + # when there are no parameters. See #13648 and #17158. + else: + return sql + + def quote_name(self, name): + if name.startswith('"') and name.endswith('"'): + return name # Quoting once is enough. + return '"%s"' % name + + def no_limit_value(self): + return -1 + + def __references_graph(self, table_name): + query = """ + WITH tables AS ( + SELECT %s name + UNION + SELECT sqlite_master.name + FROM sqlite_master + JOIN tables ON (sql REGEXP %s || tables.name || %s) + ) SELECT name FROM tables; + """ + params = ( + table_name, + r'(?i)\s+references\s+("|\')?', + r'("|\')?\s*\(', + ) + with self.connection.cursor() as cursor: + results = cursor.execute(query, params) + return [row[0] for row in results.fetchall()] + + @cached_property + def _references_graph(self): + # 512 is large enough to fit the ~330 tables (as of this writing) in + # Django's test suite. + return lru_cache(maxsize=512)(self.__references_graph) + + def sql_flush(self, style, tables, *, reset_sequences=False, allow_cascade=False): + if tables and allow_cascade: + # Simulate TRUNCATE CASCADE by recursively collecting the tables + # referencing the tables to be flushed. + tables = set( + chain.from_iterable(self._references_graph(table) for table in tables) + ) + sql = [ + "%s %s %s;" + % ( + style.SQL_KEYWORD("DELETE"), + style.SQL_KEYWORD("FROM"), + style.SQL_FIELD(self.quote_name(table)), + ) + for table in tables + ] + if reset_sequences: + sequences = [{"table": table} for table in tables] + sql.extend(self.sequence_reset_by_name_sql(style, sequences)) + return sql + + def sequence_reset_by_name_sql(self, style, sequences): + if not sequences: + return [] + return [ + "%s %s %s %s = 0 %s %s %s (%s);" + % ( + style.SQL_KEYWORD("UPDATE"), + style.SQL_TABLE(self.quote_name("sqlite_sequence")), + style.SQL_KEYWORD("SET"), + style.SQL_FIELD(self.quote_name("seq")), + style.SQL_KEYWORD("WHERE"), + style.SQL_FIELD(self.quote_name("name")), + style.SQL_KEYWORD("IN"), + ", ".join( + ["'%s'" % sequence_info["table"] for sequence_info in sequences] + ), + ), + ] + + def adapt_datetimefield_value(self, value): + if value is None: + return None + + # Expression values are adapted by the database. + if hasattr(value, "resolve_expression"): + return value + + # SQLite doesn't support tz-aware datetimes + if timezone.is_aware(value): + if settings.USE_TZ: + value = timezone.make_naive(value, self.connection.timezone) + else: + raise ValueError( + "SQLite backend does not support timezone-aware datetimes when " + "USE_TZ is False." + ) + + return str(value) + + def adapt_timefield_value(self, value): + if value is None: + return None + + # Expression values are adapted by the database. + if hasattr(value, "resolve_expression"): + return value + + # SQLite doesn't support tz-aware datetimes + if timezone.is_aware(value): + raise ValueError("SQLite backend does not support timezone-aware times.") + + return str(value) + + def get_db_converters(self, expression): + converters = super().get_db_converters(expression) + internal_type = expression.output_field.get_internal_type() + if internal_type == "DateTimeField": + converters.append(self.convert_datetimefield_value) + elif internal_type == "DateField": + converters.append(self.convert_datefield_value) + elif internal_type == "TimeField": + converters.append(self.convert_timefield_value) + elif internal_type == "DecimalField": + converters.append(self.get_decimalfield_converter(expression)) + elif internal_type == "UUIDField": + converters.append(self.convert_uuidfield_value) + elif internal_type == "BooleanField": + converters.append(self.convert_booleanfield_value) + return converters + + def convert_datetimefield_value(self, value, expression, connection): + if value is not None: + if not isinstance(value, datetime.datetime): + value = parse_datetime(value) + if settings.USE_TZ and not timezone.is_aware(value): + value = timezone.make_aware(value, self.connection.timezone) + return value + + def convert_datefield_value(self, value, expression, connection): + if value is not None: + if not isinstance(value, datetime.date): + value = parse_date(value) + return value + + def convert_timefield_value(self, value, expression, connection): + if value is not None: + if not isinstance(value, datetime.time): + value = parse_time(value) + return value + + def get_decimalfield_converter(self, expression): + # SQLite stores only 15 significant digits. Digits coming from + # float inaccuracy must be removed. + create_decimal = decimal.Context(prec=15).create_decimal_from_float + if isinstance(expression, Col): + quantize_value = decimal.Decimal(1).scaleb( + -expression.output_field.decimal_places + ) + + def converter(value, expression, connection): + if value is not None: + return create_decimal(value).quantize( + quantize_value, context=expression.output_field.context + ) + + else: + + def converter(value, expression, connection): + if value is not None: + return create_decimal(value) + + return converter + + def convert_uuidfield_value(self, value, expression, connection): + if value is not None: + value = uuid.UUID(value) + return value + + def convert_booleanfield_value(self, value, expression, connection): + return bool(value) if value in (1, 0) else value + + def bulk_insert_sql(self, fields, placeholder_rows): + placeholder_rows_sql = (", ".join(row) for row in placeholder_rows) + values_sql = ", ".join(f"({sql})" for sql in placeholder_rows_sql) + return f"VALUES {values_sql}" + + def combine_expression(self, connector, sub_expressions): + # SQLite doesn't have a ^ operator, so use the user-defined POWER + # function that's registered in connect(). + if connector == "^": + return "POWER(%s)" % ",".join(sub_expressions) + elif connector == "#": + return "BITXOR(%s)" % ",".join(sub_expressions) + return super().combine_expression(connector, sub_expressions) + + def combine_duration_expression(self, connector, sub_expressions): + if connector not in ["+", "-", "*", "/"]: + raise DatabaseError("Invalid connector for timedelta: %s." % connector) + fn_params = ["'%s'" % connector] + sub_expressions + if len(fn_params) > 3: + raise ValueError("Too many params for timedelta operations.") + return "django_format_dtdelta(%s)" % ", ".join(fn_params) + + def integer_field_range(self, internal_type): + # SQLite doesn't enforce any integer constraints + return (None, None) + + def subtract_temporals(self, internal_type, lhs, rhs): + lhs_sql, lhs_params = lhs + rhs_sql, rhs_params = rhs + params = (*lhs_params, *rhs_params) + if internal_type == "TimeField": + return "django_time_diff(%s, %s)" % (lhs_sql, rhs_sql), params + return "django_timestamp_diff(%s, %s)" % (lhs_sql, rhs_sql), params + + def insert_statement(self, on_conflict=None): + if on_conflict == OnConflict.IGNORE: + return "INSERT OR IGNORE INTO" + return super().insert_statement(on_conflict=on_conflict) + + def return_insert_columns(self, fields): + # SQLite < 3.35 doesn't support an INSERT...RETURNING statement. + if not fields: + return "", () + columns = [ + "%s.%s" + % ( + self.quote_name(field.model._meta.db_table), + self.quote_name(field.column), + ) + for field in fields + ] + return "RETURNING %s" % ", ".join(columns), () + + def on_conflict_suffix_sql(self, fields, on_conflict, update_fields, unique_fields): + if ( + on_conflict == OnConflict.UPDATE + and self.connection.features.supports_update_conflicts_with_target + ): + return "ON CONFLICT(%s) DO UPDATE SET %s" % ( + ", ".join(map(self.quote_name, unique_fields)), + ", ".join( + [ + f"{field} = EXCLUDED.{field}" + for field in map(self.quote_name, update_fields) + ] + ), + ) + return super().on_conflict_suffix_sql( + fields, + on_conflict, + update_fields, + unique_fields, + ) diff --git a/virt/lib/python3.9/site-packages/django/db/migrations/executor 3.py b/virt/lib/python3.9/site-packages/django/db/migrations/executor 3.py new file mode 100644 index 00000000..9a236925 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/db/migrations/executor 3.py @@ -0,0 +1,410 @@ +from django.apps.registry import apps as global_apps +from django.db import migrations, router + +from .exceptions import InvalidMigrationPlan +from .loader import MigrationLoader +from .recorder import MigrationRecorder +from .state import ProjectState + + +class MigrationExecutor: + """ + End-to-end migration execution - load migrations and run them up or down + to a specified set of targets. + """ + + def __init__(self, connection, progress_callback=None): + self.connection = connection + self.loader = MigrationLoader(self.connection) + self.recorder = MigrationRecorder(self.connection) + self.progress_callback = progress_callback + + def migration_plan(self, targets, clean_start=False): + """ + Given a set of targets, return a list of (Migration instance, backwards?). + """ + plan = [] + if clean_start: + applied = {} + else: + applied = dict(self.loader.applied_migrations) + for target in targets: + # If the target is (app_label, None), that means unmigrate everything + if target[1] is None: + for root in self.loader.graph.root_nodes(): + if root[0] == target[0]: + for migration in self.loader.graph.backwards_plan(root): + if migration in applied: + plan.append((self.loader.graph.nodes[migration], True)) + applied.pop(migration) + # If the migration is already applied, do backwards mode, + # otherwise do forwards mode. + elif target in applied: + # If the target is missing, it's likely a replaced migration. + # Reload the graph without replacements. + if ( + self.loader.replace_migrations + and target not in self.loader.graph.node_map + ): + self.loader.replace_migrations = False + self.loader.build_graph() + return self.migration_plan(targets, clean_start=clean_start) + # Don't migrate backwards all the way to the target node (that + # may roll back dependencies in other apps that don't need to + # be rolled back); instead roll back through target's immediate + # child(ren) in the same app, and no further. + next_in_app = sorted( + n + for n in self.loader.graph.node_map[target].children + if n[0] == target[0] + ) + for node in next_in_app: + for migration in self.loader.graph.backwards_plan(node): + if migration in applied: + plan.append((self.loader.graph.nodes[migration], True)) + applied.pop(migration) + else: + for migration in self.loader.graph.forwards_plan(target): + if migration not in applied: + plan.append((self.loader.graph.nodes[migration], False)) + applied[migration] = self.loader.graph.nodes[migration] + return plan + + def _create_project_state(self, with_applied_migrations=False): + """ + Create a project state including all the applications without + migrations and applied migrations if with_applied_migrations=True. + """ + state = ProjectState(real_apps=self.loader.unmigrated_apps) + if with_applied_migrations: + # Create the forwards plan Django would follow on an empty database + full_plan = self.migration_plan( + self.loader.graph.leaf_nodes(), clean_start=True + ) + applied_migrations = { + self.loader.graph.nodes[key] + for key in self.loader.applied_migrations + if key in self.loader.graph.nodes + } + for migration, _ in full_plan: + if migration in applied_migrations: + migration.mutate_state(state, preserve=False) + return state + + def migrate(self, targets, plan=None, state=None, fake=False, fake_initial=False): + """ + Migrate the database up to the given targets. + + Django first needs to create all project states before a migration is + (un)applied and in a second step run all the database operations. + """ + # The django_migrations table must be present to record applied + # migrations, but don't create it if there are no migrations to apply. + if plan == []: + if not self.recorder.has_table(): + return self._create_project_state(with_applied_migrations=False) + else: + self.recorder.ensure_schema() + + if plan is None: + plan = self.migration_plan(targets) + # Create the forwards plan Django would follow on an empty database + full_plan = self.migration_plan( + self.loader.graph.leaf_nodes(), clean_start=True + ) + + all_forwards = all(not backwards for mig, backwards in plan) + all_backwards = all(backwards for mig, backwards in plan) + + if not plan: + if state is None: + # The resulting state should include applied migrations. + state = self._create_project_state(with_applied_migrations=True) + elif all_forwards == all_backwards: + # This should only happen if there's a mixed plan + raise InvalidMigrationPlan( + "Migration plans with both forwards and backwards migrations " + "are not supported. Please split your migration process into " + "separate plans of only forwards OR backwards migrations.", + plan, + ) + elif all_forwards: + if state is None: + # The resulting state should still include applied migrations. + state = self._create_project_state(with_applied_migrations=True) + state = self._migrate_all_forwards( + state, plan, full_plan, fake=fake, fake_initial=fake_initial + ) + else: + # No need to check for `elif all_backwards` here, as that condition + # would always evaluate to true. + state = self._migrate_all_backwards(plan, full_plan, fake=fake) + + self.check_replacements() + + return state + + def _migrate_all_forwards(self, state, plan, full_plan, fake, fake_initial): + """ + Take a list of 2-tuples of the form (migration instance, False) and + apply them in the order they occur in the full_plan. + """ + migrations_to_run = {m[0] for m in plan} + for migration, _ in full_plan: + if not migrations_to_run: + # We remove every migration that we applied from these sets so + # that we can bail out once the last migration has been applied + # and don't always run until the very end of the migration + # process. + break + if migration in migrations_to_run: + if "apps" not in state.__dict__: + if self.progress_callback: + self.progress_callback("render_start") + state.apps # Render all -- performance critical + if self.progress_callback: + self.progress_callback("render_success") + state = self.apply_migration( + state, migration, fake=fake, fake_initial=fake_initial + ) + migrations_to_run.remove(migration) + + return state + + def _migrate_all_backwards(self, plan, full_plan, fake): + """ + Take a list of 2-tuples of the form (migration instance, True) and + unapply them in reverse order they occur in the full_plan. + + Since unapplying a migration requires the project state prior to that + migration, Django will compute the migration states before each of them + in a first run over the plan and then unapply them in a second run over + the plan. + """ + migrations_to_run = {m[0] for m in plan} + # Holds all migration states prior to the migrations being unapplied + states = {} + state = self._create_project_state() + applied_migrations = { + self.loader.graph.nodes[key] + for key in self.loader.applied_migrations + if key in self.loader.graph.nodes + } + if self.progress_callback: + self.progress_callback("render_start") + for migration, _ in full_plan: + if not migrations_to_run: + # We remove every migration that we applied from this set so + # that we can bail out once the last migration has been applied + # and don't always run until the very end of the migration + # process. + break + if migration in migrations_to_run: + if "apps" not in state.__dict__: + state.apps # Render all -- performance critical + # The state before this migration + states[migration] = state + # The old state keeps as-is, we continue with the new state + state = migration.mutate_state(state, preserve=True) + migrations_to_run.remove(migration) + elif migration in applied_migrations: + # Only mutate the state if the migration is actually applied + # to make sure the resulting state doesn't include changes + # from unrelated migrations. + migration.mutate_state(state, preserve=False) + if self.progress_callback: + self.progress_callback("render_success") + + for migration, _ in plan: + self.unapply_migration(states[migration], migration, fake=fake) + applied_migrations.remove(migration) + + # Generate the post migration state by starting from the state before + # the last migration is unapplied and mutating it to include all the + # remaining applied migrations. + last_unapplied_migration = plan[-1][0] + state = states[last_unapplied_migration] + for index, (migration, _) in enumerate(full_plan): + if migration == last_unapplied_migration: + for migration, _ in full_plan[index:]: + if migration in applied_migrations: + migration.mutate_state(state, preserve=False) + break + + return state + + def apply_migration(self, state, migration, fake=False, fake_initial=False): + """Run a migration forwards.""" + migration_recorded = False + if self.progress_callback: + self.progress_callback("apply_start", migration, fake) + if not fake: + if fake_initial: + # Test to see if this is an already-applied initial migration + applied, state = self.detect_soft_applied(state, migration) + if applied: + fake = True + if not fake: + # Alright, do it normally + with self.connection.schema_editor( + atomic=migration.atomic + ) as schema_editor: + state = migration.apply(state, schema_editor) + if not schema_editor.deferred_sql: + self.record_migration(migration) + migration_recorded = True + if not migration_recorded: + self.record_migration(migration) + # Report progress + if self.progress_callback: + self.progress_callback("apply_success", migration, fake) + return state + + def record_migration(self, migration): + # For replacement migrations, record individual statuses + if migration.replaces: + for app_label, name in migration.replaces: + self.recorder.record_applied(app_label, name) + else: + self.recorder.record_applied(migration.app_label, migration.name) + + def unapply_migration(self, state, migration, fake=False): + """Run a migration backwards.""" + if self.progress_callback: + self.progress_callback("unapply_start", migration, fake) + if not fake: + with self.connection.schema_editor( + atomic=migration.atomic + ) as schema_editor: + state = migration.unapply(state, schema_editor) + # For replacement migrations, also record individual statuses. + if migration.replaces: + for app_label, name in migration.replaces: + self.recorder.record_unapplied(app_label, name) + self.recorder.record_unapplied(migration.app_label, migration.name) + # Report progress + if self.progress_callback: + self.progress_callback("unapply_success", migration, fake) + return state + + def check_replacements(self): + """ + Mark replacement migrations applied if their replaced set all are. + + Do this unconditionally on every migrate, rather than just when + migrations are applied or unapplied, to correctly handle the case + when a new squash migration is pushed to a deployment that already had + all its replaced migrations applied. In this case no new migration will + be applied, but the applied state of the squashed migration must be + maintained. + """ + applied = self.recorder.applied_migrations() + for key, migration in self.loader.replacements.items(): + all_applied = all(m in applied for m in migration.replaces) + if all_applied and key not in applied: + self.recorder.record_applied(*key) + + def detect_soft_applied(self, project_state, migration): + """ + Test whether a migration has been implicitly applied - that the + tables or columns it would create exist. This is intended only for use + on initial migrations (as it only looks for CreateModel and AddField). + """ + + def should_skip_detecting_model(migration, model): + """ + No need to detect tables for proxy models, unmanaged models, or + models that can't be migrated on the current database. + """ + return ( + model._meta.proxy + or not model._meta.managed + or not router.allow_migrate( + self.connection.alias, + migration.app_label, + model_name=model._meta.model_name, + ) + ) + + if migration.initial is None: + # Bail if the migration isn't the first one in its app + if any(app == migration.app_label for app, name in migration.dependencies): + return False, project_state + elif migration.initial is False: + # Bail if it's NOT an initial migration + return False, project_state + + if project_state is None: + after_state = self.loader.project_state( + (migration.app_label, migration.name), at_end=True + ) + else: + after_state = migration.mutate_state(project_state) + apps = after_state.apps + found_create_model_migration = False + found_add_field_migration = False + fold_identifier_case = self.connection.features.ignores_table_name_case + with self.connection.cursor() as cursor: + existing_table_names = set( + self.connection.introspection.table_names(cursor) + ) + if fold_identifier_case: + existing_table_names = { + name.casefold() for name in existing_table_names + } + # Make sure all create model and add field operations are done + for operation in migration.operations: + if isinstance(operation, migrations.CreateModel): + model = apps.get_model(migration.app_label, operation.name) + if model._meta.swapped: + # We have to fetch the model to test with from the + # main app cache, as it's not a direct dependency. + model = global_apps.get_model(model._meta.swapped) + if should_skip_detecting_model(migration, model): + continue + db_table = model._meta.db_table + if fold_identifier_case: + db_table = db_table.casefold() + if db_table not in existing_table_names: + return False, project_state + found_create_model_migration = True + elif isinstance(operation, migrations.AddField): + model = apps.get_model(migration.app_label, operation.model_name) + if model._meta.swapped: + # We have to fetch the model to test with from the + # main app cache, as it's not a direct dependency. + model = global_apps.get_model(model._meta.swapped) + if should_skip_detecting_model(migration, model): + continue + + table = model._meta.db_table + field = model._meta.get_field(operation.name) + + # Handle implicit many-to-many tables created by AddField. + if field.many_to_many: + through_db_table = field.remote_field.through._meta.db_table + if fold_identifier_case: + through_db_table = through_db_table.casefold() + if through_db_table not in existing_table_names: + return False, project_state + else: + found_add_field_migration = True + continue + with self.connection.cursor() as cursor: + columns = self.connection.introspection.get_table_description( + cursor, table + ) + for column in columns: + field_column = field.column + column_name = column.name + if fold_identifier_case: + column_name = column_name.casefold() + field_column = field_column.casefold() + if column_name == field_column: + found_add_field_migration = True + break + else: + return False, project_state + # If we get this far and we found at least one CreateModel or AddField + # migration, the migration is considered implicitly applied. + return (found_create_model_migration or found_add_field_migration), after_state diff --git a/virt/lib/python3.9/site-packages/django/db/models/fields/__init__ 3.py b/virt/lib/python3.9/site-packages/django/db/models/fields/__init__ 3.py new file mode 100644 index 00000000..b86aa5bb --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/db/models/fields/__init__ 3.py @@ -0,0 +1,2812 @@ +import collections.abc +import copy +import datetime +import decimal +import operator +import uuid +import warnings +from base64 import b64decode, b64encode +from functools import partialmethod, total_ordering + +from django import forms +from django.apps import apps +from django.conf import settings +from django.core import checks, exceptions, validators +from django.db import connection, connections, router +from django.db.models.constants import LOOKUP_SEP +from django.db.models.query_utils import DeferredAttribute, RegisterLookupMixin +from django.utils import timezone +from django.utils.datastructures import DictWrapper +from django.utils.dateparse import ( + parse_date, + parse_datetime, + parse_duration, + parse_time, +) +from django.utils.duration import duration_microseconds, duration_string +from django.utils.functional import Promise, cached_property +from django.utils.ipv6 import clean_ipv6_address +from django.utils.itercompat import is_iterable +from django.utils.text import capfirst +from django.utils.translation import gettext_lazy as _ + +__all__ = [ + "AutoField", + "BLANK_CHOICE_DASH", + "BigAutoField", + "BigIntegerField", + "BinaryField", + "BooleanField", + "CharField", + "CommaSeparatedIntegerField", + "DateField", + "DateTimeField", + "DecimalField", + "DurationField", + "EmailField", + "Empty", + "Field", + "FilePathField", + "FloatField", + "GenericIPAddressField", + "IPAddressField", + "IntegerField", + "NOT_PROVIDED", + "NullBooleanField", + "PositiveBigIntegerField", + "PositiveIntegerField", + "PositiveSmallIntegerField", + "SlugField", + "SmallAutoField", + "SmallIntegerField", + "TextField", + "TimeField", + "URLField", + "UUIDField", +] + + +class Empty: + pass + + +class NOT_PROVIDED: + pass + + +# The values to use for "blank" in SelectFields. Will be appended to the start +# of most "choices" lists. +BLANK_CHOICE_DASH = [("", "---------")] + + +def _load_field(app_label, model_name, field_name): + return apps.get_model(app_label, model_name)._meta.get_field(field_name) + + +# A guide to Field parameters: +# +# * name: The name of the field specified in the model. +# * attname: The attribute to use on the model object. This is the same as +# "name", except in the case of ForeignKeys, where "_id" is +# appended. +# * db_column: The db_column specified in the model (or None). +# * column: The database column for this field. This is the same as +# "attname", except if db_column is specified. +# +# Code that introspects values, or does other dynamic things, should use +# attname. For example, this gets the primary key value of object "obj": +# +# getattr(obj, opts.pk.attname) + + +def _empty(of_cls): + new = Empty() + new.__class__ = of_cls + return new + + +def return_None(): + return None + + +@total_ordering +class Field(RegisterLookupMixin): + """Base class for all field types""" + + # Designates whether empty strings fundamentally are allowed at the + # database level. + empty_strings_allowed = True + empty_values = list(validators.EMPTY_VALUES) + + # These track each time a Field instance is created. Used to retain order. + # The auto_creation_counter is used for fields that Django implicitly + # creates, creation_counter is used for all user-specified fields. + creation_counter = 0 + auto_creation_counter = -1 + default_validators = [] # Default set of validators + default_error_messages = { + "invalid_choice": _("Value %(value)r is not a valid choice."), + "null": _("This field cannot be null."), + "blank": _("This field cannot be blank."), + "unique": _("%(model_name)s with this %(field_label)s already exists."), + "unique_for_date": _( + # Translators: The 'lookup_type' is one of 'date', 'year' or + # 'month'. Eg: "Title must be unique for pub_date year" + "%(field_label)s must be unique for " + "%(date_field_label)s %(lookup_type)s." + ), + } + system_check_deprecated_details = None + system_check_removed_details = None + + # Attributes that don't affect a column definition. + # These attributes are ignored when altering the field. + non_db_attrs = ( + "blank", + "choices", + "db_column", + "editable", + "error_messages", + "help_text", + "limit_choices_to", + # Database-level options are not supported, see #21961. + "on_delete", + "related_name", + "related_query_name", + "validators", + "verbose_name", + ) + + # Field flags + hidden = False + + many_to_many = None + many_to_one = None + one_to_many = None + one_to_one = None + related_model = None + + descriptor_class = DeferredAttribute + + # Generic field type description, usually overridden by subclasses + def _description(self): + return _("Field of type: %(field_type)s") % { + "field_type": self.__class__.__name__ + } + + description = property(_description) + + def __init__( + self, + verbose_name=None, + name=None, + primary_key=False, + max_length=None, + unique=False, + blank=False, + null=False, + db_index=False, + rel=None, + default=NOT_PROVIDED, + editable=True, + serialize=True, + unique_for_date=None, + unique_for_month=None, + unique_for_year=None, + choices=None, + help_text="", + db_column=None, + db_tablespace=None, + auto_created=False, + validators=(), + error_messages=None, + db_comment=None, + ): + self.name = name + self.verbose_name = verbose_name # May be set by set_attributes_from_name + self._verbose_name = verbose_name # Store original for deconstruction + self.primary_key = primary_key + self.max_length, self._unique = max_length, unique + self.blank, self.null = blank, null + self.remote_field = rel + self.is_relation = self.remote_field is not None + self.default = default + self.editable = editable + self.serialize = serialize + self.unique_for_date = unique_for_date + self.unique_for_month = unique_for_month + self.unique_for_year = unique_for_year + if isinstance(choices, collections.abc.Iterator): + choices = list(choices) + self.choices = choices + self.help_text = help_text + self.db_index = db_index + self.db_column = db_column + self.db_comment = db_comment + self._db_tablespace = db_tablespace + self.auto_created = auto_created + + # Adjust the appropriate creation counter, and save our local copy. + if auto_created: + self.creation_counter = Field.auto_creation_counter + Field.auto_creation_counter -= 1 + else: + self.creation_counter = Field.creation_counter + Field.creation_counter += 1 + + self._validators = list(validators) # Store for deconstruction later + + self._error_messages = error_messages # Store for deconstruction later + + def __str__(self): + """ + Return "app_label.model_label.field_name" for fields attached to + models. + """ + if not hasattr(self, "model"): + return super().__str__() + model = self.model + return "%s.%s" % (model._meta.label, self.name) + + def __repr__(self): + """Display the module, class, and name of the field.""" + path = "%s.%s" % (self.__class__.__module__, self.__class__.__qualname__) + name = getattr(self, "name", None) + if name is not None: + return "<%s: %s>" % (path, name) + return "<%s>" % path + + def check(self, **kwargs): + return [ + *self._check_field_name(), + *self._check_choices(), + *self._check_db_index(), + *self._check_db_comment(**kwargs), + *self._check_null_allowed_for_primary_keys(), + *self._check_backend_specific_checks(**kwargs), + *self._check_validators(), + *self._check_deprecation_details(), + ] + + def _check_field_name(self): + """ + Check if field name is valid, i.e. 1) does not end with an + underscore, 2) does not contain "__" and 3) is not "pk". + """ + if self.name.endswith("_"): + return [ + checks.Error( + "Field names must not end with an underscore.", + obj=self, + id="fields.E001", + ) + ] + elif LOOKUP_SEP in self.name: + return [ + checks.Error( + 'Field names must not contain "%s".' % LOOKUP_SEP, + obj=self, + id="fields.E002", + ) + ] + elif self.name == "pk": + return [ + checks.Error( + "'pk' is a reserved word that cannot be used as a field name.", + obj=self, + id="fields.E003", + ) + ] + else: + return [] + + @classmethod + def _choices_is_value(cls, value): + return isinstance(value, (str, Promise)) or not is_iterable(value) + + def _check_choices(self): + if not self.choices: + return [] + + if not is_iterable(self.choices) or isinstance(self.choices, str): + return [ + checks.Error( + "'choices' must be an iterable (e.g., a list or tuple).", + obj=self, + id="fields.E004", + ) + ] + + choice_max_length = 0 + # Expect [group_name, [value, display]] + for choices_group in self.choices: + try: + group_name, group_choices = choices_group + except (TypeError, ValueError): + # Containing non-pairs + break + try: + if not all( + self._choices_is_value(value) and self._choices_is_value(human_name) + for value, human_name in group_choices + ): + break + if self.max_length is not None and group_choices: + choice_max_length = max( + [ + choice_max_length, + *( + len(value) + for value, _ in group_choices + if isinstance(value, str) + ), + ] + ) + except (TypeError, ValueError): + # No groups, choices in the form [value, display] + value, human_name = group_name, group_choices + if not self._choices_is_value(value) or not self._choices_is_value( + human_name + ): + break + if self.max_length is not None and isinstance(value, str): + choice_max_length = max(choice_max_length, len(value)) + + # Special case: choices=['ab'] + if isinstance(choices_group, str): + break + else: + if self.max_length is not None and choice_max_length > self.max_length: + return [ + checks.Error( + "'max_length' is too small to fit the longest value " + "in 'choices' (%d characters)." % choice_max_length, + obj=self, + id="fields.E009", + ), + ] + return [] + + return [ + checks.Error( + "'choices' must be an iterable containing " + "(actual value, human readable name) tuples.", + obj=self, + id="fields.E005", + ) + ] + + def _check_db_index(self): + if self.db_index not in (None, True, False): + return [ + checks.Error( + "'db_index' must be None, True or False.", + obj=self, + id="fields.E006", + ) + ] + else: + return [] + + def _check_db_comment(self, databases=None, **kwargs): + if not self.db_comment or not databases: + return [] + errors = [] + for db in databases: + if not router.allow_migrate_model(db, self.model): + continue + connection = connections[db] + if not ( + connection.features.supports_comments + or "supports_comments" in self.model._meta.required_db_features + ): + errors.append( + checks.Warning( + f"{connection.display_name} does not support comments on " + f"columns (db_comment).", + obj=self, + id="fields.W163", + ) + ) + return errors + + def _check_null_allowed_for_primary_keys(self): + if ( + self.primary_key + and self.null + and not connection.features.interprets_empty_strings_as_nulls + ): + # We cannot reliably check this for backends like Oracle which + # consider NULL and '' to be equal (and thus set up + # character-based fields a little differently). + return [ + checks.Error( + "Primary keys must not have null=True.", + hint=( + "Set null=False on the field, or " + "remove primary_key=True argument." + ), + obj=self, + id="fields.E007", + ) + ] + else: + return [] + + def _check_backend_specific_checks(self, databases=None, **kwargs): + if databases is None: + return [] + errors = [] + for alias in databases: + if router.allow_migrate_model(alias, self.model): + errors.extend(connections[alias].validation.check_field(self, **kwargs)) + return errors + + def _check_validators(self): + errors = [] + for i, validator in enumerate(self.validators): + if not callable(validator): + errors.append( + checks.Error( + "All 'validators' must be callable.", + hint=( + "validators[{i}] ({repr}) isn't a function or " + "instance of a validator class.".format( + i=i, + repr=repr(validator), + ) + ), + obj=self, + id="fields.E008", + ) + ) + return errors + + def _check_deprecation_details(self): + if self.system_check_removed_details is not None: + return [ + checks.Error( + self.system_check_removed_details.get( + "msg", + "%s has been removed except for support in historical " + "migrations." % self.__class__.__name__, + ), + hint=self.system_check_removed_details.get("hint"), + obj=self, + id=self.system_check_removed_details.get("id", "fields.EXXX"), + ) + ] + elif self.system_check_deprecated_details is not None: + return [ + checks.Warning( + self.system_check_deprecated_details.get( + "msg", "%s has been deprecated." % self.__class__.__name__ + ), + hint=self.system_check_deprecated_details.get("hint"), + obj=self, + id=self.system_check_deprecated_details.get("id", "fields.WXXX"), + ) + ] + return [] + + def get_col(self, alias, output_field=None): + if alias == self.model._meta.db_table and ( + output_field is None or output_field == self + ): + return self.cached_col + from django.db.models.expressions import Col + + return Col(alias, self, output_field) + + @cached_property + def cached_col(self): + from django.db.models.expressions import Col + + return Col(self.model._meta.db_table, self) + + def select_format(self, compiler, sql, params): + """ + Custom format for select clauses. For example, GIS columns need to be + selected as AsText(table.col) on MySQL as the table.col data can't be + used by Django. + """ + return sql, params + + def deconstruct(self): + """ + Return enough information to recreate the field as a 4-tuple: + + * The name of the field on the model, if contribute_to_class() has + been run. + * The import path of the field, including the class, e.g. + django.db.models.IntegerField. This should be the most portable + version, so less specific may be better. + * A list of positional arguments. + * A dict of keyword arguments. + + Note that the positional or keyword arguments must contain values of + the following types (including inner values of collection types): + + * None, bool, str, int, float, complex, set, frozenset, list, tuple, + dict + * UUID + * datetime.datetime (naive), datetime.date + * top-level classes, top-level functions - will be referenced by their + full import path + * Storage instances - these have their own deconstruct() method + + This is because the values here must be serialized into a text format + (possibly new Python code, possibly JSON) and these are the only types + with encoding handlers defined. + + There's no need to return the exact way the field was instantiated this + time, just ensure that the resulting field is the same - prefer keyword + arguments over positional ones, and omit parameters with their default + values. + """ + # Short-form way of fetching all the default parameters + keywords = {} + possibles = { + "verbose_name": None, + "primary_key": False, + "max_length": None, + "unique": False, + "blank": False, + "null": False, + "db_index": False, + "default": NOT_PROVIDED, + "editable": True, + "serialize": True, + "unique_for_date": None, + "unique_for_month": None, + "unique_for_year": None, + "choices": None, + "help_text": "", + "db_column": None, + "db_comment": None, + "db_tablespace": None, + "auto_created": False, + "validators": [], + "error_messages": None, + } + attr_overrides = { + "unique": "_unique", + "error_messages": "_error_messages", + "validators": "_validators", + "verbose_name": "_verbose_name", + "db_tablespace": "_db_tablespace", + } + equals_comparison = {"choices", "validators"} + for name, default in possibles.items(): + value = getattr(self, attr_overrides.get(name, name)) + # Unroll anything iterable for choices into a concrete list + if name == "choices" and isinstance(value, collections.abc.Iterable): + value = list(value) + # Do correct kind of comparison + if name in equals_comparison: + if value != default: + keywords[name] = value + else: + if value is not default: + keywords[name] = value + # Work out path - we shorten it for known Django core fields + path = "%s.%s" % (self.__class__.__module__, self.__class__.__qualname__) + if path.startswith("django.db.models.fields.related"): + path = path.replace("django.db.models.fields.related", "django.db.models") + elif path.startswith("django.db.models.fields.files"): + path = path.replace("django.db.models.fields.files", "django.db.models") + elif path.startswith("django.db.models.fields.json"): + path = path.replace("django.db.models.fields.json", "django.db.models") + elif path.startswith("django.db.models.fields.proxy"): + path = path.replace("django.db.models.fields.proxy", "django.db.models") + elif path.startswith("django.db.models.fields"): + path = path.replace("django.db.models.fields", "django.db.models") + # Return basic info - other fields should override this. + return (self.name, path, [], keywords) + + def clone(self): + """ + Uses deconstruct() to clone a new copy of this Field. + Will not preserve any class attachments/attribute names. + """ + name, path, args, kwargs = self.deconstruct() + return self.__class__(*args, **kwargs) + + def __eq__(self, other): + # Needed for @total_ordering + if isinstance(other, Field): + return self.creation_counter == other.creation_counter and getattr( + self, "model", None + ) == getattr(other, "model", None) + return NotImplemented + + def __lt__(self, other): + # This is needed because bisect does not take a comparison function. + # Order by creation_counter first for backward compatibility. + if isinstance(other, Field): + if ( + self.creation_counter != other.creation_counter + or not hasattr(self, "model") + and not hasattr(other, "model") + ): + return self.creation_counter < other.creation_counter + elif hasattr(self, "model") != hasattr(other, "model"): + return not hasattr(self, "model") # Order no-model fields first + else: + # creation_counter's are equal, compare only models. + return (self.model._meta.app_label, self.model._meta.model_name) < ( + other.model._meta.app_label, + other.model._meta.model_name, + ) + return NotImplemented + + def __hash__(self): + return hash(self.creation_counter) + + def __deepcopy__(self, memodict): + # We don't have to deepcopy very much here, since most things are not + # intended to be altered after initial creation. + obj = copy.copy(self) + if self.remote_field: + obj.remote_field = copy.copy(self.remote_field) + if hasattr(self.remote_field, "field") and self.remote_field.field is self: + obj.remote_field.field = obj + memodict[id(self)] = obj + return obj + + def __copy__(self): + # We need to avoid hitting __reduce__, so define this + # slightly weird copy construct. + obj = Empty() + obj.__class__ = self.__class__ + obj.__dict__ = self.__dict__.copy() + return obj + + def __reduce__(self): + """ + Pickling should return the model._meta.fields instance of the field, + not a new copy of that field. So, use the app registry to load the + model and then the field back. + """ + if not hasattr(self, "model"): + # Fields are sometimes used without attaching them to models (for + # example in aggregation). In this case give back a plain field + # instance. The code below will create a new empty instance of + # class self.__class__, then update its dict with self.__dict__ + # values - so, this is very close to normal pickle. + state = self.__dict__.copy() + # The _get_default cached_property can't be pickled due to lambda + # usage. + state.pop("_get_default", None) + return _empty, (self.__class__,), state + return _load_field, ( + self.model._meta.app_label, + self.model._meta.object_name, + self.name, + ) + + def get_pk_value_on_save(self, instance): + """ + Hook to generate new PK values on save. This method is called when + saving instances with no primary key value set. If this method returns + something else than None, then the returned value is used when saving + the new instance. + """ + if self.default: + return self.get_default() + return None + + def to_python(self, value): + """ + Convert the input value into the expected Python data type, raising + django.core.exceptions.ValidationError if the data can't be converted. + Return the converted value. Subclasses should override this. + """ + return value + + @cached_property + def error_messages(self): + messages = {} + for c in reversed(self.__class__.__mro__): + messages.update(getattr(c, "default_error_messages", {})) + messages.update(self._error_messages or {}) + return messages + + @cached_property + def validators(self): + """ + Some validators can't be created at field initialization time. + This method provides a way to delay their creation until required. + """ + return [*self.default_validators, *self._validators] + + def run_validators(self, value): + if value in self.empty_values: + return + + errors = [] + for v in self.validators: + try: + v(value) + except exceptions.ValidationError as e: + if hasattr(e, "code") and e.code in self.error_messages: + e.message = self.error_messages[e.code] + errors.extend(e.error_list) + + if errors: + raise exceptions.ValidationError(errors) + + def validate(self, value, model_instance): + """ + Validate value and raise ValidationError if necessary. Subclasses + should override this to provide validation logic. + """ + if not self.editable: + # Skip validation for non-editable fields. + return + + if self.choices is not None and value not in self.empty_values: + for option_key, option_value in self.choices: + if isinstance(option_value, (list, tuple)): + # This is an optgroup, so look inside the group for + # options. + for optgroup_key, optgroup_value in option_value: + if value == optgroup_key: + return + elif value == option_key: + return + raise exceptions.ValidationError( + self.error_messages["invalid_choice"], + code="invalid_choice", + params={"value": value}, + ) + + if value is None and not self.null: + raise exceptions.ValidationError(self.error_messages["null"], code="null") + + if not self.blank and value in self.empty_values: + raise exceptions.ValidationError(self.error_messages["blank"], code="blank") + + def clean(self, value, model_instance): + """ + Convert the value's type and run validation. Validation errors + from to_python() and validate() are propagated. Return the correct + value if no error is raised. + """ + value = self.to_python(value) + self.validate(value, model_instance) + self.run_validators(value) + return value + + def db_type_parameters(self, connection): + return DictWrapper(self.__dict__, connection.ops.quote_name, "qn_") + + def db_check(self, connection): + """ + Return the database column check constraint for this field, for the + provided connection. Works the same way as db_type() for the case that + get_internal_type() does not map to a preexisting model field. + """ + data = self.db_type_parameters(connection) + try: + return ( + connection.data_type_check_constraints[self.get_internal_type()] % data + ) + except KeyError: + return None + + def db_type(self, connection): + """ + Return the database column data type for this field, for the provided + connection. + """ + # The default implementation of this method looks at the + # backend-specific data_types dictionary, looking up the field by its + # "internal type". + # + # A Field class can implement the get_internal_type() method to specify + # which *preexisting* Django Field class it's most similar to -- i.e., + # a custom field might be represented by a TEXT column type, which is + # the same as the TextField Django field type, which means the custom + # field's get_internal_type() returns 'TextField'. + # + # But the limitation of the get_internal_type() / data_types approach + # is that it cannot handle database column types that aren't already + # mapped to one of the built-in Django field types. In this case, you + # can implement db_type() instead of get_internal_type() to specify + # exactly which wacky database column type you want to use. + data = self.db_type_parameters(connection) + try: + column_type = connection.data_types[self.get_internal_type()] + except KeyError: + return None + else: + # column_type is either a single-parameter function or a string. + if callable(column_type): + return column_type(data) + return column_type % data + + def rel_db_type(self, connection): + """ + Return the data type that a related field pointing to this field should + use. For example, this method is called by ForeignKey and OneToOneField + to determine its data type. + """ + return self.db_type(connection) + + def cast_db_type(self, connection): + """Return the data type to use in the Cast() function.""" + db_type = connection.ops.cast_data_types.get(self.get_internal_type()) + if db_type: + return db_type % self.db_type_parameters(connection) + return self.db_type(connection) + + def db_parameters(self, connection): + """ + Extension of db_type(), providing a range of different return values + (type, checks). This will look at db_type(), allowing custom model + fields to override it. + """ + type_string = self.db_type(connection) + check_string = self.db_check(connection) + return { + "type": type_string, + "check": check_string, + } + + def db_type_suffix(self, connection): + return connection.data_types_suffix.get(self.get_internal_type()) + + def get_db_converters(self, connection): + if hasattr(self, "from_db_value"): + return [self.from_db_value] + return [] + + @property + def unique(self): + return self._unique or self.primary_key + + @property + def db_tablespace(self): + return self._db_tablespace or settings.DEFAULT_INDEX_TABLESPACE + + @property + def db_returning(self): + """ + Private API intended only to be used by Django itself. Currently only + the PostgreSQL backend supports returning multiple fields on a model. + """ + return False + + def set_attributes_from_name(self, name): + self.name = self.name or name + self.attname, self.column = self.get_attname_column() + self.concrete = self.column is not None + if self.verbose_name is None and self.name: + self.verbose_name = self.name.replace("_", " ") + + def contribute_to_class(self, cls, name, private_only=False): + """ + Register the field with the model class it belongs to. + + If private_only is True, create a separate instance of this field + for every subclass of cls, even if cls is not an abstract model. + """ + self.set_attributes_from_name(name) + self.model = cls + cls._meta.add_field(self, private=private_only) + if self.column: + setattr(cls, self.attname, self.descriptor_class(self)) + if self.choices is not None: + # Don't override a get_FOO_display() method defined explicitly on + # this class, but don't check methods derived from inheritance, to + # allow overriding inherited choices. For more complex inheritance + # structures users should override contribute_to_class(). + if "get_%s_display" % self.name not in cls.__dict__: + setattr( + cls, + "get_%s_display" % self.name, + partialmethod(cls._get_FIELD_display, field=self), + ) + + def get_filter_kwargs_for_object(self, obj): + """ + Return a dict that when passed as kwargs to self.model.filter(), would + yield all instances having the same value for this field as obj has. + """ + return {self.name: getattr(obj, self.attname)} + + def get_attname(self): + return self.name + + def get_attname_column(self): + attname = self.get_attname() + column = self.db_column or attname + return attname, column + + def get_internal_type(self): + return self.__class__.__name__ + + def pre_save(self, model_instance, add): + """Return field's value just before saving.""" + return getattr(model_instance, self.attname) + + def get_prep_value(self, value): + """Perform preliminary non-db specific value checks and conversions.""" + if isinstance(value, Promise): + value = value._proxy____cast() + return value + + def get_db_prep_value(self, value, connection, prepared=False): + """ + Return field's value prepared for interacting with the database backend. + + Used by the default implementations of get_db_prep_save(). + """ + if not prepared: + value = self.get_prep_value(value) + return value + + def get_db_prep_save(self, value, connection): + """Return field's value prepared for saving into a database.""" + if hasattr(value, "as_sql"): + return value + return self.get_db_prep_value(value, connection=connection, prepared=False) + + def has_default(self): + """Return a boolean of whether this field has a default value.""" + return self.default is not NOT_PROVIDED + + def get_default(self): + """Return the default value for this field.""" + return self._get_default() + + @cached_property + def _get_default(self): + if self.has_default(): + if callable(self.default): + return self.default + return lambda: self.default + + if ( + not self.empty_strings_allowed + or self.null + and not connection.features.interprets_empty_strings_as_nulls + ): + return return_None + return str # return empty string + + def get_choices( + self, + include_blank=True, + blank_choice=BLANK_CHOICE_DASH, + limit_choices_to=None, + ordering=(), + ): + """ + Return choices with a default blank choices included, for use + as <select> choices for this field. + """ + if self.choices is not None: + choices = list(self.choices) + if include_blank: + blank_defined = any( + choice in ("", None) for choice, _ in self.flatchoices + ) + if not blank_defined: + choices = blank_choice + choices + return choices + rel_model = self.remote_field.model + limit_choices_to = limit_choices_to or self.get_limit_choices_to() + choice_func = operator.attrgetter( + self.remote_field.get_related_field().attname + if hasattr(self.remote_field, "get_related_field") + else "pk" + ) + qs = rel_model._default_manager.complex_filter(limit_choices_to) + if ordering: + qs = qs.order_by(*ordering) + return (blank_choice if include_blank else []) + [ + (choice_func(x), str(x)) for x in qs + ] + + def value_to_string(self, obj): + """ + Return a string value of this field from the passed obj. + This is used by the serialization framework. + """ + return str(self.value_from_object(obj)) + + def _get_flatchoices(self): + """Flattened version of choices tuple.""" + if self.choices is None: + return [] + flat = [] + for choice, value in self.choices: + if isinstance(value, (list, tuple)): + flat.extend(value) + else: + flat.append((choice, value)) + return flat + + flatchoices = property(_get_flatchoices) + + def save_form_data(self, instance, data): + setattr(instance, self.name, data) + + def formfield(self, form_class=None, choices_form_class=None, **kwargs): + """Return a django.forms.Field instance for this field.""" + defaults = { + "required": not self.blank, + "label": capfirst(self.verbose_name), + "help_text": self.help_text, + } + if self.has_default(): + if callable(self.default): + defaults["initial"] = self.default + defaults["show_hidden_initial"] = True + else: + defaults["initial"] = self.get_default() + if self.choices is not None: + # Fields with choices get special treatment. + include_blank = self.blank or not ( + self.has_default() or "initial" in kwargs + ) + defaults["choices"] = self.get_choices(include_blank=include_blank) + defaults["coerce"] = self.to_python + if self.null: + defaults["empty_value"] = None + if choices_form_class is not None: + form_class = choices_form_class + else: + form_class = forms.TypedChoiceField + # Many of the subclass-specific formfield arguments (min_value, + # max_value) don't apply for choice fields, so be sure to only pass + # the values that TypedChoiceField will understand. + for k in list(kwargs): + if k not in ( + "coerce", + "empty_value", + "choices", + "required", + "widget", + "label", + "initial", + "help_text", + "error_messages", + "show_hidden_initial", + "disabled", + ): + del kwargs[k] + defaults.update(kwargs) + if form_class is None: + form_class = forms.CharField + return form_class(**defaults) + + def value_from_object(self, obj): + """Return the value of this field in the given model instance.""" + return getattr(obj, self.attname) + + +class BooleanField(Field): + empty_strings_allowed = False + default_error_messages = { + "invalid": _("“%(value)s” value must be either True or False."), + "invalid_nullable": _("“%(value)s” value must be either True, False, or None."), + } + description = _("Boolean (Either True or False)") + + def get_internal_type(self): + return "BooleanField" + + def to_python(self, value): + if self.null and value in self.empty_values: + return None + if value in (True, False): + # 1/0 are equal to True/False. bool() converts former to latter. + return bool(value) + if value in ("t", "True", "1"): + return True + if value in ("f", "False", "0"): + return False + raise exceptions.ValidationError( + self.error_messages["invalid_nullable" if self.null else "invalid"], + code="invalid", + params={"value": value}, + ) + + def get_prep_value(self, value): + value = super().get_prep_value(value) + if value is None: + return None + return self.to_python(value) + + def formfield(self, **kwargs): + if self.choices is not None: + include_blank = not (self.has_default() or "initial" in kwargs) + defaults = {"choices": self.get_choices(include_blank=include_blank)} + else: + form_class = forms.NullBooleanField if self.null else forms.BooleanField + # In HTML checkboxes, 'required' means "must be checked" which is + # different from the choices case ("must select some value"). + # required=False allows unchecked checkboxes. + defaults = {"form_class": form_class, "required": False} + return super().formfield(**{**defaults, **kwargs}) + + +class CharField(Field): + def __init__(self, *args, db_collation=None, **kwargs): + super().__init__(*args, **kwargs) + self.db_collation = db_collation + if self.max_length is not None: + self.validators.append(validators.MaxLengthValidator(self.max_length)) + + @property + def description(self): + if self.max_length is not None: + return _("String (up to %(max_length)s)") + else: + return _("String (unlimited)") + + def check(self, **kwargs): + databases = kwargs.get("databases") or [] + return [ + *super().check(**kwargs), + *self._check_db_collation(databases), + *self._check_max_length_attribute(**kwargs), + ] + + def _check_max_length_attribute(self, **kwargs): + if self.max_length is None: + if ( + connection.features.supports_unlimited_charfield + or "supports_unlimited_charfield" + in self.model._meta.required_db_features + ): + return [] + return [ + checks.Error( + "CharFields must define a 'max_length' attribute.", + obj=self, + id="fields.E120", + ) + ] + elif ( + not isinstance(self.max_length, int) + or isinstance(self.max_length, bool) + or self.max_length <= 0 + ): + return [ + checks.Error( + "'max_length' must be a positive integer.", + obj=self, + id="fields.E121", + ) + ] + else: + return [] + + def _check_db_collation(self, databases): + errors = [] + for db in databases: + if not router.allow_migrate_model(db, self.model): + continue + connection = connections[db] + if not ( + self.db_collation is None + or "supports_collation_on_charfield" + in self.model._meta.required_db_features + or connection.features.supports_collation_on_charfield + ): + errors.append( + checks.Error( + "%s does not support a database collation on " + "CharFields." % connection.display_name, + obj=self, + id="fields.E190", + ), + ) + return errors + + def cast_db_type(self, connection): + if self.max_length is None: + return connection.ops.cast_char_field_without_max_length + return super().cast_db_type(connection) + + def db_parameters(self, connection): + db_params = super().db_parameters(connection) + db_params["collation"] = self.db_collation + return db_params + + def get_internal_type(self): + return "CharField" + + def to_python(self, value): + if isinstance(value, str) or value is None: + return value + return str(value) + + def get_prep_value(self, value): + value = super().get_prep_value(value) + return self.to_python(value) + + def formfield(self, **kwargs): + # Passing max_length to forms.CharField means that the value's length + # will be validated twice. This is considered acceptable since we want + # the value in the form field (to pass into widget for example). + defaults = {"max_length": self.max_length} + # TODO: Handle multiple backends with different feature flags. + if self.null and not connection.features.interprets_empty_strings_as_nulls: + defaults["empty_value"] = None + defaults.update(kwargs) + return super().formfield(**defaults) + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + if self.db_collation: + kwargs["db_collation"] = self.db_collation + return name, path, args, kwargs + + +class CommaSeparatedIntegerField(CharField): + default_validators = [validators.validate_comma_separated_integer_list] + description = _("Comma-separated integers") + system_check_removed_details = { + "msg": ( + "CommaSeparatedIntegerField is removed except for support in " + "historical migrations." + ), + "hint": ( + "Use CharField(validators=[validate_comma_separated_integer_list]) " + "instead." + ), + "id": "fields.E901", + } + + +def _to_naive(value): + if timezone.is_aware(value): + value = timezone.make_naive(value, datetime.timezone.utc) + return value + + +def _get_naive_now(): + return _to_naive(timezone.now()) + + +class DateTimeCheckMixin: + def check(self, **kwargs): + return [ + *super().check(**kwargs), + *self._check_mutually_exclusive_options(), + *self._check_fix_default_value(), + ] + + def _check_mutually_exclusive_options(self): + # auto_now, auto_now_add, and default are mutually exclusive + # options. The use of more than one of these options together + # will trigger an Error + mutually_exclusive_options = [ + self.auto_now_add, + self.auto_now, + self.has_default(), + ] + enabled_options = [ + option not in (None, False) for option in mutually_exclusive_options + ].count(True) + if enabled_options > 1: + return [ + checks.Error( + "The options auto_now, auto_now_add, and default " + "are mutually exclusive. Only one of these options " + "may be present.", + obj=self, + id="fields.E160", + ) + ] + else: + return [] + + def _check_fix_default_value(self): + return [] + + # Concrete subclasses use this in their implementations of + # _check_fix_default_value(). + def _check_if_value_fixed(self, value, now=None): + """ + Check if the given value appears to have been provided as a "fixed" + time value, and include a warning in the returned list if it does. The + value argument must be a date object or aware/naive datetime object. If + now is provided, it must be a naive datetime object. + """ + if now is None: + now = _get_naive_now() + offset = datetime.timedelta(seconds=10) + lower = now - offset + upper = now + offset + if isinstance(value, datetime.datetime): + value = _to_naive(value) + else: + assert isinstance(value, datetime.date) + lower = lower.date() + upper = upper.date() + if lower <= value <= upper: + return [ + checks.Warning( + "Fixed default value provided.", + hint=( + "It seems you set a fixed date / time / datetime " + "value as default for this field. This may not be " + "what you want. If you want to have the current date " + "as default, use `django.utils.timezone.now`" + ), + obj=self, + id="fields.W161", + ) + ] + return [] + + +class DateField(DateTimeCheckMixin, Field): + empty_strings_allowed = False + default_error_messages = { + "invalid": _( + "“%(value)s” value has an invalid date format. It must be " + "in YYYY-MM-DD format." + ), + "invalid_date": _( + "“%(value)s” value has the correct format (YYYY-MM-DD) " + "but it is an invalid date." + ), + } + description = _("Date (without time)") + + def __init__( + self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs + ): + self.auto_now, self.auto_now_add = auto_now, auto_now_add + if auto_now or auto_now_add: + kwargs["editable"] = False + kwargs["blank"] = True + super().__init__(verbose_name, name, **kwargs) + + def _check_fix_default_value(self): + """ + Warn that using an actual date or datetime value is probably wrong; + it's only evaluated on server startup. + """ + if not self.has_default(): + return [] + + value = self.default + if isinstance(value, datetime.datetime): + value = _to_naive(value).date() + elif isinstance(value, datetime.date): + pass + else: + # No explicit date / datetime value -- no checks necessary + return [] + # At this point, value is a date object. + return self._check_if_value_fixed(value) + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + if self.auto_now: + kwargs["auto_now"] = True + if self.auto_now_add: + kwargs["auto_now_add"] = True + if self.auto_now or self.auto_now_add: + del kwargs["editable"] + del kwargs["blank"] + return name, path, args, kwargs + + def get_internal_type(self): + return "DateField" + + def to_python(self, value): + if value is None: + return value + if isinstance(value, datetime.datetime): + if settings.USE_TZ and timezone.is_aware(value): + # Convert aware datetimes to the default time zone + # before casting them to dates (#17742). + default_timezone = timezone.get_default_timezone() + value = timezone.make_naive(value, default_timezone) + return value.date() + if isinstance(value, datetime.date): + return value + + try: + parsed = parse_date(value) + if parsed is not None: + return parsed + except ValueError: + raise exceptions.ValidationError( + self.error_messages["invalid_date"], + code="invalid_date", + params={"value": value}, + ) + + raise exceptions.ValidationError( + self.error_messages["invalid"], + code="invalid", + params={"value": value}, + ) + + def pre_save(self, model_instance, add): + if self.auto_now or (self.auto_now_add and add): + value = datetime.date.today() + setattr(model_instance, self.attname, value) + return value + else: + return super().pre_save(model_instance, add) + + def contribute_to_class(self, cls, name, **kwargs): + super().contribute_to_class(cls, name, **kwargs) + if not self.null: + setattr( + cls, + "get_next_by_%s" % self.name, + partialmethod( + cls._get_next_or_previous_by_FIELD, field=self, is_next=True + ), + ) + setattr( + cls, + "get_previous_by_%s" % self.name, + partialmethod( + cls._get_next_or_previous_by_FIELD, field=self, is_next=False + ), + ) + + def get_prep_value(self, value): + value = super().get_prep_value(value) + return self.to_python(value) + + def get_db_prep_value(self, value, connection, prepared=False): + # Casts dates into the format expected by the backend + if not prepared: + value = self.get_prep_value(value) + return connection.ops.adapt_datefield_value(value) + + def value_to_string(self, obj): + val = self.value_from_object(obj) + return "" if val is None else val.isoformat() + + def formfield(self, **kwargs): + return super().formfield( + **{ + "form_class": forms.DateField, + **kwargs, + } + ) + + +class DateTimeField(DateField): + empty_strings_allowed = False + default_error_messages = { + "invalid": _( + "“%(value)s” value has an invalid format. It must be in " + "YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format." + ), + "invalid_date": _( + "“%(value)s” value has the correct format " + "(YYYY-MM-DD) but it is an invalid date." + ), + "invalid_datetime": _( + "“%(value)s” value has the correct format " + "(YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) " + "but it is an invalid date/time." + ), + } + description = _("Date (with time)") + + # __init__ is inherited from DateField + + def _check_fix_default_value(self): + """ + Warn that using an actual date or datetime value is probably wrong; + it's only evaluated on server startup. + """ + if not self.has_default(): + return [] + + value = self.default + if isinstance(value, (datetime.datetime, datetime.date)): + return self._check_if_value_fixed(value) + # No explicit date / datetime value -- no checks necessary. + return [] + + def get_internal_type(self): + return "DateTimeField" + + def to_python(self, value): + if value is None: + return value + if isinstance(value, datetime.datetime): + return value + if isinstance(value, datetime.date): + value = datetime.datetime(value.year, value.month, value.day) + if settings.USE_TZ: + # For backwards compatibility, interpret naive datetimes in + # local time. This won't work during DST change, but we can't + # do much about it, so we let the exceptions percolate up the + # call stack. + warnings.warn( + "DateTimeField %s.%s received a naive datetime " + "(%s) while time zone support is active." + % (self.model.__name__, self.name, value), + RuntimeWarning, + ) + default_timezone = timezone.get_default_timezone() + value = timezone.make_aware(value, default_timezone) + return value + + try: + parsed = parse_datetime(value) + if parsed is not None: + return parsed + except ValueError: + raise exceptions.ValidationError( + self.error_messages["invalid_datetime"], + code="invalid_datetime", + params={"value": value}, + ) + + try: + parsed = parse_date(value) + if parsed is not None: + return datetime.datetime(parsed.year, parsed.month, parsed.day) + except ValueError: + raise exceptions.ValidationError( + self.error_messages["invalid_date"], + code="invalid_date", + params={"value": value}, + ) + + raise exceptions.ValidationError( + self.error_messages["invalid"], + code="invalid", + params={"value": value}, + ) + + def pre_save(self, model_instance, add): + if self.auto_now or (self.auto_now_add and add): + value = timezone.now() + setattr(model_instance, self.attname, value) + return value + else: + return super().pre_save(model_instance, add) + + # contribute_to_class is inherited from DateField, it registers + # get_next_by_FOO and get_prev_by_FOO + + def get_prep_value(self, value): + value = super().get_prep_value(value) + value = self.to_python(value) + if value is not None and settings.USE_TZ and timezone.is_naive(value): + # For backwards compatibility, interpret naive datetimes in local + # time. This won't work during DST change, but we can't do much + # about it, so we let the exceptions percolate up the call stack. + try: + name = "%s.%s" % (self.model.__name__, self.name) + except AttributeError: + name = "(unbound)" + warnings.warn( + "DateTimeField %s received a naive datetime (%s)" + " while time zone support is active." % (name, value), + RuntimeWarning, + ) + default_timezone = timezone.get_default_timezone() + value = timezone.make_aware(value, default_timezone) + return value + + def get_db_prep_value(self, value, connection, prepared=False): + # Casts datetimes into the format expected by the backend + if not prepared: + value = self.get_prep_value(value) + return connection.ops.adapt_datetimefield_value(value) + + def value_to_string(self, obj): + val = self.value_from_object(obj) + return "" if val is None else val.isoformat() + + def formfield(self, **kwargs): + return super().formfield( + **{ + "form_class": forms.DateTimeField, + **kwargs, + } + ) + + +class DecimalField(Field): + empty_strings_allowed = False + default_error_messages = { + "invalid": _("“%(value)s” value must be a decimal number."), + } + description = _("Decimal number") + + def __init__( + self, + verbose_name=None, + name=None, + max_digits=None, + decimal_places=None, + **kwargs, + ): + self.max_digits, self.decimal_places = max_digits, decimal_places + super().__init__(verbose_name, name, **kwargs) + + def check(self, **kwargs): + errors = super().check(**kwargs) + + digits_errors = [ + *self._check_decimal_places(), + *self._check_max_digits(), + ] + if not digits_errors: + errors.extend(self._check_decimal_places_and_max_digits(**kwargs)) + else: + errors.extend(digits_errors) + return errors + + def _check_decimal_places(self): + try: + decimal_places = int(self.decimal_places) + if decimal_places < 0: + raise ValueError() + except TypeError: + return [ + checks.Error( + "DecimalFields must define a 'decimal_places' attribute.", + obj=self, + id="fields.E130", + ) + ] + except ValueError: + return [ + checks.Error( + "'decimal_places' must be a non-negative integer.", + obj=self, + id="fields.E131", + ) + ] + else: + return [] + + def _check_max_digits(self): + try: + max_digits = int(self.max_digits) + if max_digits <= 0: + raise ValueError() + except TypeError: + return [ + checks.Error( + "DecimalFields must define a 'max_digits' attribute.", + obj=self, + id="fields.E132", + ) + ] + except ValueError: + return [ + checks.Error( + "'max_digits' must be a positive integer.", + obj=self, + id="fields.E133", + ) + ] + else: + return [] + + def _check_decimal_places_and_max_digits(self, **kwargs): + if int(self.decimal_places) > int(self.max_digits): + return [ + checks.Error( + "'max_digits' must be greater or equal to 'decimal_places'.", + obj=self, + id="fields.E134", + ) + ] + return [] + + @cached_property + def validators(self): + return super().validators + [ + validators.DecimalValidator(self.max_digits, self.decimal_places) + ] + + @cached_property + def context(self): + return decimal.Context(prec=self.max_digits) + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + if self.max_digits is not None: + kwargs["max_digits"] = self.max_digits + if self.decimal_places is not None: + kwargs["decimal_places"] = self.decimal_places + return name, path, args, kwargs + + def get_internal_type(self): + return "DecimalField" + + def to_python(self, value): + if value is None: + return value + try: + if isinstance(value, float): + decimal_value = self.context.create_decimal_from_float(value) + else: + decimal_value = decimal.Decimal(value) + except (decimal.InvalidOperation, TypeError, ValueError): + raise exceptions.ValidationError( + self.error_messages["invalid"], + code="invalid", + params={"value": value}, + ) + if not decimal_value.is_finite(): + raise exceptions.ValidationError( + self.error_messages["invalid"], + code="invalid", + params={"value": value}, + ) + return decimal_value + + def get_db_prep_save(self, value, connection): + if hasattr(value, "as_sql"): + return value + return connection.ops.adapt_decimalfield_value( + self.to_python(value), self.max_digits, self.decimal_places + ) + + def get_prep_value(self, value): + value = super().get_prep_value(value) + return self.to_python(value) + + def formfield(self, **kwargs): + return super().formfield( + **{ + "max_digits": self.max_digits, + "decimal_places": self.decimal_places, + "form_class": forms.DecimalField, + **kwargs, + } + ) + + +class DurationField(Field): + """ + Store timedelta objects. + + Use interval on PostgreSQL, INTERVAL DAY TO SECOND on Oracle, and bigint + of microseconds on other databases. + """ + + empty_strings_allowed = False + default_error_messages = { + "invalid": _( + "“%(value)s” value has an invalid format. It must be in " + "[DD] [[HH:]MM:]ss[.uuuuuu] format." + ) + } + description = _("Duration") + + def get_internal_type(self): + return "DurationField" + + def to_python(self, value): + if value is None: + return value + if isinstance(value, datetime.timedelta): + return value + try: + parsed = parse_duration(value) + except ValueError: + pass + else: + if parsed is not None: + return parsed + + raise exceptions.ValidationError( + self.error_messages["invalid"], + code="invalid", + params={"value": value}, + ) + + def get_db_prep_value(self, value, connection, prepared=False): + if connection.features.has_native_duration_field: + return value + if value is None: + return None + return duration_microseconds(value) + + def get_db_converters(self, connection): + converters = [] + if not connection.features.has_native_duration_field: + converters.append(connection.ops.convert_durationfield_value) + return converters + super().get_db_converters(connection) + + def value_to_string(self, obj): + val = self.value_from_object(obj) + return "" if val is None else duration_string(val) + + def formfield(self, **kwargs): + return super().formfield( + **{ + "form_class": forms.DurationField, + **kwargs, + } + ) + + +class EmailField(CharField): + default_validators = [validators.validate_email] + description = _("Email address") + + def __init__(self, *args, **kwargs): + # max_length=254 to be compliant with RFCs 3696 and 5321 + kwargs.setdefault("max_length", 254) + super().__init__(*args, **kwargs) + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + # We do not exclude max_length if it matches default as we want to change + # the default in future. + return name, path, args, kwargs + + def formfield(self, **kwargs): + # As with CharField, this will cause email validation to be performed + # twice. + return super().formfield( + **{ + "form_class": forms.EmailField, + **kwargs, + } + ) + + +class FilePathField(Field): + description = _("File path") + + def __init__( + self, + verbose_name=None, + name=None, + path="", + match=None, + recursive=False, + allow_files=True, + allow_folders=False, + **kwargs, + ): + self.path, self.match, self.recursive = path, match, recursive + self.allow_files, self.allow_folders = allow_files, allow_folders + kwargs.setdefault("max_length", 100) + super().__init__(verbose_name, name, **kwargs) + + def check(self, **kwargs): + return [ + *super().check(**kwargs), + *self._check_allowing_files_or_folders(**kwargs), + ] + + def _check_allowing_files_or_folders(self, **kwargs): + if not self.allow_files and not self.allow_folders: + return [ + checks.Error( + "FilePathFields must have either 'allow_files' or 'allow_folders' " + "set to True.", + obj=self, + id="fields.E140", + ) + ] + return [] + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + if self.path != "": + kwargs["path"] = self.path + if self.match is not None: + kwargs["match"] = self.match + if self.recursive is not False: + kwargs["recursive"] = self.recursive + if self.allow_files is not True: + kwargs["allow_files"] = self.allow_files + if self.allow_folders is not False: + kwargs["allow_folders"] = self.allow_folders + if kwargs.get("max_length") == 100: + del kwargs["max_length"] + return name, path, args, kwargs + + def get_prep_value(self, value): + value = super().get_prep_value(value) + if value is None: + return None + return str(value) + + def formfield(self, **kwargs): + return super().formfield( + **{ + "path": self.path() if callable(self.path) else self.path, + "match": self.match, + "recursive": self.recursive, + "form_class": forms.FilePathField, + "allow_files": self.allow_files, + "allow_folders": self.allow_folders, + **kwargs, + } + ) + + def get_internal_type(self): + return "FilePathField" + + +class FloatField(Field): + empty_strings_allowed = False + default_error_messages = { + "invalid": _("“%(value)s” value must be a float."), + } + description = _("Floating point number") + + def get_prep_value(self, value): + value = super().get_prep_value(value) + if value is None: + return None + try: + return float(value) + except (TypeError, ValueError) as e: + raise e.__class__( + "Field '%s' expected a number but got %r." % (self.name, value), + ) from e + + def get_internal_type(self): + return "FloatField" + + def to_python(self, value): + if value is None: + return value + try: + return float(value) + except (TypeError, ValueError): + raise exceptions.ValidationError( + self.error_messages["invalid"], + code="invalid", + params={"value": value}, + ) + + def formfield(self, **kwargs): + return super().formfield( + **{ + "form_class": forms.FloatField, + **kwargs, + } + ) + + +class IntegerField(Field): + empty_strings_allowed = False + default_error_messages = { + "invalid": _("“%(value)s” value must be an integer."), + } + description = _("Integer") + + def check(self, **kwargs): + return [ + *super().check(**kwargs), + *self._check_max_length_warning(), + ] + + def _check_max_length_warning(self): + if self.max_length is not None: + return [ + checks.Warning( + "'max_length' is ignored when used with %s." + % self.__class__.__name__, + hint="Remove 'max_length' from field", + obj=self, + id="fields.W122", + ) + ] + return [] + + @cached_property + def validators(self): + # These validators can't be added at field initialization time since + # they're based on values retrieved from `connection`. + validators_ = super().validators + internal_type = self.get_internal_type() + min_value, max_value = connection.ops.integer_field_range(internal_type) + if min_value is not None and not any( + ( + isinstance(validator, validators.MinValueValidator) + and ( + validator.limit_value() + if callable(validator.limit_value) + else validator.limit_value + ) + >= min_value + ) + for validator in validators_ + ): + validators_.append(validators.MinValueValidator(min_value)) + if max_value is not None and not any( + ( + isinstance(validator, validators.MaxValueValidator) + and ( + validator.limit_value() + if callable(validator.limit_value) + else validator.limit_value + ) + <= max_value + ) + for validator in validators_ + ): + validators_.append(validators.MaxValueValidator(max_value)) + return validators_ + + def get_prep_value(self, value): + value = super().get_prep_value(value) + if value is None: + return None + try: + return int(value) + except (TypeError, ValueError) as e: + raise e.__class__( + "Field '%s' expected a number but got %r." % (self.name, value), + ) from e + + def get_db_prep_value(self, value, connection, prepared=False): + value = super().get_db_prep_value(value, connection, prepared) + return connection.ops.adapt_integerfield_value(value, self.get_internal_type()) + + def get_internal_type(self): + return "IntegerField" + + def to_python(self, value): + if value is None: + return value + try: + return int(value) + except (TypeError, ValueError): + raise exceptions.ValidationError( + self.error_messages["invalid"], + code="invalid", + params={"value": value}, + ) + + def formfield(self, **kwargs): + return super().formfield( + **{ + "form_class": forms.IntegerField, + **kwargs, + } + ) + + +class BigIntegerField(IntegerField): + description = _("Big (8 byte) integer") + MAX_BIGINT = 9223372036854775807 + + def get_internal_type(self): + return "BigIntegerField" + + def formfield(self, **kwargs): + return super().formfield( + **{ + "min_value": -BigIntegerField.MAX_BIGINT - 1, + "max_value": BigIntegerField.MAX_BIGINT, + **kwargs, + } + ) + + +class SmallIntegerField(IntegerField): + description = _("Small integer") + + def get_internal_type(self): + return "SmallIntegerField" + + +class IPAddressField(Field): + empty_strings_allowed = False + description = _("IPv4 address") + system_check_removed_details = { + "msg": ( + "IPAddressField has been removed except for support in " + "historical migrations." + ), + "hint": "Use GenericIPAddressField instead.", + "id": "fields.E900", + } + + def __init__(self, *args, **kwargs): + kwargs["max_length"] = 15 + super().__init__(*args, **kwargs) + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + del kwargs["max_length"] + return name, path, args, kwargs + + def get_prep_value(self, value): + value = super().get_prep_value(value) + if value is None: + return None + return str(value) + + def get_internal_type(self): + return "IPAddressField" + + +class GenericIPAddressField(Field): + empty_strings_allowed = False + description = _("IP address") + default_error_messages = {} + + def __init__( + self, + verbose_name=None, + name=None, + protocol="both", + unpack_ipv4=False, + *args, + **kwargs, + ): + self.unpack_ipv4 = unpack_ipv4 + self.protocol = protocol + ( + self.default_validators, + invalid_error_message, + ) = validators.ip_address_validators(protocol, unpack_ipv4) + self.default_error_messages["invalid"] = invalid_error_message + kwargs["max_length"] = 39 + super().__init__(verbose_name, name, *args, **kwargs) + + def check(self, **kwargs): + return [ + *super().check(**kwargs), + *self._check_blank_and_null_values(**kwargs), + ] + + def _check_blank_and_null_values(self, **kwargs): + if not getattr(self, "null", False) and getattr(self, "blank", False): + return [ + checks.Error( + "GenericIPAddressFields cannot have blank=True if null=False, " + "as blank values are stored as nulls.", + obj=self, + id="fields.E150", + ) + ] + return [] + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + if self.unpack_ipv4 is not False: + kwargs["unpack_ipv4"] = self.unpack_ipv4 + if self.protocol != "both": + kwargs["protocol"] = self.protocol + if kwargs.get("max_length") == 39: + del kwargs["max_length"] + return name, path, args, kwargs + + def get_internal_type(self): + return "GenericIPAddressField" + + def to_python(self, value): + if value is None: + return None + if not isinstance(value, str): + value = str(value) + value = value.strip() + if ":" in value: + return clean_ipv6_address( + value, self.unpack_ipv4, self.error_messages["invalid"] + ) + return value + + def get_db_prep_value(self, value, connection, prepared=False): + if not prepared: + value = self.get_prep_value(value) + return connection.ops.adapt_ipaddressfield_value(value) + + def get_prep_value(self, value): + value = super().get_prep_value(value) + if value is None: + return None + if value and ":" in value: + try: + return clean_ipv6_address(value, self.unpack_ipv4) + except exceptions.ValidationError: + pass + return str(value) + + def formfield(self, **kwargs): + return super().formfield( + **{ + "protocol": self.protocol, + "form_class": forms.GenericIPAddressField, + **kwargs, + } + ) + + +class NullBooleanField(BooleanField): + default_error_messages = { + "invalid": _("“%(value)s” value must be either None, True or False."), + "invalid_nullable": _("“%(value)s” value must be either None, True or False."), + } + description = _("Boolean (Either True, False or None)") + system_check_removed_details = { + "msg": ( + "NullBooleanField is removed except for support in historical " + "migrations." + ), + "hint": "Use BooleanField(null=True) instead.", + "id": "fields.E903", + } + + def __init__(self, *args, **kwargs): + kwargs["null"] = True + kwargs["blank"] = True + super().__init__(*args, **kwargs) + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + del kwargs["null"] + del kwargs["blank"] + return name, path, args, kwargs + + +class PositiveIntegerRelDbTypeMixin: + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + if not hasattr(cls, "integer_field_class"): + cls.integer_field_class = next( + ( + parent + for parent in cls.__mro__[1:] + if issubclass(parent, IntegerField) + ), + None, + ) + + def rel_db_type(self, connection): + """ + Return the data type that a related field pointing to this field should + use. In most cases, a foreign key pointing to a positive integer + primary key will have an integer column data type but some databases + (e.g. MySQL) have an unsigned integer type. In that case + (related_fields_match_type=True), the primary key should return its + db_type. + """ + if connection.features.related_fields_match_type: + return self.db_type(connection) + else: + return self.integer_field_class().db_type(connection=connection) + + +class PositiveBigIntegerField(PositiveIntegerRelDbTypeMixin, BigIntegerField): + description = _("Positive big integer") + + def get_internal_type(self): + return "PositiveBigIntegerField" + + def formfield(self, **kwargs): + return super().formfield( + **{ + "min_value": 0, + **kwargs, + } + ) + + +class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField): + description = _("Positive integer") + + def get_internal_type(self): + return "PositiveIntegerField" + + def formfield(self, **kwargs): + return super().formfield( + **{ + "min_value": 0, + **kwargs, + } + ) + + +class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, SmallIntegerField): + description = _("Positive small integer") + + def get_internal_type(self): + return "PositiveSmallIntegerField" + + def formfield(self, **kwargs): + return super().formfield( + **{ + "min_value": 0, + **kwargs, + } + ) + + +class SlugField(CharField): + default_validators = [validators.validate_slug] + description = _("Slug (up to %(max_length)s)") + + def __init__( + self, *args, max_length=50, db_index=True, allow_unicode=False, **kwargs + ): + self.allow_unicode = allow_unicode + if self.allow_unicode: + self.default_validators = [validators.validate_unicode_slug] + super().__init__(*args, max_length=max_length, db_index=db_index, **kwargs) + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + if kwargs.get("max_length") == 50: + del kwargs["max_length"] + if self.db_index is False: + kwargs["db_index"] = False + else: + del kwargs["db_index"] + if self.allow_unicode is not False: + kwargs["allow_unicode"] = self.allow_unicode + return name, path, args, kwargs + + def get_internal_type(self): + return "SlugField" + + def formfield(self, **kwargs): + return super().formfield( + **{ + "form_class": forms.SlugField, + "allow_unicode": self.allow_unicode, + **kwargs, + } + ) + + +class TextField(Field): + description = _("Text") + + def __init__(self, *args, db_collation=None, **kwargs): + super().__init__(*args, **kwargs) + self.db_collation = db_collation + + def check(self, **kwargs): + databases = kwargs.get("databases") or [] + return [ + *super().check(**kwargs), + *self._check_db_collation(databases), + ] + + def _check_db_collation(self, databases): + errors = [] + for db in databases: + if not router.allow_migrate_model(db, self.model): + continue + connection = connections[db] + if not ( + self.db_collation is None + or "supports_collation_on_textfield" + in self.model._meta.required_db_features + or connection.features.supports_collation_on_textfield + ): + errors.append( + checks.Error( + "%s does not support a database collation on " + "TextFields." % connection.display_name, + obj=self, + id="fields.E190", + ), + ) + return errors + + def db_parameters(self, connection): + db_params = super().db_parameters(connection) + db_params["collation"] = self.db_collation + return db_params + + def get_internal_type(self): + return "TextField" + + def to_python(self, value): + if isinstance(value, str) or value is None: + return value + return str(value) + + def get_prep_value(self, value): + value = super().get_prep_value(value) + return self.to_python(value) + + def formfield(self, **kwargs): + # Passing max_length to forms.CharField means that the value's length + # will be validated twice. This is considered acceptable since we want + # the value in the form field (to pass into widget for example). + return super().formfield( + **{ + "max_length": self.max_length, + **({} if self.choices is not None else {"widget": forms.Textarea}), + **kwargs, + } + ) + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + if self.db_collation: + kwargs["db_collation"] = self.db_collation + return name, path, args, kwargs + + +class TimeField(DateTimeCheckMixin, Field): + empty_strings_allowed = False + default_error_messages = { + "invalid": _( + "“%(value)s” value has an invalid format. It must be in " + "HH:MM[:ss[.uuuuuu]] format." + ), + "invalid_time": _( + "“%(value)s” value has the correct format " + "(HH:MM[:ss[.uuuuuu]]) but it is an invalid time." + ), + } + description = _("Time") + + def __init__( + self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs + ): + self.auto_now, self.auto_now_add = auto_now, auto_now_add + if auto_now or auto_now_add: + kwargs["editable"] = False + kwargs["blank"] = True + super().__init__(verbose_name, name, **kwargs) + + def _check_fix_default_value(self): + """ + Warn that using an actual date or datetime value is probably wrong; + it's only evaluated on server startup. + """ + if not self.has_default(): + return [] + + value = self.default + if isinstance(value, datetime.datetime): + now = None + elif isinstance(value, datetime.time): + now = _get_naive_now() + # This will not use the right date in the race condition where now + # is just before the date change and value is just past 0:00. + value = datetime.datetime.combine(now.date(), value) + else: + # No explicit time / datetime value -- no checks necessary + return [] + # At this point, value is a datetime object. + return self._check_if_value_fixed(value, now=now) + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + if self.auto_now is not False: + kwargs["auto_now"] = self.auto_now + if self.auto_now_add is not False: + kwargs["auto_now_add"] = self.auto_now_add + if self.auto_now or self.auto_now_add: + del kwargs["blank"] + del kwargs["editable"] + return name, path, args, kwargs + + def get_internal_type(self): + return "TimeField" + + def to_python(self, value): + if value is None: + return None + if isinstance(value, datetime.time): + return value + if isinstance(value, datetime.datetime): + # Not usually a good idea to pass in a datetime here (it loses + # information), but this can be a side-effect of interacting with a + # database backend (e.g. Oracle), so we'll be accommodating. + return value.time() + + try: + parsed = parse_time(value) + if parsed is not None: + return parsed + except ValueError: + raise exceptions.ValidationError( + self.error_messages["invalid_time"], + code="invalid_time", + params={"value": value}, + ) + + raise exceptions.ValidationError( + self.error_messages["invalid"], + code="invalid", + params={"value": value}, + ) + + def pre_save(self, model_instance, add): + if self.auto_now or (self.auto_now_add and add): + value = datetime.datetime.now().time() + setattr(model_instance, self.attname, value) + return value + else: + return super().pre_save(model_instance, add) + + def get_prep_value(self, value): + value = super().get_prep_value(value) + return self.to_python(value) + + def get_db_prep_value(self, value, connection, prepared=False): + # Casts times into the format expected by the backend + if not prepared: + value = self.get_prep_value(value) + return connection.ops.adapt_timefield_value(value) + + def value_to_string(self, obj): + val = self.value_from_object(obj) + return "" if val is None else val.isoformat() + + def formfield(self, **kwargs): + return super().formfield( + **{ + "form_class": forms.TimeField, + **kwargs, + } + ) + + +class URLField(CharField): + default_validators = [validators.URLValidator()] + description = _("URL") + + def __init__(self, verbose_name=None, name=None, **kwargs): + kwargs.setdefault("max_length", 200) + super().__init__(verbose_name, name, **kwargs) + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + if kwargs.get("max_length") == 200: + del kwargs["max_length"] + return name, path, args, kwargs + + def formfield(self, **kwargs): + # As with CharField, this will cause URL validation to be performed + # twice. + return super().formfield( + **{ + "form_class": forms.URLField, + **kwargs, + } + ) + + +class BinaryField(Field): + description = _("Raw binary data") + empty_values = [None, b""] + + def __init__(self, *args, **kwargs): + kwargs.setdefault("editable", False) + super().__init__(*args, **kwargs) + if self.max_length is not None: + self.validators.append(validators.MaxLengthValidator(self.max_length)) + + def check(self, **kwargs): + return [*super().check(**kwargs), *self._check_str_default_value()] + + def _check_str_default_value(self): + if self.has_default() and isinstance(self.default, str): + return [ + checks.Error( + "BinaryField's default cannot be a string. Use bytes " + "content instead.", + obj=self, + id="fields.E170", + ) + ] + return [] + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + if self.editable: + kwargs["editable"] = True + else: + del kwargs["editable"] + return name, path, args, kwargs + + def get_internal_type(self): + return "BinaryField" + + def get_placeholder(self, value, compiler, connection): + return connection.ops.binary_placeholder_sql(value) + + def get_default(self): + if self.has_default() and not callable(self.default): + return self.default + default = super().get_default() + if default == "": + return b"" + return default + + def get_db_prep_value(self, value, connection, prepared=False): + value = super().get_db_prep_value(value, connection, prepared) + if value is not None: + return connection.Database.Binary(value) + return value + + def value_to_string(self, obj): + """Binary data is serialized as base64""" + return b64encode(self.value_from_object(obj)).decode("ascii") + + def to_python(self, value): + # If it's a string, it should be base64-encoded data + if isinstance(value, str): + return memoryview(b64decode(value.encode("ascii"))) + return value + + +class UUIDField(Field): + default_error_messages = { + "invalid": _("“%(value)s” is not a valid UUID."), + } + description = _("Universally unique identifier") + empty_strings_allowed = False + + def __init__(self, verbose_name=None, **kwargs): + kwargs["max_length"] = 32 + super().__init__(verbose_name, **kwargs) + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + del kwargs["max_length"] + return name, path, args, kwargs + + def get_internal_type(self): + return "UUIDField" + + def get_prep_value(self, value): + value = super().get_prep_value(value) + return self.to_python(value) + + def get_db_prep_value(self, value, connection, prepared=False): + if value is None: + return None + if not isinstance(value, uuid.UUID): + value = self.to_python(value) + + if connection.features.has_native_uuid_field: + return value + return value.hex + + def to_python(self, value): + if value is not None and not isinstance(value, uuid.UUID): + input_form = "int" if isinstance(value, int) else "hex" + try: + return uuid.UUID(**{input_form: value}) + except (AttributeError, ValueError): + raise exceptions.ValidationError( + self.error_messages["invalid"], + code="invalid", + params={"value": value}, + ) + return value + + def formfield(self, **kwargs): + return super().formfield( + **{ + "form_class": forms.UUIDField, + **kwargs, + } + ) + + +class AutoFieldMixin: + db_returning = True + + def __init__(self, *args, **kwargs): + kwargs["blank"] = True + super().__init__(*args, **kwargs) + + def check(self, **kwargs): + return [ + *super().check(**kwargs), + *self._check_primary_key(), + ] + + def _check_primary_key(self): + if not self.primary_key: + return [ + checks.Error( + "AutoFields must set primary_key=True.", + obj=self, + id="fields.E100", + ), + ] + else: + return [] + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + del kwargs["blank"] + kwargs["primary_key"] = True + return name, path, args, kwargs + + def validate(self, value, model_instance): + pass + + def get_db_prep_value(self, value, connection, prepared=False): + if not prepared: + value = self.get_prep_value(value) + value = connection.ops.validate_autopk_value(value) + return value + + def contribute_to_class(self, cls, name, **kwargs): + if cls._meta.auto_field: + raise ValueError( + "Model %s can't have more than one auto-generated field." + % cls._meta.label + ) + super().contribute_to_class(cls, name, **kwargs) + cls._meta.auto_field = self + + def formfield(self, **kwargs): + return None + + +class AutoFieldMeta(type): + """ + Metaclass to maintain backward inheritance compatibility for AutoField. + + It is intended that AutoFieldMixin become public API when it is possible to + create a non-integer automatically-generated field using column defaults + stored in the database. + + In many areas Django also relies on using isinstance() to check for an + automatically-generated field as a subclass of AutoField. A new flag needs + to be implemented on Field to be used instead. + + When these issues have been addressed, this metaclass could be used to + deprecate inheritance from AutoField and use of isinstance() with AutoField + for detecting automatically-generated fields. + """ + + @property + def _subclasses(self): + return (BigAutoField, SmallAutoField) + + def __instancecheck__(self, instance): + return isinstance(instance, self._subclasses) or super().__instancecheck__( + instance + ) + + def __subclasscheck__(self, subclass): + return issubclass(subclass, self._subclasses) or super().__subclasscheck__( + subclass + ) + + +class AutoField(AutoFieldMixin, IntegerField, metaclass=AutoFieldMeta): + def get_internal_type(self): + return "AutoField" + + def rel_db_type(self, connection): + return IntegerField().db_type(connection=connection) + + +class BigAutoField(AutoFieldMixin, BigIntegerField): + def get_internal_type(self): + return "BigAutoField" + + def rel_db_type(self, connection): + return BigIntegerField().db_type(connection=connection) + + +class SmallAutoField(AutoFieldMixin, SmallIntegerField): + def get_internal_type(self): + return "SmallAutoField" + + def rel_db_type(self, connection): + return SmallIntegerField().db_type(connection=connection) diff --git a/virt/lib/python3.9/site-packages/django/db/models/fields/files 3.py b/virt/lib/python3.9/site-packages/django/db/models/fields/files 3.py new file mode 100644 index 00000000..4e8a31ba --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/db/models/fields/files 3.py @@ -0,0 +1,510 @@ +import datetime +import posixpath + +from django import forms +from django.core import checks +from django.core.files.base import File +from django.core.files.images import ImageFile +from django.core.files.storage import Storage, default_storage +from django.core.files.utils import validate_file_name +from django.db.models import signals +from django.db.models.fields import Field +from django.db.models.query_utils import DeferredAttribute +from django.db.models.utils import AltersData +from django.utils.translation import gettext_lazy as _ + + +class FieldFile(File, AltersData): + def __init__(self, instance, field, name): + super().__init__(None, name) + self.instance = instance + self.field = field + self.storage = field.storage + self._committed = True + + def __eq__(self, other): + # Older code may be expecting FileField values to be simple strings. + # By overriding the == operator, it can remain backwards compatibility. + if hasattr(other, "name"): + return self.name == other.name + return self.name == other + + def __hash__(self): + return hash(self.name) + + # The standard File contains most of the necessary properties, but + # FieldFiles can be instantiated without a name, so that needs to + # be checked for here. + + def _require_file(self): + if not self: + raise ValueError( + "The '%s' attribute has no file associated with it." % self.field.name + ) + + def _get_file(self): + self._require_file() + if getattr(self, "_file", None) is None: + self._file = self.storage.open(self.name, "rb") + return self._file + + def _set_file(self, file): + self._file = file + + def _del_file(self): + del self._file + + file = property(_get_file, _set_file, _del_file) + + @property + def path(self): + self._require_file() + return self.storage.path(self.name) + + @property + def url(self): + self._require_file() + return self.storage.url(self.name) + + @property + def size(self): + self._require_file() + if not self._committed: + return self.file.size + return self.storage.size(self.name) + + def open(self, mode="rb"): + self._require_file() + if getattr(self, "_file", None) is None: + self.file = self.storage.open(self.name, mode) + else: + self.file.open(mode) + return self + + # open() doesn't alter the file's contents, but it does reset the pointer + open.alters_data = True + + # In addition to the standard File API, FieldFiles have extra methods + # to further manipulate the underlying file, as well as update the + # associated model instance. + + def save(self, name, content, save=True): + name = self.field.generate_filename(self.instance, name) + self.name = self.storage.save(name, content, max_length=self.field.max_length) + setattr(self.instance, self.field.attname, self.name) + self._committed = True + + # Save the object because it has changed, unless save is False + if save: + self.instance.save() + + save.alters_data = True + + def delete(self, save=True): + if not self: + return + # Only close the file if it's already open, which we know by the + # presence of self._file + if hasattr(self, "_file"): + self.close() + del self.file + + self.storage.delete(self.name) + + self.name = None + setattr(self.instance, self.field.attname, self.name) + self._committed = False + + if save: + self.instance.save() + + delete.alters_data = True + + @property + def closed(self): + file = getattr(self, "_file", None) + return file is None or file.closed + + def close(self): + file = getattr(self, "_file", None) + if file is not None: + file.close() + + def __getstate__(self): + # FieldFile needs access to its associated model field, an instance and + # the file's name. Everything else will be restored later, by + # FileDescriptor below. + return { + "name": self.name, + "closed": False, + "_committed": True, + "_file": None, + "instance": self.instance, + "field": self.field, + } + + def __setstate__(self, state): + self.__dict__.update(state) + self.storage = self.field.storage + + +class FileDescriptor(DeferredAttribute): + """ + The descriptor for the file attribute on the model instance. Return a + FieldFile when accessed so you can write code like:: + + >>> from myapp.models import MyModel + >>> instance = MyModel.objects.get(pk=1) + >>> instance.file.size + + Assign a file object on assignment so you can do:: + + >>> with open('/path/to/hello.world') as f: + ... instance.file = File(f) + """ + + def __get__(self, instance, cls=None): + if instance is None: + return self + + # This is slightly complicated, so worth an explanation. + # instance.file needs to ultimately return some instance of `File`, + # probably a subclass. Additionally, this returned object needs to have + # the FieldFile API so that users can easily do things like + # instance.file.path and have that delegated to the file storage engine. + # Easy enough if we're strict about assignment in __set__, but if you + # peek below you can see that we're not. So depending on the current + # value of the field we have to dynamically construct some sort of + # "thing" to return. + + # The instance dict contains whatever was originally assigned + # in __set__. + file = super().__get__(instance, cls) + + # If this value is a string (instance.file = "path/to/file") or None + # then we simply wrap it with the appropriate attribute class according + # to the file field. [This is FieldFile for FileFields and + # ImageFieldFile for ImageFields; it's also conceivable that user + # subclasses might also want to subclass the attribute class]. This + # object understands how to convert a path to a file, and also how to + # handle None. + if isinstance(file, str) or file is None: + attr = self.field.attr_class(instance, self.field, file) + instance.__dict__[self.field.attname] = attr + + # Other types of files may be assigned as well, but they need to have + # the FieldFile interface added to them. Thus, we wrap any other type of + # File inside a FieldFile (well, the field's attr_class, which is + # usually FieldFile). + elif isinstance(file, File) and not isinstance(file, FieldFile): + file_copy = self.field.attr_class(instance, self.field, file.name) + file_copy.file = file + file_copy._committed = False + instance.__dict__[self.field.attname] = file_copy + + # Finally, because of the (some would say boneheaded) way pickle works, + # the underlying FieldFile might not actually itself have an associated + # file. So we need to reset the details of the FieldFile in those cases. + elif isinstance(file, FieldFile) and not hasattr(file, "field"): + file.instance = instance + file.field = self.field + file.storage = self.field.storage + + # Make sure that the instance is correct. + elif isinstance(file, FieldFile) and instance is not file.instance: + file.instance = instance + + # That was fun, wasn't it? + return instance.__dict__[self.field.attname] + + def __set__(self, instance, value): + instance.__dict__[self.field.attname] = value + + +class FileField(Field): + # The class to wrap instance attributes in. Accessing the file object off + # the instance will always return an instance of attr_class. + attr_class = FieldFile + + # The descriptor to use for accessing the attribute off of the class. + descriptor_class = FileDescriptor + + description = _("File") + + def __init__( + self, verbose_name=None, name=None, upload_to="", storage=None, **kwargs + ): + self._primary_key_set_explicitly = "primary_key" in kwargs + + self.storage = storage or default_storage + if callable(self.storage): + # Hold a reference to the callable for deconstruct(). + self._storage_callable = self.storage + self.storage = self.storage() + if not isinstance(self.storage, Storage): + raise TypeError( + "%s.storage must be a subclass/instance of %s.%s" + % ( + self.__class__.__qualname__, + Storage.__module__, + Storage.__qualname__, + ) + ) + self.upload_to = upload_to + + kwargs.setdefault("max_length", 100) + super().__init__(verbose_name, name, **kwargs) + + def check(self, **kwargs): + return [ + *super().check(**kwargs), + *self._check_primary_key(), + *self._check_upload_to(), + ] + + def _check_primary_key(self): + if self._primary_key_set_explicitly: + return [ + checks.Error( + "'primary_key' is not a valid argument for a %s." + % self.__class__.__name__, + obj=self, + id="fields.E201", + ) + ] + else: + return [] + + def _check_upload_to(self): + if isinstance(self.upload_to, str) and self.upload_to.startswith("/"): + return [ + checks.Error( + "%s's 'upload_to' argument must be a relative path, not an " + "absolute path." % self.__class__.__name__, + obj=self, + id="fields.E202", + hint="Remove the leading slash.", + ) + ] + else: + return [] + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + if kwargs.get("max_length") == 100: + del kwargs["max_length"] + kwargs["upload_to"] = self.upload_to + storage = getattr(self, "_storage_callable", self.storage) + if storage is not default_storage: + kwargs["storage"] = storage + return name, path, args, kwargs + + def get_internal_type(self): + return "FileField" + + def get_prep_value(self, value): + value = super().get_prep_value(value) + # Need to convert File objects provided via a form to string for + # database insertion. + if value is None: + return None + return str(value) + + def pre_save(self, model_instance, add): + file = super().pre_save(model_instance, add) + if file and not file._committed: + # Commit the file to storage prior to saving the model + file.save(file.name, file.file, save=False) + return file + + def contribute_to_class(self, cls, name, **kwargs): + super().contribute_to_class(cls, name, **kwargs) + setattr(cls, self.attname, self.descriptor_class(self)) + + def generate_filename(self, instance, filename): + """ + Apply (if callable) or prepend (if a string) upload_to to the filename, + then delegate further processing of the name to the storage backend. + Until the storage layer, all file paths are expected to be Unix style + (with forward slashes). + """ + if callable(self.upload_to): + filename = self.upload_to(instance, filename) + else: + dirname = datetime.datetime.now().strftime(str(self.upload_to)) + filename = posixpath.join(dirname, filename) + filename = validate_file_name(filename, allow_relative_path=True) + return self.storage.generate_filename(filename) + + def save_form_data(self, instance, data): + # Important: None means "no change", other false value means "clear" + # This subtle distinction (rather than a more explicit marker) is + # needed because we need to consume values that are also sane for a + # regular (non Model-) Form to find in its cleaned_data dictionary. + if data is not None: + # This value will be converted to str and stored in the + # database, so leaving False as-is is not acceptable. + setattr(instance, self.name, data or "") + + def formfield(self, **kwargs): + return super().formfield( + **{ + "form_class": forms.FileField, + "max_length": self.max_length, + **kwargs, + } + ) + + +class ImageFileDescriptor(FileDescriptor): + """ + Just like the FileDescriptor, but for ImageFields. The only difference is + assigning the width/height to the width_field/height_field, if appropriate. + """ + + def __set__(self, instance, value): + previous_file = instance.__dict__.get(self.field.attname) + super().__set__(instance, value) + + # To prevent recalculating image dimensions when we are instantiating + # an object from the database (bug #11084), only update dimensions if + # the field had a value before this assignment. Since the default + # value for FileField subclasses is an instance of field.attr_class, + # previous_file will only be None when we are called from + # Model.__init__(). The ImageField.update_dimension_fields method + # hooked up to the post_init signal handles the Model.__init__() cases. + # Assignment happening outside of Model.__init__() will trigger the + # update right here. + if previous_file is not None: + self.field.update_dimension_fields(instance, force=True) + + +class ImageFieldFile(ImageFile, FieldFile): + def delete(self, save=True): + # Clear the image dimensions cache + if hasattr(self, "_dimensions_cache"): + del self._dimensions_cache + super().delete(save) + + +class ImageField(FileField): + attr_class = ImageFieldFile + descriptor_class = ImageFileDescriptor + description = _("Image") + + def __init__( + self, + verbose_name=None, + name=None, + width_field=None, + height_field=None, + **kwargs, + ): + self.width_field, self.height_field = width_field, height_field + super().__init__(verbose_name, name, **kwargs) + + def check(self, **kwargs): + return [ + *super().check(**kwargs), + *self._check_image_library_installed(), + ] + + def _check_image_library_installed(self): + try: + from PIL import Image # NOQA + except ImportError: + return [ + checks.Error( + "Cannot use ImageField because Pillow is not installed.", + hint=( + "Get Pillow at https://pypi.org/project/Pillow/ " + 'or run command "python -m pip install Pillow".' + ), + obj=self, + id="fields.E210", + ) + ] + else: + return [] + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + if self.width_field: + kwargs["width_field"] = self.width_field + if self.height_field: + kwargs["height_field"] = self.height_field + return name, path, args, kwargs + + def contribute_to_class(self, cls, name, **kwargs): + super().contribute_to_class(cls, name, **kwargs) + # Attach update_dimension_fields so that dimension fields declared + # after their corresponding image field don't stay cleared by + # Model.__init__, see bug #11196. + # Only run post-initialization dimension update on non-abstract models + if not cls._meta.abstract: + signals.post_init.connect(self.update_dimension_fields, sender=cls) + + def update_dimension_fields(self, instance, force=False, *args, **kwargs): + """ + Update field's width and height fields, if defined. + + This method is hooked up to model's post_init signal to update + dimensions after instantiating a model instance. However, dimensions + won't be updated if the dimensions fields are already populated. This + avoids unnecessary recalculation when loading an object from the + database. + + Dimensions can be forced to update with force=True, which is how + ImageFileDescriptor.__set__ calls this method. + """ + # Nothing to update if the field doesn't have dimension fields or if + # the field is deferred. + has_dimension_fields = self.width_field or self.height_field + if not has_dimension_fields or self.attname not in instance.__dict__: + return + + # getattr will call the ImageFileDescriptor's __get__ method, which + # coerces the assigned value into an instance of self.attr_class + # (ImageFieldFile in this case). + file = getattr(instance, self.attname) + + # Nothing to update if we have no file and not being forced to update. + if not file and not force: + return + + dimension_fields_filled = not ( + (self.width_field and not getattr(instance, self.width_field)) + or (self.height_field and not getattr(instance, self.height_field)) + ) + # When both dimension fields have values, we are most likely loading + # data from the database or updating an image field that already had + # an image stored. In the first case, we don't want to update the + # dimension fields because we are already getting their values from the + # database. In the second case, we do want to update the dimensions + # fields and will skip this return because force will be True since we + # were called from ImageFileDescriptor.__set__. + if dimension_fields_filled and not force: + return + + # file should be an instance of ImageFieldFile or should be None. + if file: + width = file.width + height = file.height + else: + # No file, so clear dimensions fields. + width = None + height = None + + # Update the width and height fields. + if self.width_field: + setattr(instance, self.width_field, width) + if self.height_field: + setattr(instance, self.height_field, height) + + def formfield(self, **kwargs): + return super().formfield( + **{ + "form_class": forms.ImageField, + **kwargs, + } + ) diff --git a/virt/lib/python3.9/site-packages/django/db/models/fields/related_descriptors 3.py b/virt/lib/python3.9/site-packages/django/db/models/fields/related_descriptors 3.py new file mode 100644 index 00000000..78f7b2d2 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/db/models/fields/related_descriptors 3.py @@ -0,0 +1,1506 @@ +""" +Accessors for related objects. + +When a field defines a relation between two models, each model class provides +an attribute to access related instances of the other model class (unless the +reverse accessor has been disabled with related_name='+'). + +Accessors are implemented as descriptors in order to customize access and +assignment. This module defines the descriptor classes. + +Forward accessors follow foreign keys. Reverse accessors trace them back. For +example, with the following models:: + + class Parent(Model): + pass + + class Child(Model): + parent = ForeignKey(Parent, related_name='children') + + ``child.parent`` is a forward many-to-one relation. ``parent.children`` is a +reverse many-to-one relation. + +There are three types of relations (many-to-one, one-to-one, and many-to-many) +and two directions (forward and reverse) for a total of six combinations. + +1. Related instance on the forward side of a many-to-one relation: + ``ForwardManyToOneDescriptor``. + + Uniqueness of foreign key values is irrelevant to accessing the related + instance, making the many-to-one and one-to-one cases identical as far as + the descriptor is concerned. The constraint is checked upstream (unicity + validation in forms) or downstream (unique indexes in the database). + +2. Related instance on the forward side of a one-to-one + relation: ``ForwardOneToOneDescriptor``. + + It avoids querying the database when accessing the parent link field in + a multi-table inheritance scenario. + +3. Related instance on the reverse side of a one-to-one relation: + ``ReverseOneToOneDescriptor``. + + One-to-one relations are asymmetrical, despite the apparent symmetry of the + name, because they're implemented in the database with a foreign key from + one table to another. As a consequence ``ReverseOneToOneDescriptor`` is + slightly different from ``ForwardManyToOneDescriptor``. + +4. Related objects manager for related instances on the reverse side of a + many-to-one relation: ``ReverseManyToOneDescriptor``. + + Unlike the previous two classes, this one provides access to a collection + of objects. It returns a manager rather than an instance. + +5. Related objects manager for related instances on the forward or reverse + sides of a many-to-many relation: ``ManyToManyDescriptor``. + + Many-to-many relations are symmetrical. The syntax of Django models + requires declaring them on one side but that's an implementation detail. + They could be declared on the other side without any change in behavior. + Therefore the forward and reverse descriptors can be the same. + + If you're looking for ``ForwardManyToManyDescriptor`` or + ``ReverseManyToManyDescriptor``, use ``ManyToManyDescriptor`` instead. +""" + +from asgiref.sync import sync_to_async + +from django.core.exceptions import FieldError +from django.db import ( + DEFAULT_DB_ALIAS, + NotSupportedError, + connections, + router, + transaction, +) +from django.db.models import Q, Window, signals +from django.db.models.functions import RowNumber +from django.db.models.lookups import GreaterThan, LessThanOrEqual +from django.db.models.query import QuerySet +from django.db.models.query_utils import DeferredAttribute +from django.db.models.utils import AltersData, resolve_callables +from django.utils.functional import cached_property + + +class ForeignKeyDeferredAttribute(DeferredAttribute): + def __set__(self, instance, value): + if instance.__dict__.get(self.field.attname) != value and self.field.is_cached( + instance + ): + self.field.delete_cached_value(instance) + instance.__dict__[self.field.attname] = value + + +def _filter_prefetch_queryset(queryset, field_name, instances): + predicate = Q(**{f"{field_name}__in": instances}) + db = queryset._db or DEFAULT_DB_ALIAS + if queryset.query.is_sliced: + if not connections[db].features.supports_over_clause: + raise NotSupportedError( + "Prefetching from a limited queryset is only supported on backends " + "that support window functions." + ) + low_mark, high_mark = queryset.query.low_mark, queryset.query.high_mark + order_by = [ + expr for expr, _ in queryset.query.get_compiler(using=db).get_order_by() + ] + window = Window(RowNumber(), partition_by=field_name, order_by=order_by) + predicate &= GreaterThan(window, low_mark) + if high_mark is not None: + predicate &= LessThanOrEqual(window, high_mark) + queryset.query.clear_limits() + return queryset.filter(predicate) + + +class ForwardManyToOneDescriptor: + """ + Accessor to the related object on the forward side of a many-to-one or + one-to-one (via ForwardOneToOneDescriptor subclass) relation. + + In the example:: + + class Child(Model): + parent = ForeignKey(Parent, related_name='children') + + ``Child.parent`` is a ``ForwardManyToOneDescriptor`` instance. + """ + + def __init__(self, field_with_rel): + self.field = field_with_rel + + @cached_property + def RelatedObjectDoesNotExist(self): + # The exception can't be created at initialization time since the + # related model might not be resolved yet; `self.field.model` might + # still be a string model reference. + return type( + "RelatedObjectDoesNotExist", + (self.field.remote_field.model.DoesNotExist, AttributeError), + { + "__module__": self.field.model.__module__, + "__qualname__": "%s.%s.RelatedObjectDoesNotExist" + % ( + self.field.model.__qualname__, + self.field.name, + ), + }, + ) + + def is_cached(self, instance): + return self.field.is_cached(instance) + + def get_queryset(self, **hints): + return self.field.remote_field.model._base_manager.db_manager(hints=hints).all() + + def get_prefetch_queryset(self, instances, queryset=None): + if queryset is None: + queryset = self.get_queryset() + queryset._add_hints(instance=instances[0]) + + rel_obj_attr = self.field.get_foreign_related_value + instance_attr = self.field.get_local_related_value + instances_dict = {instance_attr(inst): inst for inst in instances} + related_field = self.field.foreign_related_fields[0] + remote_field = self.field.remote_field + + # FIXME: This will need to be revisited when we introduce support for + # composite fields. In the meantime we take this practical approach to + # solve a regression on 1.6 when the reverse manager in hidden + # (related_name ends with a '+'). Refs #21410. + # The check for len(...) == 1 is a special case that allows the query + # to be join-less and smaller. Refs #21760. + if remote_field.is_hidden() or len(self.field.foreign_related_fields) == 1: + query = { + "%s__in" + % related_field.name: {instance_attr(inst)[0] for inst in instances} + } + else: + query = {"%s__in" % self.field.related_query_name(): instances} + queryset = queryset.filter(**query) + + # Since we're going to assign directly in the cache, + # we must manage the reverse relation cache manually. + if not remote_field.multiple: + for rel_obj in queryset: + instance = instances_dict[rel_obj_attr(rel_obj)] + remote_field.set_cached_value(rel_obj, instance) + return ( + queryset, + rel_obj_attr, + instance_attr, + True, + self.field.get_cache_name(), + False, + ) + + def get_object(self, instance): + qs = self.get_queryset(instance=instance) + # Assuming the database enforces foreign keys, this won't fail. + return qs.get(self.field.get_reverse_related_filter(instance)) + + def __get__(self, instance, cls=None): + """ + Get the related instance through the forward relation. + + With the example above, when getting ``child.parent``: + + - ``self`` is the descriptor managing the ``parent`` attribute + - ``instance`` is the ``child`` instance + - ``cls`` is the ``Child`` class (we don't need it) + """ + if instance is None: + return self + + # The related instance is loaded from the database and then cached + # by the field on the model instance state. It can also be pre-cached + # by the reverse accessor (ReverseOneToOneDescriptor). + try: + rel_obj = self.field.get_cached_value(instance) + except KeyError: + has_value = None not in self.field.get_local_related_value(instance) + ancestor_link = ( + instance._meta.get_ancestor_link(self.field.model) + if has_value + else None + ) + if ancestor_link and ancestor_link.is_cached(instance): + # An ancestor link will exist if this field is defined on a + # multi-table inheritance parent of the instance's class. + ancestor = ancestor_link.get_cached_value(instance) + # The value might be cached on an ancestor if the instance + # originated from walking down the inheritance chain. + rel_obj = self.field.get_cached_value(ancestor, default=None) + else: + rel_obj = None + if rel_obj is None and has_value: + rel_obj = self.get_object(instance) + remote_field = self.field.remote_field + # If this is a one-to-one relation, set the reverse accessor + # cache on the related object to the current instance to avoid + # an extra SQL query if it's accessed later on. + if not remote_field.multiple: + remote_field.set_cached_value(rel_obj, instance) + self.field.set_cached_value(instance, rel_obj) + + if rel_obj is None and not self.field.null: + raise self.RelatedObjectDoesNotExist( + "%s has no %s." % (self.field.model.__name__, self.field.name) + ) + else: + return rel_obj + + def __set__(self, instance, value): + """ + Set the related instance through the forward relation. + + With the example above, when setting ``child.parent = parent``: + + - ``self`` is the descriptor managing the ``parent`` attribute + - ``instance`` is the ``child`` instance + - ``value`` is the ``parent`` instance on the right of the equal sign + """ + # An object must be an instance of the related class. + if value is not None and not isinstance( + value, self.field.remote_field.model._meta.concrete_model + ): + raise ValueError( + 'Cannot assign "%r": "%s.%s" must be a "%s" instance.' + % ( + value, + instance._meta.object_name, + self.field.name, + self.field.remote_field.model._meta.object_name, + ) + ) + elif value is not None: + if instance._state.db is None: + instance._state.db = router.db_for_write( + instance.__class__, instance=value + ) + if value._state.db is None: + value._state.db = router.db_for_write( + value.__class__, instance=instance + ) + if not router.allow_relation(value, instance): + raise ValueError( + 'Cannot assign "%r": the current database router prevents this ' + "relation." % value + ) + + remote_field = self.field.remote_field + # If we're setting the value of a OneToOneField to None, we need to clear + # out the cache on any old related object. Otherwise, deleting the + # previously-related object will also cause this object to be deleted, + # which is wrong. + if value is None: + # Look up the previously-related object, which may still be available + # since we've not yet cleared out the related field. + # Use the cache directly, instead of the accessor; if we haven't + # populated the cache, then we don't care - we're only accessing + # the object to invalidate the accessor cache, so there's no + # need to populate the cache just to expire it again. + related = self.field.get_cached_value(instance, default=None) + + # If we've got an old related object, we need to clear out its + # cache. This cache also might not exist if the related object + # hasn't been accessed yet. + if related is not None: + remote_field.set_cached_value(related, None) + + for lh_field, rh_field in self.field.related_fields: + setattr(instance, lh_field.attname, None) + + # Set the values of the related field. + else: + for lh_field, rh_field in self.field.related_fields: + setattr(instance, lh_field.attname, getattr(value, rh_field.attname)) + + # Set the related instance cache used by __get__ to avoid an SQL query + # when accessing the attribute we just set. + self.field.set_cached_value(instance, value) + + # If this is a one-to-one relation, set the reverse accessor cache on + # the related object to the current instance to avoid an extra SQL + # query if it's accessed later on. + if value is not None and not remote_field.multiple: + remote_field.set_cached_value(value, instance) + + def __reduce__(self): + """ + Pickling should return the instance attached by self.field on the + model, not a new copy of that descriptor. Use getattr() to retrieve + the instance directly from the model. + """ + return getattr, (self.field.model, self.field.name) + + +class ForwardOneToOneDescriptor(ForwardManyToOneDescriptor): + """ + Accessor to the related object on the forward side of a one-to-one relation. + + In the example:: + + class Restaurant(Model): + place = OneToOneField(Place, related_name='restaurant') + + ``Restaurant.place`` is a ``ForwardOneToOneDescriptor`` instance. + """ + + def get_object(self, instance): + if self.field.remote_field.parent_link: + deferred = instance.get_deferred_fields() + # Because it's a parent link, all the data is available in the + # instance, so populate the parent model with this data. + rel_model = self.field.remote_field.model + fields = [field.attname for field in rel_model._meta.concrete_fields] + + # If any of the related model's fields are deferred, fallback to + # fetching all fields from the related model. This avoids a query + # on the related model for every deferred field. + if not any(field in fields for field in deferred): + kwargs = {field: getattr(instance, field) for field in fields} + obj = rel_model(**kwargs) + obj._state.adding = instance._state.adding + obj._state.db = instance._state.db + return obj + return super().get_object(instance) + + def __set__(self, instance, value): + super().__set__(instance, value) + # If the primary key is a link to a parent model and a parent instance + # is being set, update the value of the inherited pk(s). + if self.field.primary_key and self.field.remote_field.parent_link: + opts = instance._meta + # Inherited primary key fields from this object's base classes. + inherited_pk_fields = [ + field + for field in opts.concrete_fields + if field.primary_key and field.remote_field + ] + for field in inherited_pk_fields: + rel_model_pk_name = field.remote_field.model._meta.pk.attname + raw_value = ( + getattr(value, rel_model_pk_name) if value is not None else None + ) + setattr(instance, rel_model_pk_name, raw_value) + + +class ReverseOneToOneDescriptor: + """ + Accessor to the related object on the reverse side of a one-to-one + relation. + + In the example:: + + class Restaurant(Model): + place = OneToOneField(Place, related_name='restaurant') + + ``Place.restaurant`` is a ``ReverseOneToOneDescriptor`` instance. + """ + + def __init__(self, related): + # Following the example above, `related` is an instance of OneToOneRel + # which represents the reverse restaurant field (place.restaurant). + self.related = related + + @cached_property + def RelatedObjectDoesNotExist(self): + # The exception isn't created at initialization time for the sake of + # consistency with `ForwardManyToOneDescriptor`. + return type( + "RelatedObjectDoesNotExist", + (self.related.related_model.DoesNotExist, AttributeError), + { + "__module__": self.related.model.__module__, + "__qualname__": "%s.%s.RelatedObjectDoesNotExist" + % ( + self.related.model.__qualname__, + self.related.name, + ), + }, + ) + + def is_cached(self, instance): + return self.related.is_cached(instance) + + def get_queryset(self, **hints): + return self.related.related_model._base_manager.db_manager(hints=hints).all() + + def get_prefetch_queryset(self, instances, queryset=None): + if queryset is None: + queryset = self.get_queryset() + queryset._add_hints(instance=instances[0]) + + rel_obj_attr = self.related.field.get_local_related_value + instance_attr = self.related.field.get_foreign_related_value + instances_dict = {instance_attr(inst): inst for inst in instances} + query = {"%s__in" % self.related.field.name: instances} + queryset = queryset.filter(**query) + + # Since we're going to assign directly in the cache, + # we must manage the reverse relation cache manually. + for rel_obj in queryset: + instance = instances_dict[rel_obj_attr(rel_obj)] + self.related.field.set_cached_value(rel_obj, instance) + return ( + queryset, + rel_obj_attr, + instance_attr, + True, + self.related.get_cache_name(), + False, + ) + + def __get__(self, instance, cls=None): + """ + Get the related instance through the reverse relation. + + With the example above, when getting ``place.restaurant``: + + - ``self`` is the descriptor managing the ``restaurant`` attribute + - ``instance`` is the ``place`` instance + - ``cls`` is the ``Place`` class (unused) + + Keep in mind that ``Restaurant`` holds the foreign key to ``Place``. + """ + if instance is None: + return self + + # The related instance is loaded from the database and then cached + # by the field on the model instance state. It can also be pre-cached + # by the forward accessor (ForwardManyToOneDescriptor). + try: + rel_obj = self.related.get_cached_value(instance) + except KeyError: + related_pk = instance.pk + if related_pk is None: + rel_obj = None + else: + filter_args = self.related.field.get_forward_related_filter(instance) + try: + rel_obj = self.get_queryset(instance=instance).get(**filter_args) + except self.related.related_model.DoesNotExist: + rel_obj = None + else: + # Set the forward accessor cache on the related object to + # the current instance to avoid an extra SQL query if it's + # accessed later on. + self.related.field.set_cached_value(rel_obj, instance) + self.related.set_cached_value(instance, rel_obj) + + if rel_obj is None: + raise self.RelatedObjectDoesNotExist( + "%s has no %s." + % (instance.__class__.__name__, self.related.get_accessor_name()) + ) + else: + return rel_obj + + def __set__(self, instance, value): + """ + Set the related instance through the reverse relation. + + With the example above, when setting ``place.restaurant = restaurant``: + + - ``self`` is the descriptor managing the ``restaurant`` attribute + - ``instance`` is the ``place`` instance + - ``value`` is the ``restaurant`` instance on the right of the equal sign + + Keep in mind that ``Restaurant`` holds the foreign key to ``Place``. + """ + # The similarity of the code below to the code in + # ForwardManyToOneDescriptor is annoying, but there's a bunch + # of small differences that would make a common base class convoluted. + + if value is None: + # Update the cached related instance (if any) & clear the cache. + # Following the example above, this would be the cached + # ``restaurant`` instance (if any). + rel_obj = self.related.get_cached_value(instance, default=None) + if rel_obj is not None: + # Remove the ``restaurant`` instance from the ``place`` + # instance cache. + self.related.delete_cached_value(instance) + # Set the ``place`` field on the ``restaurant`` + # instance to None. + setattr(rel_obj, self.related.field.name, None) + elif not isinstance(value, self.related.related_model): + # An object must be an instance of the related class. + raise ValueError( + 'Cannot assign "%r": "%s.%s" must be a "%s" instance.' + % ( + value, + instance._meta.object_name, + self.related.get_accessor_name(), + self.related.related_model._meta.object_name, + ) + ) + else: + if instance._state.db is None: + instance._state.db = router.db_for_write( + instance.__class__, instance=value + ) + if value._state.db is None: + value._state.db = router.db_for_write( + value.__class__, instance=instance + ) + if not router.allow_relation(value, instance): + raise ValueError( + 'Cannot assign "%r": the current database router prevents this ' + "relation." % value + ) + + related_pk = tuple( + getattr(instance, field.attname) + for field in self.related.field.foreign_related_fields + ) + # Set the value of the related field to the value of the related + # object's related field. + for index, field in enumerate(self.related.field.local_related_fields): + setattr(value, field.attname, related_pk[index]) + + # Set the related instance cache used by __get__ to avoid an SQL query + # when accessing the attribute we just set. + self.related.set_cached_value(instance, value) + + # Set the forward accessor cache on the related object to the current + # instance to avoid an extra SQL query if it's accessed later on. + self.related.field.set_cached_value(value, instance) + + def __reduce__(self): + # Same purpose as ForwardManyToOneDescriptor.__reduce__(). + return getattr, (self.related.model, self.related.name) + + +class ReverseManyToOneDescriptor: + """ + Accessor to the related objects manager on the reverse side of a + many-to-one relation. + + In the example:: + + class Child(Model): + parent = ForeignKey(Parent, related_name='children') + + ``Parent.children`` is a ``ReverseManyToOneDescriptor`` instance. + + Most of the implementation is delegated to a dynamically defined manager + class built by ``create_forward_many_to_many_manager()`` defined below. + """ + + def __init__(self, rel): + self.rel = rel + self.field = rel.field + + @cached_property + def related_manager_cls(self): + related_model = self.rel.related_model + + return create_reverse_many_to_one_manager( + related_model._default_manager.__class__, + self.rel, + ) + + def __get__(self, instance, cls=None): + """ + Get the related objects through the reverse relation. + + With the example above, when getting ``parent.children``: + + - ``self`` is the descriptor managing the ``children`` attribute + - ``instance`` is the ``parent`` instance + - ``cls`` is the ``Parent`` class (unused) + """ + if instance is None: + return self + + return self.related_manager_cls(instance) + + def _get_set_deprecation_msg_params(self): + return ( + "reverse side of a related set", + self.rel.get_accessor_name(), + ) + + def __set__(self, instance, value): + raise TypeError( + "Direct assignment to the %s is prohibited. Use %s.set() instead." + % self._get_set_deprecation_msg_params(), + ) + + +def create_reverse_many_to_one_manager(superclass, rel): + """ + Create a manager for the reverse side of a many-to-one relation. + + This manager subclasses another manager, generally the default manager of + the related model, and adds behaviors specific to many-to-one relations. + """ + + class RelatedManager(superclass, AltersData): + def __init__(self, instance): + super().__init__() + + self.instance = instance + self.model = rel.related_model + self.field = rel.field + + self.core_filters = {self.field.name: instance} + + def __call__(self, *, manager): + manager = getattr(self.model, manager) + manager_class = create_reverse_many_to_one_manager(manager.__class__, rel) + return manager_class(self.instance) + + do_not_call_in_templates = True + + def _check_fk_val(self): + for field in self.field.foreign_related_fields: + if getattr(self.instance, field.attname) is None: + raise ValueError( + f'"{self.instance!r}" needs to have a value for field ' + f'"{field.attname}" before this relationship can be used.' + ) + + def _apply_rel_filters(self, queryset): + """ + Filter the queryset for the instance this manager is bound to. + """ + db = self._db or router.db_for_read(self.model, instance=self.instance) + empty_strings_as_null = connections[ + db + ].features.interprets_empty_strings_as_nulls + queryset._add_hints(instance=self.instance) + if self._db: + queryset = queryset.using(self._db) + queryset._defer_next_filter = True + queryset = queryset.filter(**self.core_filters) + for field in self.field.foreign_related_fields: + val = getattr(self.instance, field.attname) + if val is None or (val == "" and empty_strings_as_null): + return queryset.none() + if self.field.many_to_one: + # Guard against field-like objects such as GenericRelation + # that abuse create_reverse_many_to_one_manager() with reverse + # one-to-many relationships instead and break known related + # objects assignment. + try: + target_field = self.field.target_field + except FieldError: + # The relationship has multiple target fields. Use a tuple + # for related object id. + rel_obj_id = tuple( + [ + getattr(self.instance, target_field.attname) + for target_field in self.field.path_infos[-1].target_fields + ] + ) + else: + rel_obj_id = getattr(self.instance, target_field.attname) + queryset._known_related_objects = { + self.field: {rel_obj_id: self.instance} + } + return queryset + + def _remove_prefetched_objects(self): + try: + self.instance._prefetched_objects_cache.pop( + self.field.remote_field.get_cache_name() + ) + except (AttributeError, KeyError): + pass # nothing to clear from cache + + def get_queryset(self): + # Even if this relation is not to pk, we require still pk value. + # The wish is that the instance has been already saved to DB, + # although having a pk value isn't a guarantee of that. + if self.instance.pk is None: + raise ValueError( + f"{self.instance.__class__.__name__!r} instance needs to have a " + f"primary key value before this relationship can be used." + ) + try: + return self.instance._prefetched_objects_cache[ + self.field.remote_field.get_cache_name() + ] + except (AttributeError, KeyError): + queryset = super().get_queryset() + return self._apply_rel_filters(queryset) + + def get_prefetch_queryset(self, instances, queryset=None): + if queryset is None: + queryset = super().get_queryset() + + queryset._add_hints(instance=instances[0]) + queryset = queryset.using(queryset._db or self._db) + + rel_obj_attr = self.field.get_local_related_value + instance_attr = self.field.get_foreign_related_value + instances_dict = {instance_attr(inst): inst for inst in instances} + queryset = _filter_prefetch_queryset(queryset, self.field.name, instances) + + # Since we just bypassed this class' get_queryset(), we must manage + # the reverse relation manually. + for rel_obj in queryset: + if not self.field.is_cached(rel_obj): + instance = instances_dict[rel_obj_attr(rel_obj)] + setattr(rel_obj, self.field.name, instance) + cache_name = self.field.remote_field.get_cache_name() + return queryset, rel_obj_attr, instance_attr, False, cache_name, False + + def add(self, *objs, bulk=True): + self._check_fk_val() + self._remove_prefetched_objects() + db = router.db_for_write(self.model, instance=self.instance) + + def check_and_update_obj(obj): + if not isinstance(obj, self.model): + raise TypeError( + "'%s' instance expected, got %r" + % ( + self.model._meta.object_name, + obj, + ) + ) + setattr(obj, self.field.name, self.instance) + + if bulk: + pks = [] + for obj in objs: + check_and_update_obj(obj) + if obj._state.adding or obj._state.db != db: + raise ValueError( + "%r instance isn't saved. Use bulk=False or save " + "the object first." % obj + ) + pks.append(obj.pk) + self.model._base_manager.using(db).filter(pk__in=pks).update( + **{ + self.field.name: self.instance, + } + ) + else: + with transaction.atomic(using=db, savepoint=False): + for obj in objs: + check_and_update_obj(obj) + obj.save() + + add.alters_data = True + + async def aadd(self, *objs, bulk=True): + return await sync_to_async(self.add)(*objs, bulk=bulk) + + aadd.alters_data = True + + def create(self, **kwargs): + self._check_fk_val() + kwargs[self.field.name] = self.instance + db = router.db_for_write(self.model, instance=self.instance) + return super(RelatedManager, self.db_manager(db)).create(**kwargs) + + create.alters_data = True + + async def acreate(self, **kwargs): + return await sync_to_async(self.create)(**kwargs) + + acreate.alters_data = True + + def get_or_create(self, **kwargs): + self._check_fk_val() + kwargs[self.field.name] = self.instance + db = router.db_for_write(self.model, instance=self.instance) + return super(RelatedManager, self.db_manager(db)).get_or_create(**kwargs) + + get_or_create.alters_data = True + + async def aget_or_create(self, **kwargs): + return await sync_to_async(self.get_or_create)(**kwargs) + + aget_or_create.alters_data = True + + def update_or_create(self, **kwargs): + self._check_fk_val() + kwargs[self.field.name] = self.instance + db = router.db_for_write(self.model, instance=self.instance) + return super(RelatedManager, self.db_manager(db)).update_or_create(**kwargs) + + update_or_create.alters_data = True + + async def aupdate_or_create(self, **kwargs): + return await sync_to_async(self.update_or_create)(**kwargs) + + aupdate_or_create.alters_data = True + + # remove() and clear() are only provided if the ForeignKey can have a + # value of null. + if rel.field.null: + + def remove(self, *objs, bulk=True): + if not objs: + return + self._check_fk_val() + val = self.field.get_foreign_related_value(self.instance) + old_ids = set() + for obj in objs: + if not isinstance(obj, self.model): + raise TypeError( + "'%s' instance expected, got %r" + % ( + self.model._meta.object_name, + obj, + ) + ) + # Is obj actually part of this descriptor set? + if self.field.get_local_related_value(obj) == val: + old_ids.add(obj.pk) + else: + raise self.field.remote_field.model.DoesNotExist( + "%r is not related to %r." % (obj, self.instance) + ) + self._clear(self.filter(pk__in=old_ids), bulk) + + remove.alters_data = True + + async def aremove(self, *objs, bulk=True): + return await sync_to_async(self.remove)(*objs, bulk=bulk) + + aremove.alters_data = True + + def clear(self, *, bulk=True): + self._check_fk_val() + self._clear(self, bulk) + + clear.alters_data = True + + async def aclear(self, *, bulk=True): + return await sync_to_async(self.clear)(bulk=bulk) + + aclear.alters_data = True + + def _clear(self, queryset, bulk): + self._remove_prefetched_objects() + db = router.db_for_write(self.model, instance=self.instance) + queryset = queryset.using(db) + if bulk: + # `QuerySet.update()` is intrinsically atomic. + queryset.update(**{self.field.name: None}) + else: + with transaction.atomic(using=db, savepoint=False): + for obj in queryset: + setattr(obj, self.field.name, None) + obj.save(update_fields=[self.field.name]) + + _clear.alters_data = True + + def set(self, objs, *, bulk=True, clear=False): + self._check_fk_val() + # Force evaluation of `objs` in case it's a queryset whose value + # could be affected by `manager.clear()`. Refs #19816. + objs = tuple(objs) + + if self.field.null: + db = router.db_for_write(self.model, instance=self.instance) + with transaction.atomic(using=db, savepoint=False): + if clear: + self.clear(bulk=bulk) + self.add(*objs, bulk=bulk) + else: + old_objs = set(self.using(db).all()) + new_objs = [] + for obj in objs: + if obj in old_objs: + old_objs.remove(obj) + else: + new_objs.append(obj) + + self.remove(*old_objs, bulk=bulk) + self.add(*new_objs, bulk=bulk) + else: + self.add(*objs, bulk=bulk) + + set.alters_data = True + + async def aset(self, objs, *, bulk=True, clear=False): + return await sync_to_async(self.set)(objs=objs, bulk=bulk, clear=clear) + + aset.alters_data = True + + return RelatedManager + + +class ManyToManyDescriptor(ReverseManyToOneDescriptor): + """ + Accessor to the related objects manager on the forward and reverse sides of + a many-to-many relation. + + In the example:: + + class Pizza(Model): + toppings = ManyToManyField(Topping, related_name='pizzas') + + ``Pizza.toppings`` and ``Topping.pizzas`` are ``ManyToManyDescriptor`` + instances. + + Most of the implementation is delegated to a dynamically defined manager + class built by ``create_forward_many_to_many_manager()`` defined below. + """ + + def __init__(self, rel, reverse=False): + super().__init__(rel) + + self.reverse = reverse + + @property + def through(self): + # through is provided so that you have easy access to the through + # model (Book.authors.through) for inlines, etc. This is done as + # a property to ensure that the fully resolved value is returned. + return self.rel.through + + @cached_property + def related_manager_cls(self): + related_model = self.rel.related_model if self.reverse else self.rel.model + + return create_forward_many_to_many_manager( + related_model._default_manager.__class__, + self.rel, + reverse=self.reverse, + ) + + def _get_set_deprecation_msg_params(self): + return ( + "%s side of a many-to-many set" + % ("reverse" if self.reverse else "forward"), + self.rel.get_accessor_name() if self.reverse else self.field.name, + ) + + +def create_forward_many_to_many_manager(superclass, rel, reverse): + """ + Create a manager for the either side of a many-to-many relation. + + This manager subclasses another manager, generally the default manager of + the related model, and adds behaviors specific to many-to-many relations. + """ + + class ManyRelatedManager(superclass, AltersData): + def __init__(self, instance=None): + super().__init__() + + self.instance = instance + + if not reverse: + self.model = rel.model + self.query_field_name = rel.field.related_query_name() + self.prefetch_cache_name = rel.field.name + self.source_field_name = rel.field.m2m_field_name() + self.target_field_name = rel.field.m2m_reverse_field_name() + self.symmetrical = rel.symmetrical + else: + self.model = rel.related_model + self.query_field_name = rel.field.name + self.prefetch_cache_name = rel.field.related_query_name() + self.source_field_name = rel.field.m2m_reverse_field_name() + self.target_field_name = rel.field.m2m_field_name() + self.symmetrical = False + + self.through = rel.through + self.reverse = reverse + + self.source_field = self.through._meta.get_field(self.source_field_name) + self.target_field = self.through._meta.get_field(self.target_field_name) + + self.core_filters = {} + self.pk_field_names = {} + for lh_field, rh_field in self.source_field.related_fields: + core_filter_key = "%s__%s" % (self.query_field_name, rh_field.name) + self.core_filters[core_filter_key] = getattr(instance, rh_field.attname) + self.pk_field_names[lh_field.name] = rh_field.name + + self.related_val = self.source_field.get_foreign_related_value(instance) + if None in self.related_val: + raise ValueError( + '"%r" needs to have a value for field "%s" before ' + "this many-to-many relationship can be used." + % (instance, self.pk_field_names[self.source_field_name]) + ) + # Even if this relation is not to pk, we require still pk value. + # The wish is that the instance has been already saved to DB, + # although having a pk value isn't a guarantee of that. + if instance.pk is None: + raise ValueError( + "%r instance needs to have a primary key value before " + "a many-to-many relationship can be used." + % instance.__class__.__name__ + ) + + def __call__(self, *, manager): + manager = getattr(self.model, manager) + manager_class = create_forward_many_to_many_manager( + manager.__class__, rel, reverse + ) + return manager_class(instance=self.instance) + + do_not_call_in_templates = True + + def _build_remove_filters(self, removed_vals): + filters = Q.create([(self.source_field_name, self.related_val)]) + # No need to add a subquery condition if removed_vals is a QuerySet without + # filters. + removed_vals_filters = ( + not isinstance(removed_vals, QuerySet) or removed_vals._has_filters() + ) + if removed_vals_filters: + filters &= Q.create([(f"{self.target_field_name}__in", removed_vals)]) + if self.symmetrical: + symmetrical_filters = Q.create( + [(self.target_field_name, self.related_val)] + ) + if removed_vals_filters: + symmetrical_filters &= Q.create( + [(f"{self.source_field_name}__in", removed_vals)] + ) + filters |= symmetrical_filters + return filters + + def _apply_rel_filters(self, queryset): + """ + Filter the queryset for the instance this manager is bound to. + """ + queryset._add_hints(instance=self.instance) + if self._db: + queryset = queryset.using(self._db) + queryset._defer_next_filter = True + return queryset._next_is_sticky().filter(**self.core_filters) + + def _remove_prefetched_objects(self): + try: + self.instance._prefetched_objects_cache.pop(self.prefetch_cache_name) + except (AttributeError, KeyError): + pass # nothing to clear from cache + + def get_queryset(self): + try: + return self.instance._prefetched_objects_cache[self.prefetch_cache_name] + except (AttributeError, KeyError): + queryset = super().get_queryset() + return self._apply_rel_filters(queryset) + + def get_prefetch_queryset(self, instances, queryset=None): + if queryset is None: + queryset = super().get_queryset() + + queryset._add_hints(instance=instances[0]) + queryset = queryset.using(queryset._db or self._db) + queryset = _filter_prefetch_queryset( + queryset._next_is_sticky(), self.query_field_name, instances + ) + + # M2M: need to annotate the query in order to get the primary model + # that the secondary model was actually related to. We know that + # there will already be a join on the join table, so we can just add + # the select. + + # For non-autocreated 'through' models, can't assume we are + # dealing with PK values. + fk = self.through._meta.get_field(self.source_field_name) + join_table = fk.model._meta.db_table + connection = connections[queryset.db] + qn = connection.ops.quote_name + queryset = queryset.extra( + select={ + "_prefetch_related_val_%s" + % f.attname: "%s.%s" + % (qn(join_table), qn(f.column)) + for f in fk.local_related_fields + } + ) + return ( + queryset, + lambda result: tuple( + getattr(result, "_prefetch_related_val_%s" % f.attname) + for f in fk.local_related_fields + ), + lambda inst: tuple( + f.get_db_prep_value(getattr(inst, f.attname), connection) + for f in fk.foreign_related_fields + ), + False, + self.prefetch_cache_name, + False, + ) + + def add(self, *objs, through_defaults=None): + self._remove_prefetched_objects() + db = router.db_for_write(self.through, instance=self.instance) + with transaction.atomic(using=db, savepoint=False): + self._add_items( + self.source_field_name, + self.target_field_name, + *objs, + through_defaults=through_defaults, + ) + # If this is a symmetrical m2m relation to self, add the mirror + # entry in the m2m table. + if self.symmetrical: + self._add_items( + self.target_field_name, + self.source_field_name, + *objs, + through_defaults=through_defaults, + ) + + add.alters_data = True + + async def aadd(self, *objs, through_defaults=None): + return await sync_to_async(self.add)( + *objs, through_defaults=through_defaults + ) + + aadd.alters_data = True + + def remove(self, *objs): + self._remove_prefetched_objects() + self._remove_items(self.source_field_name, self.target_field_name, *objs) + + remove.alters_data = True + + async def aremove(self, *objs): + return await sync_to_async(self.remove)(*objs) + + aremove.alters_data = True + + def clear(self): + db = router.db_for_write(self.through, instance=self.instance) + with transaction.atomic(using=db, savepoint=False): + signals.m2m_changed.send( + sender=self.through, + action="pre_clear", + instance=self.instance, + reverse=self.reverse, + model=self.model, + pk_set=None, + using=db, + ) + self._remove_prefetched_objects() + filters = self._build_remove_filters(super().get_queryset().using(db)) + self.through._default_manager.using(db).filter(filters).delete() + + signals.m2m_changed.send( + sender=self.through, + action="post_clear", + instance=self.instance, + reverse=self.reverse, + model=self.model, + pk_set=None, + using=db, + ) + + clear.alters_data = True + + async def aclear(self): + return await sync_to_async(self.clear)() + + aclear.alters_data = True + + def set(self, objs, *, clear=False, through_defaults=None): + # Force evaluation of `objs` in case it's a queryset whose value + # could be affected by `manager.clear()`. Refs #19816. + objs = tuple(objs) + + db = router.db_for_write(self.through, instance=self.instance) + with transaction.atomic(using=db, savepoint=False): + if clear: + self.clear() + self.add(*objs, through_defaults=through_defaults) + else: + old_ids = set( + self.using(db).values_list( + self.target_field.target_field.attname, flat=True + ) + ) + + new_objs = [] + for obj in objs: + fk_val = ( + self.target_field.get_foreign_related_value(obj)[0] + if isinstance(obj, self.model) + else self.target_field.get_prep_value(obj) + ) + if fk_val in old_ids: + old_ids.remove(fk_val) + else: + new_objs.append(obj) + + self.remove(*old_ids) + self.add(*new_objs, through_defaults=through_defaults) + + set.alters_data = True + + async def aset(self, objs, *, clear=False, through_defaults=None): + return await sync_to_async(self.set)( + objs=objs, clear=clear, through_defaults=through_defaults + ) + + aset.alters_data = True + + def create(self, *, through_defaults=None, **kwargs): + db = router.db_for_write(self.instance.__class__, instance=self.instance) + new_obj = super(ManyRelatedManager, self.db_manager(db)).create(**kwargs) + self.add(new_obj, through_defaults=through_defaults) + return new_obj + + create.alters_data = True + + async def acreate(self, *, through_defaults=None, **kwargs): + return await sync_to_async(self.create)( + through_defaults=through_defaults, **kwargs + ) + + acreate.alters_data = True + + def get_or_create(self, *, through_defaults=None, **kwargs): + db = router.db_for_write(self.instance.__class__, instance=self.instance) + obj, created = super(ManyRelatedManager, self.db_manager(db)).get_or_create( + **kwargs + ) + # We only need to add() if created because if we got an object back + # from get() then the relationship already exists. + if created: + self.add(obj, through_defaults=through_defaults) + return obj, created + + get_or_create.alters_data = True + + async def aget_or_create(self, *, through_defaults=None, **kwargs): + return await sync_to_async(self.get_or_create)( + through_defaults=through_defaults, **kwargs + ) + + aget_or_create.alters_data = True + + def update_or_create(self, *, through_defaults=None, **kwargs): + db = router.db_for_write(self.instance.__class__, instance=self.instance) + obj, created = super( + ManyRelatedManager, self.db_manager(db) + ).update_or_create(**kwargs) + # We only need to add() if created because if we got an object back + # from get() then the relationship already exists. + if created: + self.add(obj, through_defaults=through_defaults) + return obj, created + + update_or_create.alters_data = True + + async def aupdate_or_create(self, *, through_defaults=None, **kwargs): + return await sync_to_async(self.update_or_create)( + through_defaults=through_defaults, **kwargs + ) + + aupdate_or_create.alters_data = True + + def _get_target_ids(self, target_field_name, objs): + """ + Return the set of ids of `objs` that the target field references. + """ + from django.db.models import Model + + target_ids = set() + target_field = self.through._meta.get_field(target_field_name) + for obj in objs: + if isinstance(obj, self.model): + if not router.allow_relation(obj, self.instance): + raise ValueError( + 'Cannot add "%r": instance is on database "%s", ' + 'value is on database "%s"' + % (obj, self.instance._state.db, obj._state.db) + ) + target_id = target_field.get_foreign_related_value(obj)[0] + if target_id is None: + raise ValueError( + 'Cannot add "%r": the value for field "%s" is None' + % (obj, target_field_name) + ) + target_ids.add(target_id) + elif isinstance(obj, Model): + raise TypeError( + "'%s' instance expected, got %r" + % (self.model._meta.object_name, obj) + ) + else: + target_ids.add(target_field.get_prep_value(obj)) + return target_ids + + def _get_missing_target_ids( + self, source_field_name, target_field_name, db, target_ids + ): + """ + Return the subset of ids of `objs` that aren't already assigned to + this relationship. + """ + vals = ( + self.through._default_manager.using(db) + .values_list(target_field_name, flat=True) + .filter( + **{ + source_field_name: self.related_val[0], + "%s__in" % target_field_name: target_ids, + } + ) + ) + return target_ids.difference(vals) + + def _get_add_plan(self, db, source_field_name): + """ + Return a boolean triple of the way the add should be performed. + + The first element is whether or not bulk_create(ignore_conflicts) + can be used, the second whether or not signals must be sent, and + the third element is whether or not the immediate bulk insertion + with conflicts ignored can be performed. + """ + # Conflicts can be ignored when the intermediary model is + # auto-created as the only possible collision is on the + # (source_id, target_id) tuple. The same assertion doesn't hold for + # user-defined intermediary models as they could have other fields + # causing conflicts which must be surfaced. + can_ignore_conflicts = ( + self.through._meta.auto_created is not False + and connections[db].features.supports_ignore_conflicts + ) + # Don't send the signal when inserting duplicate data row + # for symmetrical reverse entries. + must_send_signals = ( + self.reverse or source_field_name == self.source_field_name + ) and (signals.m2m_changed.has_listeners(self.through)) + # Fast addition through bulk insertion can only be performed + # if no m2m_changed listeners are connected for self.through + # as they require the added set of ids to be provided via + # pk_set. + return ( + can_ignore_conflicts, + must_send_signals, + (can_ignore_conflicts and not must_send_signals), + ) + + def _add_items( + self, source_field_name, target_field_name, *objs, through_defaults=None + ): + # source_field_name: the PK fieldname in join table for the source object + # target_field_name: the PK fieldname in join table for the target object + # *objs - objects to add. Either object instances, or primary keys + # of object instances. + if not objs: + return + + through_defaults = dict(resolve_callables(through_defaults or {})) + target_ids = self._get_target_ids(target_field_name, objs) + db = router.db_for_write(self.through, instance=self.instance) + can_ignore_conflicts, must_send_signals, can_fast_add = self._get_add_plan( + db, source_field_name + ) + if can_fast_add: + self.through._default_manager.using(db).bulk_create( + [ + self.through( + **{ + "%s_id" % source_field_name: self.related_val[0], + "%s_id" % target_field_name: target_id, + } + ) + for target_id in target_ids + ], + ignore_conflicts=True, + ) + return + + missing_target_ids = self._get_missing_target_ids( + source_field_name, target_field_name, db, target_ids + ) + with transaction.atomic(using=db, savepoint=False): + if must_send_signals: + signals.m2m_changed.send( + sender=self.through, + action="pre_add", + instance=self.instance, + reverse=self.reverse, + model=self.model, + pk_set=missing_target_ids, + using=db, + ) + # Add the ones that aren't there already. + self.through._default_manager.using(db).bulk_create( + [ + self.through( + **through_defaults, + **{ + "%s_id" % source_field_name: self.related_val[0], + "%s_id" % target_field_name: target_id, + }, + ) + for target_id in missing_target_ids + ], + ignore_conflicts=can_ignore_conflicts, + ) + + if must_send_signals: + signals.m2m_changed.send( + sender=self.through, + action="post_add", + instance=self.instance, + reverse=self.reverse, + model=self.model, + pk_set=missing_target_ids, + using=db, + ) + + def _remove_items(self, source_field_name, target_field_name, *objs): + # source_field_name: the PK colname in join table for the source object + # target_field_name: the PK colname in join table for the target object + # *objs - objects to remove. Either object instances, or primary + # keys of object instances. + if not objs: + return + + # Check that all the objects are of the right type + old_ids = set() + for obj in objs: + if isinstance(obj, self.model): + fk_val = self.target_field.get_foreign_related_value(obj)[0] + old_ids.add(fk_val) + else: + old_ids.add(obj) + + db = router.db_for_write(self.through, instance=self.instance) + with transaction.atomic(using=db, savepoint=False): + # Send a signal to the other end if need be. + signals.m2m_changed.send( + sender=self.through, + action="pre_remove", + instance=self.instance, + reverse=self.reverse, + model=self.model, + pk_set=old_ids, + using=db, + ) + target_model_qs = super().get_queryset() + if target_model_qs._has_filters(): + old_vals = target_model_qs.using(db).filter( + **{"%s__in" % self.target_field.target_field.attname: old_ids} + ) + else: + old_vals = old_ids + filters = self._build_remove_filters(old_vals) + self.through._default_manager.using(db).filter(filters).delete() + + signals.m2m_changed.send( + sender=self.through, + action="post_remove", + instance=self.instance, + reverse=self.reverse, + model=self.model, + pk_set=old_ids, + using=db, + ) + + return ManyRelatedManager diff --git a/virt/lib/python3.9/site-packages/django/db/models/query 3.py b/virt/lib/python3.9/site-packages/django/db/models/query 3.py new file mode 100644 index 00000000..8c11d28c --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/db/models/query 3.py @@ -0,0 +1,2631 @@ +""" +The main QuerySet implementation. This provides the public API for the ORM. +""" + +import copy +import operator +import warnings +from itertools import chain, islice + +from asgiref.sync import sync_to_async + +import django +from django.conf import settings +from django.core import exceptions +from django.db import ( + DJANGO_VERSION_PICKLE_KEY, + IntegrityError, + NotSupportedError, + connections, + router, + transaction, +) +from django.db.models import AutoField, DateField, DateTimeField, Field, sql +from django.db.models.constants import LOOKUP_SEP, OnConflict +from django.db.models.deletion import Collector +from django.db.models.expressions import Case, F, Value, When +from django.db.models.functions import Cast, Trunc +from django.db.models.query_utils import FilteredRelation, Q +from django.db.models.sql.constants import CURSOR, GET_ITERATOR_CHUNK_SIZE +from django.db.models.utils import ( + AltersData, + create_namedtuple_class, + resolve_callables, +) +from django.utils import timezone +from django.utils.deprecation import RemovedInDjango50Warning +from django.utils.functional import cached_property, partition + +# The maximum number of results to fetch in a get() query. +MAX_GET_RESULTS = 21 + +# The maximum number of items to display in a QuerySet.__repr__ +REPR_OUTPUT_SIZE = 20 + + +class BaseIterable: + def __init__( + self, queryset, chunked_fetch=False, chunk_size=GET_ITERATOR_CHUNK_SIZE + ): + self.queryset = queryset + self.chunked_fetch = chunked_fetch + self.chunk_size = chunk_size + + async def _async_generator(self): + # Generators don't actually start running until the first time you call + # next() on them, so make the generator object in the async thread and + # then repeatedly dispatch to it in a sync thread. + sync_generator = self.__iter__() + + def next_slice(gen): + return list(islice(gen, self.chunk_size)) + + while True: + chunk = await sync_to_async(next_slice)(sync_generator) + for item in chunk: + yield item + if len(chunk) < self.chunk_size: + break + + # __aiter__() is a *synchronous* method that has to then return an + # *asynchronous* iterator/generator. Thus, nest an async generator inside + # it. + # This is a generic iterable converter for now, and is going to suffer a + # performance penalty on large sets of items due to the cost of crossing + # over the sync barrier for each chunk. Custom __aiter__() methods should + # be added to each Iterable subclass, but that needs some work in the + # Compiler first. + def __aiter__(self): + return self._async_generator() + + +class ModelIterable(BaseIterable): + """Iterable that yields a model instance for each row.""" + + def __iter__(self): + queryset = self.queryset + db = queryset.db + compiler = queryset.query.get_compiler(using=db) + # Execute the query. This will also fill compiler.select, klass_info, + # and annotations. + results = compiler.execute_sql( + chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size + ) + select, klass_info, annotation_col_map = ( + compiler.select, + compiler.klass_info, + compiler.annotation_col_map, + ) + model_cls = klass_info["model"] + select_fields = klass_info["select_fields"] + model_fields_start, model_fields_end = select_fields[0], select_fields[-1] + 1 + init_list = [ + f[0].target.attname for f in select[model_fields_start:model_fields_end] + ] + related_populators = get_related_populators(klass_info, select, db) + known_related_objects = [ + ( + field, + related_objs, + operator.attrgetter( + *[ + field.attname + if from_field == "self" + else queryset.model._meta.get_field(from_field).attname + for from_field in field.from_fields + ] + ), + ) + for field, related_objs in queryset._known_related_objects.items() + ] + for row in compiler.results_iter(results): + obj = model_cls.from_db( + db, init_list, row[model_fields_start:model_fields_end] + ) + for rel_populator in related_populators: + rel_populator.populate(row, obj) + if annotation_col_map: + for attr_name, col_pos in annotation_col_map.items(): + setattr(obj, attr_name, row[col_pos]) + + # Add the known related objects to the model. + for field, rel_objs, rel_getter in known_related_objects: + # Avoid overwriting objects loaded by, e.g., select_related(). + if field.is_cached(obj): + continue + rel_obj_id = rel_getter(obj) + try: + rel_obj = rel_objs[rel_obj_id] + except KeyError: + pass # May happen in qs1 | qs2 scenarios. + else: + setattr(obj, field.name, rel_obj) + + yield obj + + +class RawModelIterable(BaseIterable): + """ + Iterable that yields a model instance for each row from a raw queryset. + """ + + def __iter__(self): + # Cache some things for performance reasons outside the loop. + db = self.queryset.db + query = self.queryset.query + connection = connections[db] + compiler = connection.ops.compiler("SQLCompiler")(query, connection, db) + query_iterator = iter(query) + + try: + ( + model_init_names, + model_init_pos, + annotation_fields, + ) = self.queryset.resolve_model_init_order() + model_cls = self.queryset.model + if model_cls._meta.pk.attname not in model_init_names: + raise exceptions.FieldDoesNotExist( + "Raw query must include the primary key" + ) + fields = [self.queryset.model_fields.get(c) for c in self.queryset.columns] + converters = compiler.get_converters( + [f.get_col(f.model._meta.db_table) if f else None for f in fields] + ) + if converters: + query_iterator = compiler.apply_converters(query_iterator, converters) + for values in query_iterator: + # Associate fields to values + model_init_values = [values[pos] for pos in model_init_pos] + instance = model_cls.from_db(db, model_init_names, model_init_values) + if annotation_fields: + for column, pos in annotation_fields: + setattr(instance, column, values[pos]) + yield instance + finally: + # Done iterating the Query. If it has its own cursor, close it. + if hasattr(query, "cursor") and query.cursor: + query.cursor.close() + + +class ValuesIterable(BaseIterable): + """ + Iterable returned by QuerySet.values() that yields a dict for each row. + """ + + def __iter__(self): + queryset = self.queryset + query = queryset.query + compiler = query.get_compiler(queryset.db) + + # extra(select=...) cols are always at the start of the row. + names = [ + *query.extra_select, + *query.values_select, + *query.annotation_select, + ] + indexes = range(len(names)) + for row in compiler.results_iter( + chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size + ): + yield {names[i]: row[i] for i in indexes} + + +class ValuesListIterable(BaseIterable): + """ + Iterable returned by QuerySet.values_list(flat=False) that yields a tuple + for each row. + """ + + def __iter__(self): + queryset = self.queryset + query = queryset.query + compiler = query.get_compiler(queryset.db) + + if queryset._fields: + # extra(select=...) cols are always at the start of the row. + names = [ + *query.extra_select, + *query.values_select, + *query.annotation_select, + ] + fields = [ + *queryset._fields, + *(f for f in query.annotation_select if f not in queryset._fields), + ] + if fields != names: + # Reorder according to fields. + index_map = {name: idx for idx, name in enumerate(names)} + rowfactory = operator.itemgetter(*[index_map[f] for f in fields]) + return map( + rowfactory, + compiler.results_iter( + chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size + ), + ) + return compiler.results_iter( + tuple_expected=True, + chunked_fetch=self.chunked_fetch, + chunk_size=self.chunk_size, + ) + + +class NamedValuesListIterable(ValuesListIterable): + """ + Iterable returned by QuerySet.values_list(named=True) that yields a + namedtuple for each row. + """ + + def __iter__(self): + queryset = self.queryset + if queryset._fields: + names = queryset._fields + else: + query = queryset.query + names = [ + *query.extra_select, + *query.values_select, + *query.annotation_select, + ] + tuple_class = create_namedtuple_class(*names) + new = tuple.__new__ + for row in super().__iter__(): + yield new(tuple_class, row) + + +class FlatValuesListIterable(BaseIterable): + """ + Iterable returned by QuerySet.values_list(flat=True) that yields single + values. + """ + + def __iter__(self): + queryset = self.queryset + compiler = queryset.query.get_compiler(queryset.db) + for row in compiler.results_iter( + chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size + ): + yield row[0] + + +class QuerySet(AltersData): + """Represent a lazy database lookup for a set of objects.""" + + def __init__(self, model=None, query=None, using=None, hints=None): + self.model = model + self._db = using + self._hints = hints or {} + self._query = query or sql.Query(self.model) + self._result_cache = None + self._sticky_filter = False + self._for_write = False + self._prefetch_related_lookups = () + self._prefetch_done = False + self._known_related_objects = {} # {rel_field: {pk: rel_obj}} + self._iterable_class = ModelIterable + self._fields = None + self._defer_next_filter = False + self._deferred_filter = None + + @property + def query(self): + if self._deferred_filter: + negate, args, kwargs = self._deferred_filter + self._filter_or_exclude_inplace(negate, args, kwargs) + self._deferred_filter = None + return self._query + + @query.setter + def query(self, value): + if value.values_select: + self._iterable_class = ValuesIterable + self._query = value + + def as_manager(cls): + # Address the circular dependency between `Queryset` and `Manager`. + from django.db.models.manager import Manager + + manager = Manager.from_queryset(cls)() + manager._built_with_as_manager = True + return manager + + as_manager.queryset_only = True + as_manager = classmethod(as_manager) + + ######################## + # PYTHON MAGIC METHODS # + ######################## + + def __deepcopy__(self, memo): + """Don't populate the QuerySet's cache.""" + obj = self.__class__() + for k, v in self.__dict__.items(): + if k == "_result_cache": + obj.__dict__[k] = None + else: + obj.__dict__[k] = copy.deepcopy(v, memo) + return obj + + def __getstate__(self): + # Force the cache to be fully populated. + self._fetch_all() + return {**self.__dict__, DJANGO_VERSION_PICKLE_KEY: django.__version__} + + def __setstate__(self, state): + pickled_version = state.get(DJANGO_VERSION_PICKLE_KEY) + if pickled_version: + if pickled_version != django.__version__: + warnings.warn( + "Pickled queryset instance's Django version %s does not " + "match the current version %s." + % (pickled_version, django.__version__), + RuntimeWarning, + stacklevel=2, + ) + else: + warnings.warn( + "Pickled queryset instance's Django version is not specified.", + RuntimeWarning, + stacklevel=2, + ) + self.__dict__.update(state) + + def __repr__(self): + data = list(self[: REPR_OUTPUT_SIZE + 1]) + if len(data) > REPR_OUTPUT_SIZE: + data[-1] = "...(remaining elements truncated)..." + return "<%s %r>" % (self.__class__.__name__, data) + + def __len__(self): + self._fetch_all() + return len(self._result_cache) + + def __iter__(self): + """ + The queryset iterator protocol uses three nested iterators in the + default case: + 1. sql.compiler.execute_sql() + - Returns 100 rows at time (constants.GET_ITERATOR_CHUNK_SIZE) + using cursor.fetchmany(). This part is responsible for + doing some column masking, and returning the rows in chunks. + 2. sql.compiler.results_iter() + - Returns one row at time. At this point the rows are still just + tuples. In some cases the return values are converted to + Python values at this location. + 3. self.iterator() + - Responsible for turning the rows into model objects. + """ + self._fetch_all() + return iter(self._result_cache) + + def __aiter__(self): + # Remember, __aiter__ itself is synchronous, it's the thing it returns + # that is async! + async def generator(): + await sync_to_async(self._fetch_all)() + for item in self._result_cache: + yield item + + return generator() + + def __bool__(self): + self._fetch_all() + return bool(self._result_cache) + + def __getitem__(self, k): + """Retrieve an item or slice from the set of results.""" + if not isinstance(k, (int, slice)): + raise TypeError( + "QuerySet indices must be integers or slices, not %s." + % type(k).__name__ + ) + if (isinstance(k, int) and k < 0) or ( + isinstance(k, slice) + and ( + (k.start is not None and k.start < 0) + or (k.stop is not None and k.stop < 0) + ) + ): + raise ValueError("Negative indexing is not supported.") + + if self._result_cache is not None: + return self._result_cache[k] + + if isinstance(k, slice): + qs = self._chain() + if k.start is not None: + start = int(k.start) + else: + start = None + if k.stop is not None: + stop = int(k.stop) + else: + stop = None + qs.query.set_limits(start, stop) + return list(qs)[:: k.step] if k.step else qs + + qs = self._chain() + qs.query.set_limits(k, k + 1) + qs._fetch_all() + return qs._result_cache[0] + + def __class_getitem__(cls, *args, **kwargs): + return cls + + def __and__(self, other): + self._check_operator_queryset(other, "&") + self._merge_sanity_check(other) + if isinstance(other, EmptyQuerySet): + return other + if isinstance(self, EmptyQuerySet): + return self + combined = self._chain() + combined._merge_known_related_objects(other) + combined.query.combine(other.query, sql.AND) + return combined + + def __or__(self, other): + self._check_operator_queryset(other, "|") + self._merge_sanity_check(other) + if isinstance(self, EmptyQuerySet): + return other + if isinstance(other, EmptyQuerySet): + return self + query = ( + self + if self.query.can_filter() + else self.model._base_manager.filter(pk__in=self.values("pk")) + ) + combined = query._chain() + combined._merge_known_related_objects(other) + if not other.query.can_filter(): + other = other.model._base_manager.filter(pk__in=other.values("pk")) + combined.query.combine(other.query, sql.OR) + return combined + + def __xor__(self, other): + self._check_operator_queryset(other, "^") + self._merge_sanity_check(other) + if isinstance(self, EmptyQuerySet): + return other + if isinstance(other, EmptyQuerySet): + return self + query = ( + self + if self.query.can_filter() + else self.model._base_manager.filter(pk__in=self.values("pk")) + ) + combined = query._chain() + combined._merge_known_related_objects(other) + if not other.query.can_filter(): + other = other.model._base_manager.filter(pk__in=other.values("pk")) + combined.query.combine(other.query, sql.XOR) + return combined + + #################################### + # METHODS THAT DO DATABASE QUERIES # + #################################### + + def _iterator(self, use_chunked_fetch, chunk_size): + iterable = self._iterable_class( + self, + chunked_fetch=use_chunked_fetch, + chunk_size=chunk_size or 2000, + ) + if not self._prefetch_related_lookups or chunk_size is None: + yield from iterable + return + + iterator = iter(iterable) + while results := list(islice(iterator, chunk_size)): + prefetch_related_objects(results, *self._prefetch_related_lookups) + yield from results + + def iterator(self, chunk_size=None): + """ + An iterator over the results from applying this QuerySet to the + database. chunk_size must be provided for QuerySets that prefetch + related objects. Otherwise, a default chunk_size of 2000 is supplied. + """ + if chunk_size is None: + if self._prefetch_related_lookups: + # When the deprecation ends, replace with: + # raise ValueError( + # 'chunk_size must be provided when using ' + # 'QuerySet.iterator() after prefetch_related().' + # ) + warnings.warn( + "Using QuerySet.iterator() after prefetch_related() " + "without specifying chunk_size is deprecated.", + category=RemovedInDjango50Warning, + stacklevel=2, + ) + elif chunk_size <= 0: + raise ValueError("Chunk size must be strictly positive.") + use_chunked_fetch = not connections[self.db].settings_dict.get( + "DISABLE_SERVER_SIDE_CURSORS" + ) + return self._iterator(use_chunked_fetch, chunk_size) + + async def aiterator(self, chunk_size=2000): + """ + An asynchronous iterator over the results from applying this QuerySet + to the database. + """ + if self._prefetch_related_lookups: + raise NotSupportedError( + "Using QuerySet.aiterator() after prefetch_related() is not supported." + ) + if chunk_size <= 0: + raise ValueError("Chunk size must be strictly positive.") + use_chunked_fetch = not connections[self.db].settings_dict.get( + "DISABLE_SERVER_SIDE_CURSORS" + ) + async for item in self._iterable_class( + self, chunked_fetch=use_chunked_fetch, chunk_size=chunk_size + ): + yield item + + def aggregate(self, *args, **kwargs): + """ + Return a dictionary containing the calculations (aggregation) + over the current queryset. + + If args is present the expression is passed as a kwarg using + the Aggregate object's default alias. + """ + if self.query.distinct_fields: + raise NotImplementedError("aggregate() + distinct(fields) not implemented.") + self._validate_values_are_expressions( + (*args, *kwargs.values()), method_name="aggregate" + ) + for arg in args: + # The default_alias property raises TypeError if default_alias + # can't be set automatically or AttributeError if it isn't an + # attribute. + try: + arg.default_alias + except (AttributeError, TypeError): + raise TypeError("Complex aggregates require an alias") + kwargs[arg.default_alias] = arg + + return self.query.chain().get_aggregation(self.db, kwargs) + + async def aaggregate(self, *args, **kwargs): + return await sync_to_async(self.aggregate)(*args, **kwargs) + + def count(self): + """ + Perform a SELECT COUNT() and return the number of records as an + integer. + + If the QuerySet is already fully cached, return the length of the + cached results set to avoid multiple SELECT COUNT(*) calls. + """ + if self._result_cache is not None: + return len(self._result_cache) + + return self.query.get_count(using=self.db) + + async def acount(self): + return await sync_to_async(self.count)() + + def get(self, *args, **kwargs): + """ + Perform the query and return a single object matching the given + keyword arguments. + """ + if self.query.combinator and (args or kwargs): + raise NotSupportedError( + "Calling QuerySet.get(...) with filters after %s() is not " + "supported." % self.query.combinator + ) + clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs) + if self.query.can_filter() and not self.query.distinct_fields: + clone = clone.order_by() + limit = None + if ( + not clone.query.select_for_update + or connections[clone.db].features.supports_select_for_update_with_limit + ): + limit = MAX_GET_RESULTS + clone.query.set_limits(high=limit) + num = len(clone) + if num == 1: + return clone._result_cache[0] + if not num: + raise self.model.DoesNotExist( + "%s matching query does not exist." % self.model._meta.object_name + ) + raise self.model.MultipleObjectsReturned( + "get() returned more than one %s -- it returned %s!" + % ( + self.model._meta.object_name, + num if not limit or num < limit else "more than %s" % (limit - 1), + ) + ) + + async def aget(self, *args, **kwargs): + return await sync_to_async(self.get)(*args, **kwargs) + + def create(self, **kwargs): + """ + Create a new object with the given kwargs, saving it to the database + and returning the created object. + """ + obj = self.model(**kwargs) + self._for_write = True + obj.save(force_insert=True, using=self.db) + return obj + + async def acreate(self, **kwargs): + return await sync_to_async(self.create)(**kwargs) + + def _prepare_for_bulk_create(self, objs): + for obj in objs: + if obj.pk is None: + # Populate new PK values. + obj.pk = obj._meta.pk.get_pk_value_on_save(obj) + obj._prepare_related_fields_for_save(operation_name="bulk_create") + + def _check_bulk_create_options( + self, ignore_conflicts, update_conflicts, update_fields, unique_fields + ): + if ignore_conflicts and update_conflicts: + raise ValueError( + "ignore_conflicts and update_conflicts are mutually exclusive." + ) + db_features = connections[self.db].features + if ignore_conflicts: + if not db_features.supports_ignore_conflicts: + raise NotSupportedError( + "This database backend does not support ignoring conflicts." + ) + return OnConflict.IGNORE + elif update_conflicts: + if not db_features.supports_update_conflicts: + raise NotSupportedError( + "This database backend does not support updating conflicts." + ) + if not update_fields: + raise ValueError( + "Fields that will be updated when a row insertion fails " + "on conflicts must be provided." + ) + if unique_fields and not db_features.supports_update_conflicts_with_target: + raise NotSupportedError( + "This database backend does not support updating " + "conflicts with specifying unique fields that can trigger " + "the upsert." + ) + if not unique_fields and db_features.supports_update_conflicts_with_target: + raise ValueError( + "Unique fields that can trigger the upsert must be provided." + ) + # Updating primary keys and non-concrete fields is forbidden. + if any(not f.concrete or f.many_to_many for f in update_fields): + raise ValueError( + "bulk_create() can only be used with concrete fields in " + "update_fields." + ) + if any(f.primary_key for f in update_fields): + raise ValueError( + "bulk_create() cannot be used with primary keys in " + "update_fields." + ) + if unique_fields: + if any(not f.concrete or f.many_to_many for f in unique_fields): + raise ValueError( + "bulk_create() can only be used with concrete fields " + "in unique_fields." + ) + return OnConflict.UPDATE + return None + + def bulk_create( + self, + objs, + batch_size=None, + ignore_conflicts=False, + update_conflicts=False, + update_fields=None, + unique_fields=None, + ): + """ + Insert each of the instances into the database. Do *not* call + save() on each of the instances, do not send any pre/post_save + signals, and do not set the primary key attribute if it is an + autoincrement field (except if features.can_return_rows_from_bulk_insert=True). + Multi-table models are not supported. + """ + # When you bulk insert you don't get the primary keys back (if it's an + # autoincrement, except if can_return_rows_from_bulk_insert=True), so + # you can't insert into the child tables which references this. There + # are two workarounds: + # 1) This could be implemented if you didn't have an autoincrement pk + # 2) You could do it by doing O(n) normal inserts into the parent + # tables to get the primary keys back and then doing a single bulk + # insert into the childmost table. + # We currently set the primary keys on the objects when using + # PostgreSQL via the RETURNING ID clause. It should be possible for + # Oracle as well, but the semantics for extracting the primary keys is + # trickier so it's not done yet. + if batch_size is not None and batch_size <= 0: + raise ValueError("Batch size must be a positive integer.") + # Check that the parents share the same concrete model with the our + # model to detect the inheritance pattern ConcreteGrandParent -> + # MultiTableParent -> ProxyChild. Simply checking self.model._meta.proxy + # would not identify that case as involving multiple tables. + for parent in self.model._meta.get_parent_list(): + if parent._meta.concrete_model is not self.model._meta.concrete_model: + raise ValueError("Can't bulk create a multi-table inherited model") + if not objs: + return objs + opts = self.model._meta + if unique_fields: + # Primary key is allowed in unique_fields. + unique_fields = [ + self.model._meta.get_field(opts.pk.name if name == "pk" else name) + for name in unique_fields + ] + if update_fields: + update_fields = [self.model._meta.get_field(name) for name in update_fields] + on_conflict = self._check_bulk_create_options( + ignore_conflicts, + update_conflicts, + update_fields, + unique_fields, + ) + self._for_write = True + fields = opts.concrete_fields + objs = list(objs) + self._prepare_for_bulk_create(objs) + with transaction.atomic(using=self.db, savepoint=False): + objs_with_pk, objs_without_pk = partition(lambda o: o.pk is None, objs) + if objs_with_pk: + returned_columns = self._batched_insert( + objs_with_pk, + fields, + batch_size, + on_conflict=on_conflict, + update_fields=update_fields, + unique_fields=unique_fields, + ) + for obj_with_pk, results in zip(objs_with_pk, returned_columns): + for result, field in zip(results, opts.db_returning_fields): + if field != opts.pk: + setattr(obj_with_pk, field.attname, result) + for obj_with_pk in objs_with_pk: + obj_with_pk._state.adding = False + obj_with_pk._state.db = self.db + if objs_without_pk: + fields = [f for f in fields if not isinstance(f, AutoField)] + returned_columns = self._batched_insert( + objs_without_pk, + fields, + batch_size, + on_conflict=on_conflict, + update_fields=update_fields, + unique_fields=unique_fields, + ) + connection = connections[self.db] + if ( + connection.features.can_return_rows_from_bulk_insert + and on_conflict is None + ): + assert len(returned_columns) == len(objs_without_pk) + for obj_without_pk, results in zip(objs_without_pk, returned_columns): + for result, field in zip(results, opts.db_returning_fields): + setattr(obj_without_pk, field.attname, result) + obj_without_pk._state.adding = False + obj_without_pk._state.db = self.db + + return objs + + async def abulk_create( + self, + objs, + batch_size=None, + ignore_conflicts=False, + update_conflicts=False, + update_fields=None, + unique_fields=None, + ): + return await sync_to_async(self.bulk_create)( + objs=objs, + batch_size=batch_size, + ignore_conflicts=ignore_conflicts, + update_conflicts=update_conflicts, + update_fields=update_fields, + unique_fields=unique_fields, + ) + + def bulk_update(self, objs, fields, batch_size=None): + """ + Update the given fields in each of the given objects in the database. + """ + if batch_size is not None and batch_size <= 0: + raise ValueError("Batch size must be a positive integer.") + if not fields: + raise ValueError("Field names must be given to bulk_update().") + objs = tuple(objs) + if any(obj.pk is None for obj in objs): + raise ValueError("All bulk_update() objects must have a primary key set.") + fields = [self.model._meta.get_field(name) for name in fields] + if any(not f.concrete or f.many_to_many for f in fields): + raise ValueError("bulk_update() can only be used with concrete fields.") + if any(f.primary_key for f in fields): + raise ValueError("bulk_update() cannot be used with primary key fields.") + if not objs: + return 0 + for obj in objs: + obj._prepare_related_fields_for_save( + operation_name="bulk_update", fields=fields + ) + # PK is used twice in the resulting update query, once in the filter + # and once in the WHEN. Each field will also have one CAST. + self._for_write = True + connection = connections[self.db] + max_batch_size = connection.ops.bulk_batch_size(["pk", "pk"] + fields, objs) + batch_size = min(batch_size, max_batch_size) if batch_size else max_batch_size + requires_casting = connection.features.requires_casted_case_in_updates + batches = (objs[i : i + batch_size] for i in range(0, len(objs), batch_size)) + updates = [] + for batch_objs in batches: + update_kwargs = {} + for field in fields: + when_statements = [] + for obj in batch_objs: + attr = getattr(obj, field.attname) + if not hasattr(attr, "resolve_expression"): + attr = Value(attr, output_field=field) + when_statements.append(When(pk=obj.pk, then=attr)) + case_statement = Case(*when_statements, output_field=field) + if requires_casting: + case_statement = Cast(case_statement, output_field=field) + update_kwargs[field.attname] = case_statement + updates.append(([obj.pk for obj in batch_objs], update_kwargs)) + rows_updated = 0 + queryset = self.using(self.db) + with transaction.atomic(using=self.db, savepoint=False): + for pks, update_kwargs in updates: + rows_updated += queryset.filter(pk__in=pks).update(**update_kwargs) + return rows_updated + + bulk_update.alters_data = True + + async def abulk_update(self, objs, fields, batch_size=None): + return await sync_to_async(self.bulk_update)( + objs=objs, + fields=fields, + batch_size=batch_size, + ) + + abulk_update.alters_data = True + + def get_or_create(self, defaults=None, **kwargs): + """ + Look up an object with the given kwargs, creating one if necessary. + Return a tuple of (object, created), where created is a boolean + specifying whether an object was created. + """ + # The get() needs to be targeted at the write database in order + # to avoid potential transaction consistency problems. + self._for_write = True + try: + return self.get(**kwargs), False + except self.model.DoesNotExist: + params = self._extract_model_params(defaults, **kwargs) + # Try to create an object using passed params. + try: + with transaction.atomic(using=self.db): + params = dict(resolve_callables(params)) + return self.create(**params), True + except IntegrityError: + try: + return self.get(**kwargs), False + except self.model.DoesNotExist: + pass + raise + + async def aget_or_create(self, defaults=None, **kwargs): + return await sync_to_async(self.get_or_create)( + defaults=defaults, + **kwargs, + ) + + def update_or_create(self, defaults=None, **kwargs): + """ + Look up an object with the given kwargs, updating one with defaults + if it exists, otherwise create a new one. + Return a tuple (object, created), where created is a boolean + specifying whether an object was created. + """ + defaults = defaults or {} + self._for_write = True + with transaction.atomic(using=self.db): + # Lock the row so that a concurrent update is blocked until + # update_or_create() has performed its save. + obj, created = self.select_for_update().get_or_create(defaults, **kwargs) + if created: + return obj, created + for k, v in resolve_callables(defaults): + setattr(obj, k, v) + + update_fields = set(defaults) + concrete_field_names = self.model._meta._non_pk_concrete_field_names + # update_fields does not support non-concrete fields. + if concrete_field_names.issuperset(update_fields): + # Add fields which are set on pre_save(), e.g. auto_now fields. + # This is to maintain backward compatibility as these fields + # are not updated unless explicitly specified in the + # update_fields list. + for field in self.model._meta.local_concrete_fields: + if not ( + field.primary_key or field.__class__.pre_save is Field.pre_save + ): + update_fields.add(field.name) + if field.name != field.attname: + update_fields.add(field.attname) + obj.save(using=self.db, update_fields=update_fields) + else: + obj.save(using=self.db) + return obj, False + + async def aupdate_or_create(self, defaults=None, **kwargs): + return await sync_to_async(self.update_or_create)( + defaults=defaults, + **kwargs, + ) + + def _extract_model_params(self, defaults, **kwargs): + """ + Prepare `params` for creating a model instance based on the given + kwargs; for use by get_or_create(). + """ + defaults = defaults or {} + params = {k: v for k, v in kwargs.items() if LOOKUP_SEP not in k} + params.update(defaults) + property_names = self.model._meta._property_names + invalid_params = [] + for param in params: + try: + self.model._meta.get_field(param) + except exceptions.FieldDoesNotExist: + # It's okay to use a model's property if it has a setter. + if not (param in property_names and getattr(self.model, param).fset): + invalid_params.append(param) + if invalid_params: + raise exceptions.FieldError( + "Invalid field name(s) for model %s: '%s'." + % ( + self.model._meta.object_name, + "', '".join(sorted(invalid_params)), + ) + ) + return params + + def _earliest(self, *fields): + """ + Return the earliest object according to fields (if given) or by the + model's Meta.get_latest_by. + """ + if fields: + order_by = fields + else: + order_by = getattr(self.model._meta, "get_latest_by") + if order_by and not isinstance(order_by, (tuple, list)): + order_by = (order_by,) + if order_by is None: + raise ValueError( + "earliest() and latest() require either fields as positional " + "arguments or 'get_latest_by' in the model's Meta." + ) + obj = self._chain() + obj.query.set_limits(high=1) + obj.query.clear_ordering(force=True) + obj.query.add_ordering(*order_by) + return obj.get() + + def earliest(self, *fields): + if self.query.is_sliced: + raise TypeError("Cannot change a query once a slice has been taken.") + return self._earliest(*fields) + + async def aearliest(self, *fields): + return await sync_to_async(self.earliest)(*fields) + + def latest(self, *fields): + """ + Return the latest object according to fields (if given) or by the + model's Meta.get_latest_by. + """ + if self.query.is_sliced: + raise TypeError("Cannot change a query once a slice has been taken.") + return self.reverse()._earliest(*fields) + + async def alatest(self, *fields): + return await sync_to_async(self.latest)(*fields) + + def first(self): + """Return the first object of a query or None if no match is found.""" + if self.ordered: + queryset = self + else: + self._check_ordering_first_last_queryset_aggregation(method="first") + queryset = self.order_by("pk") + for obj in queryset[:1]: + return obj + + async def afirst(self): + return await sync_to_async(self.first)() + + def last(self): + """Return the last object of a query or None if no match is found.""" + if self.ordered: + queryset = self.reverse() + else: + self._check_ordering_first_last_queryset_aggregation(method="last") + queryset = self.order_by("-pk") + for obj in queryset[:1]: + return obj + + async def alast(self): + return await sync_to_async(self.last)() + + def in_bulk(self, id_list=None, *, field_name="pk"): + """ + Return a dictionary mapping each of the given IDs to the object with + that ID. If `id_list` isn't provided, evaluate the entire QuerySet. + """ + if self.query.is_sliced: + raise TypeError("Cannot use 'limit' or 'offset' with in_bulk().") + opts = self.model._meta + unique_fields = [ + constraint.fields[0] + for constraint in opts.total_unique_constraints + if len(constraint.fields) == 1 + ] + if ( + field_name != "pk" + and not opts.get_field(field_name).unique + and field_name not in unique_fields + and self.query.distinct_fields != (field_name,) + ): + raise ValueError( + "in_bulk()'s field_name must be a unique field but %r isn't." + % field_name + ) + if id_list is not None: + if not id_list: + return {} + filter_key = "{}__in".format(field_name) + batch_size = connections[self.db].features.max_query_params + id_list = tuple(id_list) + # If the database has a limit on the number of query parameters + # (e.g. SQLite), retrieve objects in batches if necessary. + if batch_size and batch_size < len(id_list): + qs = () + for offset in range(0, len(id_list), batch_size): + batch = id_list[offset : offset + batch_size] + qs += tuple(self.filter(**{filter_key: batch}).order_by()) + else: + qs = self.filter(**{filter_key: id_list}).order_by() + else: + qs = self._chain() + return {getattr(obj, field_name): obj for obj in qs} + + async def ain_bulk(self, id_list=None, *, field_name="pk"): + return await sync_to_async(self.in_bulk)( + id_list=id_list, + field_name=field_name, + ) + + def delete(self): + """Delete the records in the current QuerySet.""" + self._not_support_combined_queries("delete") + if self.query.is_sliced: + raise TypeError("Cannot use 'limit' or 'offset' with delete().") + if self.query.distinct or self.query.distinct_fields: + raise TypeError("Cannot call delete() after .distinct().") + if self._fields is not None: + raise TypeError("Cannot call delete() after .values() or .values_list()") + + del_query = self._chain() + + # The delete is actually 2 queries - one to find related objects, + # and one to delete. Make sure that the discovery of related + # objects is performed on the same database as the deletion. + del_query._for_write = True + + # Disable non-supported fields. + del_query.query.select_for_update = False + del_query.query.select_related = False + del_query.query.clear_ordering(force=True) + + collector = Collector(using=del_query.db, origin=self) + collector.collect(del_query) + deleted, _rows_count = collector.delete() + + # Clear the result cache, in case this QuerySet gets reused. + self._result_cache = None + return deleted, _rows_count + + delete.alters_data = True + delete.queryset_only = True + + async def adelete(self): + return await sync_to_async(self.delete)() + + adelete.alters_data = True + adelete.queryset_only = True + + def _raw_delete(self, using): + """ + Delete objects found from the given queryset in single direct SQL + query. No signals are sent and there is no protection for cascades. + """ + query = self.query.clone() + query.__class__ = sql.DeleteQuery + cursor = query.get_compiler(using).execute_sql(CURSOR) + if cursor: + with cursor: + return cursor.rowcount + return 0 + + _raw_delete.alters_data = True + + def update(self, **kwargs): + """ + Update all elements in the current QuerySet, setting all the given + fields to the appropriate values. + """ + self._not_support_combined_queries("update") + if self.query.is_sliced: + raise TypeError("Cannot update a query once a slice has been taken.") + self._for_write = True + query = self.query.chain(sql.UpdateQuery) + query.add_update_values(kwargs) + + # Inline annotations in order_by(), if possible. + new_order_by = [] + for col in query.order_by: + if annotation := query.annotations.get(col): + if getattr(annotation, "contains_aggregate", False): + raise exceptions.FieldError( + f"Cannot update when ordering by an aggregate: {annotation}" + ) + new_order_by.append(annotation) + else: + new_order_by.append(col) + query.order_by = tuple(new_order_by) + + # Clear any annotations so that they won't be present in subqueries. + query.annotations = {} + with transaction.mark_for_rollback_on_error(using=self.db): + rows = query.get_compiler(self.db).execute_sql(CURSOR) + self._result_cache = None + return rows + + update.alters_data = True + + async def aupdate(self, **kwargs): + return await sync_to_async(self.update)(**kwargs) + + aupdate.alters_data = True + + def _update(self, values): + """ + A version of update() that accepts field objects instead of field names. + Used primarily for model saving and not intended for use by general + code (it requires too much poking around at model internals to be + useful at that level). + """ + if self.query.is_sliced: + raise TypeError("Cannot update a query once a slice has been taken.") + query = self.query.chain(sql.UpdateQuery) + query.add_update_fields(values) + # Clear any annotations so that they won't be present in subqueries. + query.annotations = {} + self._result_cache = None + return query.get_compiler(self.db).execute_sql(CURSOR) + + _update.alters_data = True + _update.queryset_only = False + + def exists(self): + """ + Return True if the QuerySet would have any results, False otherwise. + """ + if self._result_cache is None: + return self.query.has_results(using=self.db) + return bool(self._result_cache) + + async def aexists(self): + return await sync_to_async(self.exists)() + + def contains(self, obj): + """ + Return True if the QuerySet contains the provided obj, + False otherwise. + """ + self._not_support_combined_queries("contains") + if self._fields is not None: + raise TypeError( + "Cannot call QuerySet.contains() after .values() or .values_list()." + ) + try: + if obj._meta.concrete_model != self.model._meta.concrete_model: + return False + except AttributeError: + raise TypeError("'obj' must be a model instance.") + if obj.pk is None: + raise ValueError("QuerySet.contains() cannot be used on unsaved objects.") + if self._result_cache is not None: + return obj in self._result_cache + return self.filter(pk=obj.pk).exists() + + async def acontains(self, obj): + return await sync_to_async(self.contains)(obj=obj) + + def _prefetch_related_objects(self): + # This method can only be called once the result cache has been filled. + prefetch_related_objects(self._result_cache, *self._prefetch_related_lookups) + self._prefetch_done = True + + def explain(self, *, format=None, **options): + """ + Runs an EXPLAIN on the SQL query this QuerySet would perform, and + returns the results. + """ + return self.query.explain(using=self.db, format=format, **options) + + async def aexplain(self, *, format=None, **options): + return await sync_to_async(self.explain)(format=format, **options) + + ################################################## + # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS # + ################################################## + + def raw(self, raw_query, params=(), translations=None, using=None): + if using is None: + using = self.db + qs = RawQuerySet( + raw_query, + model=self.model, + params=params, + translations=translations, + using=using, + ) + qs._prefetch_related_lookups = self._prefetch_related_lookups[:] + return qs + + def _values(self, *fields, **expressions): + clone = self._chain() + if expressions: + clone = clone.annotate(**expressions) + clone._fields = fields + clone.query.set_values(fields) + return clone + + def values(self, *fields, **expressions): + fields += tuple(expressions) + clone = self._values(*fields, **expressions) + clone._iterable_class = ValuesIterable + return clone + + def values_list(self, *fields, flat=False, named=False): + if flat and named: + raise TypeError("'flat' and 'named' can't be used together.") + if flat and len(fields) > 1: + raise TypeError( + "'flat' is not valid when values_list is called with more than one " + "field." + ) + + field_names = {f for f in fields if not hasattr(f, "resolve_expression")} + _fields = [] + expressions = {} + counter = 1 + for field in fields: + if hasattr(field, "resolve_expression"): + field_id_prefix = getattr( + field, "default_alias", field.__class__.__name__.lower() + ) + while True: + field_id = field_id_prefix + str(counter) + counter += 1 + if field_id not in field_names: + break + expressions[field_id] = field + _fields.append(field_id) + else: + _fields.append(field) + + clone = self._values(*_fields, **expressions) + clone._iterable_class = ( + NamedValuesListIterable + if named + else FlatValuesListIterable + if flat + else ValuesListIterable + ) + return clone + + def dates(self, field_name, kind, order="ASC"): + """ + Return a list of date objects representing all available dates for + the given field_name, scoped to 'kind'. + """ + if kind not in ("year", "month", "week", "day"): + raise ValueError("'kind' must be one of 'year', 'month', 'week', or 'day'.") + if order not in ("ASC", "DESC"): + raise ValueError("'order' must be either 'ASC' or 'DESC'.") + return ( + self.annotate( + datefield=Trunc(field_name, kind, output_field=DateField()), + plain_field=F(field_name), + ) + .values_list("datefield", flat=True) + .distinct() + .filter(plain_field__isnull=False) + .order_by(("-" if order == "DESC" else "") + "datefield") + ) + + # RemovedInDjango50Warning: when the deprecation ends, remove is_dst + # argument. + def datetimes( + self, field_name, kind, order="ASC", tzinfo=None, is_dst=timezone.NOT_PASSED + ): + """ + Return a list of datetime objects representing all available + datetimes for the given field_name, scoped to 'kind'. + """ + if kind not in ("year", "month", "week", "day", "hour", "minute", "second"): + raise ValueError( + "'kind' must be one of 'year', 'month', 'week', 'day', " + "'hour', 'minute', or 'second'." + ) + if order not in ("ASC", "DESC"): + raise ValueError("'order' must be either 'ASC' or 'DESC'.") + if settings.USE_TZ: + if tzinfo is None: + tzinfo = timezone.get_current_timezone() + else: + tzinfo = None + return ( + self.annotate( + datetimefield=Trunc( + field_name, + kind, + output_field=DateTimeField(), + tzinfo=tzinfo, + is_dst=is_dst, + ), + plain_field=F(field_name), + ) + .values_list("datetimefield", flat=True) + .distinct() + .filter(plain_field__isnull=False) + .order_by(("-" if order == "DESC" else "") + "datetimefield") + ) + + def none(self): + """Return an empty QuerySet.""" + clone = self._chain() + clone.query.set_empty() + return clone + + ################################################################## + # PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET # + ################################################################## + + def all(self): + """ + Return a new QuerySet that is a copy of the current one. This allows a + QuerySet to proxy for a model manager in some cases. + """ + return self._chain() + + def filter(self, *args, **kwargs): + """ + Return a new QuerySet instance with the args ANDed to the existing + set. + """ + self._not_support_combined_queries("filter") + return self._filter_or_exclude(False, args, kwargs) + + def exclude(self, *args, **kwargs): + """ + Return a new QuerySet instance with NOT (args) ANDed to the existing + set. + """ + self._not_support_combined_queries("exclude") + return self._filter_or_exclude(True, args, kwargs) + + def _filter_or_exclude(self, negate, args, kwargs): + if (args or kwargs) and self.query.is_sliced: + raise TypeError("Cannot filter a query once a slice has been taken.") + clone = self._chain() + if self._defer_next_filter: + self._defer_next_filter = False + clone._deferred_filter = negate, args, kwargs + else: + clone._filter_or_exclude_inplace(negate, args, kwargs) + return clone + + def _filter_or_exclude_inplace(self, negate, args, kwargs): + if negate: + self._query.add_q(~Q(*args, **kwargs)) + else: + self._query.add_q(Q(*args, **kwargs)) + + def complex_filter(self, filter_obj): + """ + Return a new QuerySet instance with filter_obj added to the filters. + + filter_obj can be a Q object or a dictionary of keyword lookup + arguments. + + This exists to support framework features such as 'limit_choices_to', + and usually it will be more natural to use other methods. + """ + if isinstance(filter_obj, Q): + clone = self._chain() + clone.query.add_q(filter_obj) + return clone + else: + return self._filter_or_exclude(False, args=(), kwargs=filter_obj) + + def _combinator_query(self, combinator, *other_qs, all=False): + # Clone the query to inherit the select list and everything + clone = self._chain() + # Clear limits and ordering so they can be reapplied + clone.query.clear_ordering(force=True) + clone.query.clear_limits() + clone.query.combined_queries = (self.query,) + tuple( + qs.query for qs in other_qs + ) + clone.query.combinator = combinator + clone.query.combinator_all = all + return clone + + def union(self, *other_qs, all=False): + # If the query is an EmptyQuerySet, combine all nonempty querysets. + if isinstance(self, EmptyQuerySet): + qs = [q for q in other_qs if not isinstance(q, EmptyQuerySet)] + if not qs: + return self + if len(qs) == 1: + return qs[0] + return qs[0]._combinator_query("union", *qs[1:], all=all) + return self._combinator_query("union", *other_qs, all=all) + + def intersection(self, *other_qs): + # If any query is an EmptyQuerySet, return it. + if isinstance(self, EmptyQuerySet): + return self + for other in other_qs: + if isinstance(other, EmptyQuerySet): + return other + return self._combinator_query("intersection", *other_qs) + + def difference(self, *other_qs): + # If the query is an EmptyQuerySet, return it. + if isinstance(self, EmptyQuerySet): + return self + return self._combinator_query("difference", *other_qs) + + def select_for_update(self, nowait=False, skip_locked=False, of=(), no_key=False): + """ + Return a new QuerySet instance that will select objects with a + FOR UPDATE lock. + """ + if nowait and skip_locked: + raise ValueError("The nowait option cannot be used with skip_locked.") + obj = self._chain() + obj._for_write = True + obj.query.select_for_update = True + obj.query.select_for_update_nowait = nowait + obj.query.select_for_update_skip_locked = skip_locked + obj.query.select_for_update_of = of + obj.query.select_for_no_key_update = no_key + return obj + + def select_related(self, *fields): + """ + Return a new QuerySet instance that will select related objects. + + If fields are specified, they must be ForeignKey fields and only those + related objects are included in the selection. + + If select_related(None) is called, clear the list. + """ + self._not_support_combined_queries("select_related") + if self._fields is not None: + raise TypeError( + "Cannot call select_related() after .values() or .values_list()" + ) + + obj = self._chain() + if fields == (None,): + obj.query.select_related = False + elif fields: + obj.query.add_select_related(fields) + else: + obj.query.select_related = True + return obj + + def prefetch_related(self, *lookups): + """ + Return a new QuerySet instance that will prefetch the specified + Many-To-One and Many-To-Many related objects when the QuerySet is + evaluated. + + When prefetch_related() is called more than once, append to the list of + prefetch lookups. If prefetch_related(None) is called, clear the list. + """ + self._not_support_combined_queries("prefetch_related") + clone = self._chain() + if lookups == (None,): + clone._prefetch_related_lookups = () + else: + for lookup in lookups: + if isinstance(lookup, Prefetch): + lookup = lookup.prefetch_to + lookup = lookup.split(LOOKUP_SEP, 1)[0] + if lookup in self.query._filtered_relations: + raise ValueError( + "prefetch_related() is not supported with FilteredRelation." + ) + clone._prefetch_related_lookups = clone._prefetch_related_lookups + lookups + return clone + + def annotate(self, *args, **kwargs): + """ + Return a query set in which the returned objects have been annotated + with extra data or aggregations. + """ + self._not_support_combined_queries("annotate") + return self._annotate(args, kwargs, select=True) + + def alias(self, *args, **kwargs): + """ + Return a query set with added aliases for extra data or aggregations. + """ + self._not_support_combined_queries("alias") + return self._annotate(args, kwargs, select=False) + + def _annotate(self, args, kwargs, select=True): + self._validate_values_are_expressions( + args + tuple(kwargs.values()), method_name="annotate" + ) + annotations = {} + for arg in args: + # The default_alias property may raise a TypeError. + try: + if arg.default_alias in kwargs: + raise ValueError( + "The named annotation '%s' conflicts with the " + "default name for another annotation." % arg.default_alias + ) + except TypeError: + raise TypeError("Complex annotations require an alias") + annotations[arg.default_alias] = arg + annotations.update(kwargs) + + clone = self._chain() + names = self._fields + if names is None: + names = set( + chain.from_iterable( + (field.name, field.attname) + if hasattr(field, "attname") + else (field.name,) + for field in self.model._meta.get_fields() + ) + ) + + for alias, annotation in annotations.items(): + if alias in names: + raise ValueError( + "The annotation '%s' conflicts with a field on " + "the model." % alias + ) + if isinstance(annotation, FilteredRelation): + clone.query.add_filtered_relation(annotation, alias) + else: + clone.query.add_annotation( + annotation, + alias, + select=select, + ) + for alias, annotation in clone.query.annotations.items(): + if alias in annotations and annotation.contains_aggregate: + if clone._fields is None: + clone.query.group_by = True + else: + clone.query.set_group_by() + break + + return clone + + def order_by(self, *field_names): + """Return a new QuerySet instance with the ordering changed.""" + if self.query.is_sliced: + raise TypeError("Cannot reorder a query once a slice has been taken.") + obj = self._chain() + obj.query.clear_ordering(force=True, clear_default=False) + obj.query.add_ordering(*field_names) + return obj + + def distinct(self, *field_names): + """ + Return a new QuerySet instance that will select only distinct results. + """ + self._not_support_combined_queries("distinct") + if self.query.is_sliced: + raise TypeError( + "Cannot create distinct fields once a slice has been taken." + ) + obj = self._chain() + obj.query.add_distinct_fields(*field_names) + return obj + + def extra( + self, + select=None, + where=None, + params=None, + tables=None, + order_by=None, + select_params=None, + ): + """Add extra SQL fragments to the query.""" + self._not_support_combined_queries("extra") + if self.query.is_sliced: + raise TypeError("Cannot change a query once a slice has been taken.") + clone = self._chain() + clone.query.add_extra(select, select_params, where, params, tables, order_by) + return clone + + def reverse(self): + """Reverse the ordering of the QuerySet.""" + if self.query.is_sliced: + raise TypeError("Cannot reverse a query once a slice has been taken.") + clone = self._chain() + clone.query.standard_ordering = not clone.query.standard_ordering + return clone + + def defer(self, *fields): + """ + Defer the loading of data for certain fields until they are accessed. + Add the set of deferred fields to any existing set of deferred fields. + The only exception to this is if None is passed in as the only + parameter, in which case removal all deferrals. + """ + self._not_support_combined_queries("defer") + if self._fields is not None: + raise TypeError("Cannot call defer() after .values() or .values_list()") + clone = self._chain() + if fields == (None,): + clone.query.clear_deferred_loading() + else: + clone.query.add_deferred_loading(fields) + return clone + + def only(self, *fields): + """ + Essentially, the opposite of defer(). Only the fields passed into this + method and that are not already specified as deferred are loaded + immediately when the queryset is evaluated. + """ + self._not_support_combined_queries("only") + if self._fields is not None: + raise TypeError("Cannot call only() after .values() or .values_list()") + if fields == (None,): + # Can only pass None to defer(), not only(), as the rest option. + # That won't stop people trying to do this, so let's be explicit. + raise TypeError("Cannot pass None as an argument to only().") + for field in fields: + field = field.split(LOOKUP_SEP, 1)[0] + if field in self.query._filtered_relations: + raise ValueError("only() is not supported with FilteredRelation.") + clone = self._chain() + clone.query.add_immediate_loading(fields) + return clone + + def using(self, alias): + """Select which database this QuerySet should execute against.""" + clone = self._chain() + clone._db = alias + return clone + + ################################### + # PUBLIC INTROSPECTION ATTRIBUTES # + ################################### + + @property + def ordered(self): + """ + Return True if the QuerySet is ordered -- i.e. has an order_by() + clause or a default ordering on the model (or is empty). + """ + if isinstance(self, EmptyQuerySet): + return True + if self.query.extra_order_by or self.query.order_by: + return True + elif ( + self.query.default_ordering + and self.query.get_meta().ordering + and + # A default ordering doesn't affect GROUP BY queries. + not self.query.group_by + ): + return True + else: + return False + + @property + def db(self): + """Return the database used if this query is executed now.""" + if self._for_write: + return self._db or router.db_for_write(self.model, **self._hints) + return self._db or router.db_for_read(self.model, **self._hints) + + ################### + # PRIVATE METHODS # + ################### + + def _insert( + self, + objs, + fields, + returning_fields=None, + raw=False, + using=None, + on_conflict=None, + update_fields=None, + unique_fields=None, + ): + """ + Insert a new record for the given model. This provides an interface to + the InsertQuery class and is how Model.save() is implemented. + """ + self._for_write = True + if using is None: + using = self.db + query = sql.InsertQuery( + self.model, + on_conflict=on_conflict, + update_fields=update_fields, + unique_fields=unique_fields, + ) + query.insert_values(fields, objs, raw=raw) + return query.get_compiler(using=using).execute_sql(returning_fields) + + _insert.alters_data = True + _insert.queryset_only = False + + def _batched_insert( + self, + objs, + fields, + batch_size, + on_conflict=None, + update_fields=None, + unique_fields=None, + ): + """ + Helper method for bulk_create() to insert objs one batch at a time. + """ + connection = connections[self.db] + ops = connection.ops + max_batch_size = max(ops.bulk_batch_size(fields, objs), 1) + batch_size = min(batch_size, max_batch_size) if batch_size else max_batch_size + inserted_rows = [] + bulk_return = connection.features.can_return_rows_from_bulk_insert + for item in [objs[i : i + batch_size] for i in range(0, len(objs), batch_size)]: + if bulk_return and on_conflict is None: + inserted_rows.extend( + self._insert( + item, + fields=fields, + using=self.db, + returning_fields=self.model._meta.db_returning_fields, + ) + ) + else: + self._insert( + item, + fields=fields, + using=self.db, + on_conflict=on_conflict, + update_fields=update_fields, + unique_fields=unique_fields, + ) + return inserted_rows + + def _chain(self): + """ + Return a copy of the current QuerySet that's ready for another + operation. + """ + obj = self._clone() + if obj._sticky_filter: + obj.query.filter_is_sticky = True + obj._sticky_filter = False + return obj + + def _clone(self): + """ + Return a copy of the current QuerySet. A lightweight alternative + to deepcopy(). + """ + c = self.__class__( + model=self.model, + query=self.query.chain(), + using=self._db, + hints=self._hints, + ) + c._sticky_filter = self._sticky_filter + c._for_write = self._for_write + c._prefetch_related_lookups = self._prefetch_related_lookups[:] + c._known_related_objects = self._known_related_objects + c._iterable_class = self._iterable_class + c._fields = self._fields + return c + + def _fetch_all(self): + if self._result_cache is None: + self._result_cache = list(self._iterable_class(self)) + if self._prefetch_related_lookups and not self._prefetch_done: + self._prefetch_related_objects() + + def _next_is_sticky(self): + """ + Indicate that the next filter call and the one following that should + be treated as a single filter. This is only important when it comes to + determining when to reuse tables for many-to-many filters. Required so + that we can filter naturally on the results of related managers. + + This doesn't return a clone of the current QuerySet (it returns + "self"). The method is only used internally and should be immediately + followed by a filter() that does create a clone. + """ + self._sticky_filter = True + return self + + def _merge_sanity_check(self, other): + """Check that two QuerySet classes may be merged.""" + if self._fields is not None and ( + set(self.query.values_select) != set(other.query.values_select) + or set(self.query.extra_select) != set(other.query.extra_select) + or set(self.query.annotation_select) != set(other.query.annotation_select) + ): + raise TypeError( + "Merging '%s' classes must involve the same values in each case." + % self.__class__.__name__ + ) + + def _merge_known_related_objects(self, other): + """ + Keep track of all known related objects from either QuerySet instance. + """ + for field, objects in other._known_related_objects.items(): + self._known_related_objects.setdefault(field, {}).update(objects) + + def resolve_expression(self, *args, **kwargs): + if self._fields and len(self._fields) > 1: + # values() queryset can only be used as nested queries + # if they are set up to select only a single field. + raise TypeError("Cannot use multi-field values as a filter value.") + query = self.query.resolve_expression(*args, **kwargs) + query._db = self._db + return query + + resolve_expression.queryset_only = True + + def _add_hints(self, **hints): + """ + Update hinting information for use by routers. Add new key/values or + overwrite existing key/values. + """ + self._hints.update(hints) + + def _has_filters(self): + """ + Check if this QuerySet has any filtering going on. This isn't + equivalent with checking if all objects are present in results, for + example, qs[1:]._has_filters() -> False. + """ + return self.query.has_filters() + + @staticmethod + def _validate_values_are_expressions(values, method_name): + invalid_args = sorted( + str(arg) for arg in values if not hasattr(arg, "resolve_expression") + ) + if invalid_args: + raise TypeError( + "QuerySet.%s() received non-expression(s): %s." + % ( + method_name, + ", ".join(invalid_args), + ) + ) + + def _not_support_combined_queries(self, operation_name): + if self.query.combinator: + raise NotSupportedError( + "Calling QuerySet.%s() after %s() is not supported." + % (operation_name, self.query.combinator) + ) + + def _check_operator_queryset(self, other, operator_): + if self.query.combinator or other.query.combinator: + raise TypeError(f"Cannot use {operator_} operator with combined queryset.") + + def _check_ordering_first_last_queryset_aggregation(self, method): + if isinstance(self.query.group_by, tuple) and not any( + col.output_field is self.model._meta.pk for col in self.query.group_by + ): + raise TypeError( + f"Cannot use QuerySet.{method}() on an unordered queryset performing " + f"aggregation. Add an ordering with order_by()." + ) + + +class InstanceCheckMeta(type): + def __instancecheck__(self, instance): + return isinstance(instance, QuerySet) and instance.query.is_empty() + + +class EmptyQuerySet(metaclass=InstanceCheckMeta): + """ + Marker class to checking if a queryset is empty by .none(): + isinstance(qs.none(), EmptyQuerySet) -> True + """ + + def __init__(self, *args, **kwargs): + raise TypeError("EmptyQuerySet can't be instantiated") + + +class RawQuerySet: + """ + Provide an iterator which converts the results of raw SQL queries into + annotated model instances. + """ + + def __init__( + self, + raw_query, + model=None, + query=None, + params=(), + translations=None, + using=None, + hints=None, + ): + self.raw_query = raw_query + self.model = model + self._db = using + self._hints = hints or {} + self.query = query or sql.RawQuery(sql=raw_query, using=self.db, params=params) + self.params = params + self.translations = translations or {} + self._result_cache = None + self._prefetch_related_lookups = () + self._prefetch_done = False + + def resolve_model_init_order(self): + """Resolve the init field names and value positions.""" + converter = connections[self.db].introspection.identifier_converter + model_init_fields = [ + f for f in self.model._meta.fields if converter(f.column) in self.columns + ] + annotation_fields = [ + (column, pos) + for pos, column in enumerate(self.columns) + if column not in self.model_fields + ] + model_init_order = [ + self.columns.index(converter(f.column)) for f in model_init_fields + ] + model_init_names = [f.attname for f in model_init_fields] + return model_init_names, model_init_order, annotation_fields + + def prefetch_related(self, *lookups): + """Same as QuerySet.prefetch_related()""" + clone = self._clone() + if lookups == (None,): + clone._prefetch_related_lookups = () + else: + clone._prefetch_related_lookups = clone._prefetch_related_lookups + lookups + return clone + + def _prefetch_related_objects(self): + prefetch_related_objects(self._result_cache, *self._prefetch_related_lookups) + self._prefetch_done = True + + def _clone(self): + """Same as QuerySet._clone()""" + c = self.__class__( + self.raw_query, + model=self.model, + query=self.query, + params=self.params, + translations=self.translations, + using=self._db, + hints=self._hints, + ) + c._prefetch_related_lookups = self._prefetch_related_lookups[:] + return c + + def _fetch_all(self): + if self._result_cache is None: + self._result_cache = list(self.iterator()) + if self._prefetch_related_lookups and not self._prefetch_done: + self._prefetch_related_objects() + + def __len__(self): + self._fetch_all() + return len(self._result_cache) + + def __bool__(self): + self._fetch_all() + return bool(self._result_cache) + + def __iter__(self): + self._fetch_all() + return iter(self._result_cache) + + def __aiter__(self): + # Remember, __aiter__ itself is synchronous, it's the thing it returns + # that is async! + async def generator(): + await sync_to_async(self._fetch_all)() + for item in self._result_cache: + yield item + + return generator() + + def iterator(self): + yield from RawModelIterable(self) + + def __repr__(self): + return "<%s: %s>" % (self.__class__.__name__, self.query) + + def __getitem__(self, k): + return list(self)[k] + + @property + def db(self): + """Return the database used if this query is executed now.""" + return self._db or router.db_for_read(self.model, **self._hints) + + def using(self, alias): + """Select the database this RawQuerySet should execute against.""" + return RawQuerySet( + self.raw_query, + model=self.model, + query=self.query.chain(using=alias), + params=self.params, + translations=self.translations, + using=alias, + ) + + @cached_property + def columns(self): + """ + A list of model field names in the order they'll appear in the + query results. + """ + columns = self.query.get_columns() + # Adjust any column names which don't match field names + for query_name, model_name in self.translations.items(): + # Ignore translations for nonexistent column names + try: + index = columns.index(query_name) + except ValueError: + pass + else: + columns[index] = model_name + return columns + + @cached_property + def model_fields(self): + """A dict mapping column names to model field names.""" + converter = connections[self.db].introspection.identifier_converter + model_fields = {} + for field in self.model._meta.fields: + name, column = field.get_attname_column() + model_fields[converter(column)] = field + return model_fields + + +class Prefetch: + def __init__(self, lookup, queryset=None, to_attr=None): + # `prefetch_through` is the path we traverse to perform the prefetch. + self.prefetch_through = lookup + # `prefetch_to` is the path to the attribute that stores the result. + self.prefetch_to = lookup + if queryset is not None and ( + isinstance(queryset, RawQuerySet) + or ( + hasattr(queryset, "_iterable_class") + and not issubclass(queryset._iterable_class, ModelIterable) + ) + ): + raise ValueError( + "Prefetch querysets cannot use raw(), values(), and values_list()." + ) + if to_attr: + self.prefetch_to = LOOKUP_SEP.join( + lookup.split(LOOKUP_SEP)[:-1] + [to_attr] + ) + + self.queryset = queryset + self.to_attr = to_attr + + def __getstate__(self): + obj_dict = self.__dict__.copy() + if self.queryset is not None: + queryset = self.queryset._chain() + # Prevent the QuerySet from being evaluated + queryset._result_cache = [] + queryset._prefetch_done = True + obj_dict["queryset"] = queryset + return obj_dict + + def add_prefix(self, prefix): + self.prefetch_through = prefix + LOOKUP_SEP + self.prefetch_through + self.prefetch_to = prefix + LOOKUP_SEP + self.prefetch_to + + def get_current_prefetch_to(self, level): + return LOOKUP_SEP.join(self.prefetch_to.split(LOOKUP_SEP)[: level + 1]) + + def get_current_to_attr(self, level): + parts = self.prefetch_to.split(LOOKUP_SEP) + to_attr = parts[level] + as_attr = self.to_attr and level == len(parts) - 1 + return to_attr, as_attr + + def get_current_queryset(self, level): + if self.get_current_prefetch_to(level) == self.prefetch_to: + return self.queryset + return None + + def __eq__(self, other): + if not isinstance(other, Prefetch): + return NotImplemented + return self.prefetch_to == other.prefetch_to + + def __hash__(self): + return hash((self.__class__, self.prefetch_to)) + + +def normalize_prefetch_lookups(lookups, prefix=None): + """Normalize lookups into Prefetch objects.""" + ret = [] + for lookup in lookups: + if not isinstance(lookup, Prefetch): + lookup = Prefetch(lookup) + if prefix: + lookup.add_prefix(prefix) + ret.append(lookup) + return ret + + +def prefetch_related_objects(model_instances, *related_lookups): + """ + Populate prefetched object caches for a list of model instances based on + the lookups/Prefetch instances given. + """ + if not model_instances: + return # nothing to do + + # We need to be able to dynamically add to the list of prefetch_related + # lookups that we look up (see below). So we need some book keeping to + # ensure we don't do duplicate work. + done_queries = {} # dictionary of things like 'foo__bar': [results] + + auto_lookups = set() # we add to this as we go through. + followed_descriptors = set() # recursion protection + + all_lookups = normalize_prefetch_lookups(reversed(related_lookups)) + while all_lookups: + lookup = all_lookups.pop() + if lookup.prefetch_to in done_queries: + if lookup.queryset is not None: + raise ValueError( + "'%s' lookup was already seen with a different queryset. " + "You may need to adjust the ordering of your lookups." + % lookup.prefetch_to + ) + + continue + + # Top level, the list of objects to decorate is the result cache + # from the primary QuerySet. It won't be for deeper levels. + obj_list = model_instances + + through_attrs = lookup.prefetch_through.split(LOOKUP_SEP) + for level, through_attr in enumerate(through_attrs): + # Prepare main instances + if not obj_list: + break + + prefetch_to = lookup.get_current_prefetch_to(level) + if prefetch_to in done_queries: + # Skip any prefetching, and any object preparation + obj_list = done_queries[prefetch_to] + continue + + # Prepare objects: + good_objects = True + for obj in obj_list: + # Since prefetching can re-use instances, it is possible to have + # the same instance multiple times in obj_list, so obj might + # already be prepared. + if not hasattr(obj, "_prefetched_objects_cache"): + try: + obj._prefetched_objects_cache = {} + except (AttributeError, TypeError): + # Must be an immutable object from + # values_list(flat=True), for example (TypeError) or + # a QuerySet subclass that isn't returning Model + # instances (AttributeError), either in Django or a 3rd + # party. prefetch_related() doesn't make sense, so quit. + good_objects = False + break + if not good_objects: + break + + # Descend down tree + + # We assume that objects retrieved are homogeneous (which is the premise + # of prefetch_related), so what applies to first object applies to all. + first_obj = obj_list[0] + to_attr = lookup.get_current_to_attr(level)[0] + prefetcher, descriptor, attr_found, is_fetched = get_prefetcher( + first_obj, through_attr, to_attr + ) + + if not attr_found: + raise AttributeError( + "Cannot find '%s' on %s object, '%s' is an invalid " + "parameter to prefetch_related()" + % ( + through_attr, + first_obj.__class__.__name__, + lookup.prefetch_through, + ) + ) + + if level == len(through_attrs) - 1 and prefetcher is None: + # Last one, this *must* resolve to something that supports + # prefetching, otherwise there is no point adding it and the + # developer asking for it has made a mistake. + raise ValueError( + "'%s' does not resolve to an item that supports " + "prefetching - this is an invalid parameter to " + "prefetch_related()." % lookup.prefetch_through + ) + + obj_to_fetch = None + if prefetcher is not None: + obj_to_fetch = [obj for obj in obj_list if not is_fetched(obj)] + + if obj_to_fetch: + obj_list, additional_lookups = prefetch_one_level( + obj_to_fetch, + prefetcher, + lookup, + level, + ) + # We need to ensure we don't keep adding lookups from the + # same relationships to stop infinite recursion. So, if we + # are already on an automatically added lookup, don't add + # the new lookups from relationships we've seen already. + if not ( + prefetch_to in done_queries + and lookup in auto_lookups + and descriptor in followed_descriptors + ): + done_queries[prefetch_to] = obj_list + new_lookups = normalize_prefetch_lookups( + reversed(additional_lookups), prefetch_to + ) + auto_lookups.update(new_lookups) + all_lookups.extend(new_lookups) + followed_descriptors.add(descriptor) + else: + # Either a singly related object that has already been fetched + # (e.g. via select_related), or hopefully some other property + # that doesn't support prefetching but needs to be traversed. + + # We replace the current list of parent objects with the list + # of related objects, filtering out empty or missing values so + # that we can continue with nullable or reverse relations. + new_obj_list = [] + for obj in obj_list: + if through_attr in getattr(obj, "_prefetched_objects_cache", ()): + # If related objects have been prefetched, use the + # cache rather than the object's through_attr. + new_obj = list(obj._prefetched_objects_cache.get(through_attr)) + else: + try: + new_obj = getattr(obj, through_attr) + except exceptions.ObjectDoesNotExist: + continue + if new_obj is None: + continue + # We special-case `list` rather than something more generic + # like `Iterable` because we don't want to accidentally match + # user models that define __iter__. + if isinstance(new_obj, list): + new_obj_list.extend(new_obj) + else: + new_obj_list.append(new_obj) + obj_list = new_obj_list + + +def get_prefetcher(instance, through_attr, to_attr): + """ + For the attribute 'through_attr' on the given instance, find + an object that has a get_prefetch_queryset(). + Return a 4 tuple containing: + (the object with get_prefetch_queryset (or None), + the descriptor object representing this relationship (or None), + a boolean that is False if the attribute was not found at all, + a function that takes an instance and returns a boolean that is True if + the attribute has already been fetched for that instance) + """ + + def has_to_attr_attribute(instance): + return hasattr(instance, to_attr) + + prefetcher = None + is_fetched = has_to_attr_attribute + + # For singly related objects, we have to avoid getting the attribute + # from the object, as this will trigger the query. So we first try + # on the class, in order to get the descriptor object. + rel_obj_descriptor = getattr(instance.__class__, through_attr, None) + if rel_obj_descriptor is None: + attr_found = hasattr(instance, through_attr) + else: + attr_found = True + if rel_obj_descriptor: + # singly related object, descriptor object has the + # get_prefetch_queryset() method. + if hasattr(rel_obj_descriptor, "get_prefetch_queryset"): + prefetcher = rel_obj_descriptor + is_fetched = rel_obj_descriptor.is_cached + else: + # descriptor doesn't support prefetching, so we go ahead and get + # the attribute on the instance rather than the class to + # support many related managers + rel_obj = getattr(instance, through_attr) + if hasattr(rel_obj, "get_prefetch_queryset"): + prefetcher = rel_obj + if through_attr != to_attr: + # Special case cached_property instances because hasattr + # triggers attribute computation and assignment. + if isinstance( + getattr(instance.__class__, to_attr, None), cached_property + ): + + def has_cached_property(instance): + return to_attr in instance.__dict__ + + is_fetched = has_cached_property + else: + + def in_prefetched_cache(instance): + return through_attr in instance._prefetched_objects_cache + + is_fetched = in_prefetched_cache + return prefetcher, rel_obj_descriptor, attr_found, is_fetched + + +def prefetch_one_level(instances, prefetcher, lookup, level): + """ + Helper function for prefetch_related_objects(). + + Run prefetches on all instances using the prefetcher object, + assigning results to relevant caches in instance. + + Return the prefetched objects along with any additional prefetches that + must be done due to prefetch_related lookups found from default managers. + """ + # prefetcher must have a method get_prefetch_queryset() which takes a list + # of instances, and returns a tuple: + + # (queryset of instances of self.model that are related to passed in instances, + # callable that gets value to be matched for returned instances, + # callable that gets value to be matched for passed in instances, + # boolean that is True for singly related objects, + # cache or field name to assign to, + # boolean that is True when the previous argument is a cache name vs a field name). + + # The 'values to be matched' must be hashable as they will be used + # in a dictionary. + + ( + rel_qs, + rel_obj_attr, + instance_attr, + single, + cache_name, + is_descriptor, + ) = prefetcher.get_prefetch_queryset(instances, lookup.get_current_queryset(level)) + # We have to handle the possibility that the QuerySet we just got back + # contains some prefetch_related lookups. We don't want to trigger the + # prefetch_related functionality by evaluating the query. Rather, we need + # to merge in the prefetch_related lookups. + # Copy the lookups in case it is a Prefetch object which could be reused + # later (happens in nested prefetch_related). + additional_lookups = [ + copy.copy(additional_lookup) + for additional_lookup in getattr(rel_qs, "_prefetch_related_lookups", ()) + ] + if additional_lookups: + # Don't need to clone because the manager should have given us a fresh + # instance, so we access an internal instead of using public interface + # for performance reasons. + rel_qs._prefetch_related_lookups = () + + all_related_objects = list(rel_qs) + + rel_obj_cache = {} + for rel_obj in all_related_objects: + rel_attr_val = rel_obj_attr(rel_obj) + rel_obj_cache.setdefault(rel_attr_val, []).append(rel_obj) + + to_attr, as_attr = lookup.get_current_to_attr(level) + # Make sure `to_attr` does not conflict with a field. + if as_attr and instances: + # We assume that objects retrieved are homogeneous (which is the premise + # of prefetch_related), so what applies to first object applies to all. + model = instances[0].__class__ + try: + model._meta.get_field(to_attr) + except exceptions.FieldDoesNotExist: + pass + else: + msg = "to_attr={} conflicts with a field on the {} model." + raise ValueError(msg.format(to_attr, model.__name__)) + + # Whether or not we're prefetching the last part of the lookup. + leaf = len(lookup.prefetch_through.split(LOOKUP_SEP)) - 1 == level + + for obj in instances: + instance_attr_val = instance_attr(obj) + vals = rel_obj_cache.get(instance_attr_val, []) + + if single: + val = vals[0] if vals else None + if as_attr: + # A to_attr has been given for the prefetch. + setattr(obj, to_attr, val) + elif is_descriptor: + # cache_name points to a field name in obj. + # This field is a descriptor for a related object. + setattr(obj, cache_name, val) + else: + # No to_attr has been given for this prefetch operation and the + # cache_name does not point to a descriptor. Store the value of + # the field in the object's field cache. + obj._state.fields_cache[cache_name] = val + else: + if as_attr: + setattr(obj, to_attr, vals) + else: + manager = getattr(obj, to_attr) + if leaf and lookup.queryset is not None: + qs = manager._apply_rel_filters(lookup.queryset) + else: + qs = manager.get_queryset() + qs._result_cache = vals + # We don't want the individual qs doing prefetch_related now, + # since we have merged this into the current work. + qs._prefetch_done = True + obj._prefetched_objects_cache[cache_name] = qs + return all_related_objects, additional_lookups + + +class RelatedPopulator: + """ + RelatedPopulator is used for select_related() object instantiation. + + The idea is that each select_related() model will be populated by a + different RelatedPopulator instance. The RelatedPopulator instances get + klass_info and select (computed in SQLCompiler) plus the used db as + input for initialization. That data is used to compute which columns + to use, how to instantiate the model, and how to populate the links + between the objects. + + The actual creation of the objects is done in populate() method. This + method gets row and from_obj as input and populates the select_related() + model instance. + """ + + def __init__(self, klass_info, select, db): + self.db = db + # Pre-compute needed attributes. The attributes are: + # - model_cls: the possibly deferred model class to instantiate + # - either: + # - cols_start, cols_end: usually the columns in the row are + # in the same order model_cls.__init__ expects them, so we + # can instantiate by model_cls(*row[cols_start:cols_end]) + # - reorder_for_init: When select_related descends to a child + # class, then we want to reuse the already selected parent + # data. However, in this case the parent data isn't necessarily + # in the same order that Model.__init__ expects it to be, so + # we have to reorder the parent data. The reorder_for_init + # attribute contains a function used to reorder the field data + # in the order __init__ expects it. + # - pk_idx: the index of the primary key field in the reordered + # model data. Used to check if a related object exists at all. + # - init_list: the field attnames fetched from the database. For + # deferred models this isn't the same as all attnames of the + # model's fields. + # - related_populators: a list of RelatedPopulator instances if + # select_related() descends to related models from this model. + # - local_setter, remote_setter: Methods to set cached values on + # the object being populated and on the remote object. Usually + # these are Field.set_cached_value() methods. + select_fields = klass_info["select_fields"] + from_parent = klass_info["from_parent"] + if not from_parent: + self.cols_start = select_fields[0] + self.cols_end = select_fields[-1] + 1 + self.init_list = [ + f[0].target.attname for f in select[self.cols_start : self.cols_end] + ] + self.reorder_for_init = None + else: + attname_indexes = { + select[idx][0].target.attname: idx for idx in select_fields + } + model_init_attnames = ( + f.attname for f in klass_info["model"]._meta.concrete_fields + ) + self.init_list = [ + attname for attname in model_init_attnames if attname in attname_indexes + ] + self.reorder_for_init = operator.itemgetter( + *[attname_indexes[attname] for attname in self.init_list] + ) + + self.model_cls = klass_info["model"] + self.pk_idx = self.init_list.index(self.model_cls._meta.pk.attname) + self.related_populators = get_related_populators(klass_info, select, self.db) + self.local_setter = klass_info["local_setter"] + self.remote_setter = klass_info["remote_setter"] + + def populate(self, row, from_obj): + if self.reorder_for_init: + obj_data = self.reorder_for_init(row) + else: + obj_data = row[self.cols_start : self.cols_end] + if obj_data[self.pk_idx] is None: + obj = None + else: + obj = self.model_cls.from_db(self.db, self.init_list, obj_data) + for rel_iter in self.related_populators: + rel_iter.populate(row, obj) + self.local_setter(from_obj, obj) + if obj is not None: + self.remote_setter(obj, from_obj) + + +def get_related_populators(klass_info, select, db): + iterators = [] + related_klass_infos = klass_info.get("related_klass_infos", []) + for rel_klass_info in related_klass_infos: + rel_cls = RelatedPopulator(rel_klass_info, select, db) + iterators.append(rel_cls) + return iterators diff --git a/virt/lib/python3.9/site-packages/django/db/models/sql/compiler 3.py b/virt/lib/python3.9/site-packages/django/db/models/sql/compiler 3.py new file mode 100644 index 00000000..81a967f4 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/db/models/sql/compiler 3.py @@ -0,0 +1,2096 @@ +import collections +import json +import re +from functools import partial +from itertools import chain + +from django.core.exceptions import EmptyResultSet, FieldError, FullResultSet +from django.db import DatabaseError, NotSupportedError +from django.db.models.constants import LOOKUP_SEP +from django.db.models.expressions import F, OrderBy, RawSQL, Ref, Value +from django.db.models.functions import Cast, Random +from django.db.models.lookups import Lookup +from django.db.models.query_utils import select_related_descend +from django.db.models.sql.constants import ( + CURSOR, + GET_ITERATOR_CHUNK_SIZE, + MULTI, + NO_RESULTS, + ORDER_DIR, + SINGLE, +) +from django.db.models.sql.query import Query, get_order_dir +from django.db.models.sql.where import AND +from django.db.transaction import TransactionManagementError +from django.utils.functional import cached_property +from django.utils.hashable import make_hashable +from django.utils.regex_helper import _lazy_re_compile + + +class PositionRef(Ref): + def __init__(self, ordinal, refs, source): + self.ordinal = ordinal + super().__init__(refs, source) + + def as_sql(self, compiler, connection): + return str(self.ordinal), () + + +class SQLCompiler: + # Multiline ordering SQL clause may appear from RawSQL. + ordering_parts = _lazy_re_compile( + r"^(.*)\s(?:ASC|DESC).*", + re.MULTILINE | re.DOTALL, + ) + + def __init__(self, query, connection, using, elide_empty=True): + self.query = query + self.connection = connection + self.using = using + # Some queries, e.g. coalesced aggregation, need to be executed even if + # they would return an empty result set. + self.elide_empty = elide_empty + self.quote_cache = {"*": "*"} + # The select, klass_info, and annotations are needed by QuerySet.iterator() + # these are set as a side-effect of executing the query. Note that we calculate + # separately a list of extra select columns needed for grammatical correctness + # of the query, but these columns are not included in self.select. + self.select = None + self.annotation_col_map = None + self.klass_info = None + self._meta_ordering = None + + def __repr__(self): + return ( + f"<{self.__class__.__qualname__} " + f"model={self.query.model.__qualname__} " + f"connection={self.connection!r} using={self.using!r}>" + ) + + def setup_query(self, with_col_aliases=False): + if all(self.query.alias_refcount[a] == 0 for a in self.query.alias_map): + self.query.get_initial_alias() + self.select, self.klass_info, self.annotation_col_map = self.get_select( + with_col_aliases=with_col_aliases, + ) + self.col_count = len(self.select) + + def pre_sql_setup(self, with_col_aliases=False): + """ + Do any necessary class setup immediately prior to producing SQL. This + is for things that can't necessarily be done in __init__ because we + might not have all the pieces in place at that time. + """ + self.setup_query(with_col_aliases=with_col_aliases) + order_by = self.get_order_by() + self.where, self.having, self.qualify = self.query.where.split_having_qualify( + must_group_by=self.query.group_by is not None + ) + extra_select = self.get_extra_select(order_by, self.select) + self.has_extra_select = bool(extra_select) + group_by = self.get_group_by(self.select + extra_select, order_by) + return extra_select, order_by, group_by + + def get_group_by(self, select, order_by): + """ + Return a list of 2-tuples of form (sql, params). + + The logic of what exactly the GROUP BY clause contains is hard + to describe in other words than "if it passes the test suite, + then it is correct". + """ + # Some examples: + # SomeModel.objects.annotate(Count('somecol')) + # GROUP BY: all fields of the model + # + # SomeModel.objects.values('name').annotate(Count('somecol')) + # GROUP BY: name + # + # SomeModel.objects.annotate(Count('somecol')).values('name') + # GROUP BY: all cols of the model + # + # SomeModel.objects.values('name', 'pk') + # .annotate(Count('somecol')).values('pk') + # GROUP BY: name, pk + # + # SomeModel.objects.values('name').annotate(Count('somecol')).values('pk') + # GROUP BY: name, pk + # + # In fact, the self.query.group_by is the minimal set to GROUP BY. It + # can't be ever restricted to a smaller set, but additional columns in + # HAVING, ORDER BY, and SELECT clauses are added to it. Unfortunately + # the end result is that it is impossible to force the query to have + # a chosen GROUP BY clause - you can almost do this by using the form: + # .values(*wanted_cols).annotate(AnAggregate()) + # but any later annotations, extra selects, values calls that + # refer some column outside of the wanted_cols, order_by, or even + # filter calls can alter the GROUP BY clause. + + # The query.group_by is either None (no GROUP BY at all), True + # (group by select fields), or a list of expressions to be added + # to the group by. + if self.query.group_by is None: + return [] + expressions = [] + group_by_refs = set() + if self.query.group_by is not True: + # If the group by is set to a list (by .values() call most likely), + # then we need to add everything in it to the GROUP BY clause. + # Backwards compatibility hack for setting query.group_by. Remove + # when we have public API way of forcing the GROUP BY clause. + # Converts string references to expressions. + for expr in self.query.group_by: + if not hasattr(expr, "as_sql"): + expr = self.query.resolve_ref(expr) + if isinstance(expr, Ref): + if expr.refs not in group_by_refs: + group_by_refs.add(expr.refs) + expressions.append(expr.source) + else: + expressions.append(expr) + # Note that even if the group_by is set, it is only the minimal + # set to group by. So, we need to add cols in select, order_by, and + # having into the select in any case. + selected_expr_indices = {} + for index, (expr, _, alias) in enumerate(select, start=1): + if alias: + selected_expr_indices[expr] = index + # Skip members of the select clause that are already explicitly + # grouped against. + if alias in group_by_refs: + continue + expressions.extend(expr.get_group_by_cols()) + if not self._meta_ordering: + for expr, (sql, params, is_ref) in order_by: + # Skip references to the SELECT clause, as all expressions in + # the SELECT clause are already part of the GROUP BY. + if not is_ref: + expressions.extend(expr.get_group_by_cols()) + having_group_by = self.having.get_group_by_cols() if self.having else () + for expr in having_group_by: + expressions.append(expr) + result = [] + seen = set() + expressions = self.collapse_group_by(expressions, having_group_by) + + allows_group_by_select_index = ( + self.connection.features.allows_group_by_select_index + ) + for expr in expressions: + try: + sql, params = self.compile(expr) + except (EmptyResultSet, FullResultSet): + continue + if ( + allows_group_by_select_index + and (select_index := selected_expr_indices.get(expr)) is not None + ): + sql, params = str(select_index), () + else: + sql, params = expr.select_format(self, sql, params) + params_hash = make_hashable(params) + if (sql, params_hash) not in seen: + result.append((sql, params)) + seen.add((sql, params_hash)) + return result + + def collapse_group_by(self, expressions, having): + # If the database supports group by functional dependence reduction, + # then the expressions can be reduced to the set of selected table + # primary keys as all other columns are functionally dependent on them. + if self.connection.features.allows_group_by_selected_pks: + # Filter out all expressions associated with a table's primary key + # present in the grouped columns. This is done by identifying all + # tables that have their primary key included in the grouped + # columns and removing non-primary key columns referring to them. + # Unmanaged models are excluded because they could be representing + # database views on which the optimization might not be allowed. + pks = { + expr + for expr in expressions + if ( + hasattr(expr, "target") + and expr.target.primary_key + and self.connection.features.allows_group_by_selected_pks_on_model( + expr.target.model + ) + ) + } + aliases = {expr.alias for expr in pks} + expressions = [ + expr + for expr in expressions + if expr in pks + or expr in having + or getattr(expr, "alias", None) not in aliases + ] + return expressions + + def get_select(self, with_col_aliases=False): + """ + Return three values: + - a list of 3-tuples of (expression, (sql, params), alias) + - a klass_info structure, + - a dictionary of annotations + + The (sql, params) is what the expression will produce, and alias is the + "AS alias" for the column (possibly None). + + The klass_info structure contains the following information: + - The base model of the query. + - Which columns for that model are present in the query (by + position of the select clause). + - related_klass_infos: [f, klass_info] to descent into + + The annotations is a dictionary of {'attname': column position} values. + """ + select = [] + klass_info = None + annotations = {} + select_idx = 0 + for alias, (sql, params) in self.query.extra_select.items(): + annotations[alias] = select_idx + select.append((RawSQL(sql, params), alias)) + select_idx += 1 + assert not (self.query.select and self.query.default_cols) + select_mask = self.query.get_select_mask() + if self.query.default_cols: + cols = self.get_default_columns(select_mask) + else: + # self.query.select is a special case. These columns never go to + # any model. + cols = self.query.select + if cols: + select_list = [] + for col in cols: + select_list.append(select_idx) + select.append((col, None)) + select_idx += 1 + klass_info = { + "model": self.query.model, + "select_fields": select_list, + } + for alias, annotation in self.query.annotation_select.items(): + annotations[alias] = select_idx + select.append((annotation, alias)) + select_idx += 1 + + if self.query.select_related: + related_klass_infos = self.get_related_selections(select, select_mask) + klass_info["related_klass_infos"] = related_klass_infos + + def get_select_from_parent(klass_info): + for ki in klass_info["related_klass_infos"]: + if ki["from_parent"]: + ki["select_fields"] = ( + klass_info["select_fields"] + ki["select_fields"] + ) + get_select_from_parent(ki) + + get_select_from_parent(klass_info) + + ret = [] + col_idx = 1 + for col, alias in select: + try: + sql, params = self.compile(col) + except EmptyResultSet: + empty_result_set_value = getattr( + col, "empty_result_set_value", NotImplemented + ) + if empty_result_set_value is NotImplemented: + # Select a predicate that's always False. + sql, params = "0", () + else: + sql, params = self.compile(Value(empty_result_set_value)) + except FullResultSet: + sql, params = self.compile(Value(True)) + else: + sql, params = col.select_format(self, sql, params) + if alias is None and with_col_aliases: + alias = f"col{col_idx}" + col_idx += 1 + ret.append((col, (sql, params), alias)) + return ret, klass_info, annotations + + def _order_by_pairs(self): + if self.query.extra_order_by: + ordering = self.query.extra_order_by + elif not self.query.default_ordering: + ordering = self.query.order_by + elif self.query.order_by: + ordering = self.query.order_by + elif (meta := self.query.get_meta()) and meta.ordering: + ordering = meta.ordering + self._meta_ordering = ordering + else: + ordering = [] + if self.query.standard_ordering: + default_order, _ = ORDER_DIR["ASC"] + else: + default_order, _ = ORDER_DIR["DESC"] + + selected_exprs = {} + # Avoid computing `selected_exprs` if there is no `ordering` as it's + # relatively expensive. + if ordering and (select := self.select): + for ordinal, (expr, _, alias) in enumerate(select, start=1): + pos_expr = PositionRef(ordinal, alias, expr) + if alias: + selected_exprs[alias] = pos_expr + selected_exprs[expr] = pos_expr + + for field in ordering: + if hasattr(field, "resolve_expression"): + if isinstance(field, Value): + # output_field must be resolved for constants. + field = Cast(field, field.output_field) + if not isinstance(field, OrderBy): + field = field.asc() + if not self.query.standard_ordering: + field = field.copy() + field.reverse_ordering() + select_ref = selected_exprs.get(field.expression) + if select_ref or ( + isinstance(field.expression, F) + and (select_ref := selected_exprs.get(field.expression.name)) + ): + # Emulation of NULLS (FIRST|LAST) cannot be combined with + # the usage of ordering by position. + if ( + field.nulls_first is None and field.nulls_last is None + ) or self.connection.features.supports_order_by_nulls_modifier: + field = field.copy() + field.expression = select_ref + # Alias collisions are not possible when dealing with + # combined queries so fallback to it if emulation of NULLS + # handling is required. + elif self.query.combinator: + field = field.copy() + field.expression = Ref(select_ref.refs, select_ref.source) + yield field, select_ref is not None + continue + if field == "?": # random + yield OrderBy(Random()), False + continue + + col, order = get_order_dir(field, default_order) + descending = order == "DESC" + + if select_ref := selected_exprs.get(col): + # Reference to expression in SELECT clause + yield ( + OrderBy( + select_ref, + descending=descending, + ), + True, + ) + continue + if col in self.query.annotations: + # References to an expression which is masked out of the SELECT + # clause. + if self.query.combinator and self.select: + # Don't use the resolved annotation because other + # combinated queries might define it differently. + expr = F(col) + else: + expr = self.query.annotations[col] + if isinstance(expr, Value): + # output_field must be resolved for constants. + expr = Cast(expr, expr.output_field) + yield OrderBy(expr, descending=descending), False + continue + + if "." in field: + # This came in through an extra(order_by=...) addition. Pass it + # on verbatim. + table, col = col.split(".", 1) + yield ( + OrderBy( + RawSQL( + "%s.%s" % (self.quote_name_unless_alias(table), col), [] + ), + descending=descending, + ), + False, + ) + continue + + if self.query.extra and col in self.query.extra: + if col in self.query.extra_select: + yield ( + OrderBy( + Ref(col, RawSQL(*self.query.extra[col])), + descending=descending, + ), + True, + ) + else: + yield ( + OrderBy(RawSQL(*self.query.extra[col]), descending=descending), + False, + ) + else: + if self.query.combinator and self.select: + # Don't use the first model's field because other + # combinated queries might define it differently. + yield OrderBy(F(col), descending=descending), False + else: + # 'col' is of the form 'field' or 'field1__field2' or + # '-field1__field2__field', etc. + yield from self.find_ordering_name( + field, + self.query.get_meta(), + default_order=default_order, + ) + + def get_order_by(self): + """ + Return a list of 2-tuples of the form (expr, (sql, params, is_ref)) for + the ORDER BY clause. + + The order_by clause can alter the select clause (for example it can add + aliases to clauses that do not yet have one, or it can add totally new + select clauses). + """ + result = [] + seen = set() + for expr, is_ref in self._order_by_pairs(): + resolved = expr.resolve_expression(self.query, allow_joins=True, reuse=None) + if not is_ref and self.query.combinator and self.select: + src = resolved.expression + expr_src = expr.expression + for sel_expr, _, col_alias in self.select: + if src == sel_expr: + # When values() is used the exact alias must be used to + # reference annotations. + if ( + self.query.has_select_fields + and col_alias in self.query.annotation_select + and not ( + isinstance(expr_src, F) and col_alias == expr_src.name + ) + ): + continue + resolved.set_source_expressions( + [Ref(col_alias if col_alias else src.target.column, src)] + ) + break + else: + # Add column used in ORDER BY clause to the selected + # columns and to each combined query. + order_by_idx = len(self.query.select) + 1 + col_alias = f"__orderbycol{order_by_idx}" + for q in self.query.combined_queries: + # If fields were explicitly selected through values() + # combined queries cannot be augmented. + if q.has_select_fields: + raise DatabaseError( + "ORDER BY term does not match any column in " + "the result set." + ) + q.add_annotation(expr_src, col_alias) + self.query.add_select_col(resolved, col_alias) + resolved.set_source_expressions([Ref(col_alias, src)]) + sql, params = self.compile(resolved) + # Don't add the same column twice, but the order direction is + # not taken into account so we strip it. When this entire method + # is refactored into expressions, then we can check each part as we + # generate it. + without_ordering = self.ordering_parts.search(sql)[1] + params_hash = make_hashable(params) + if (without_ordering, params_hash) in seen: + continue + seen.add((without_ordering, params_hash)) + result.append((resolved, (sql, params, is_ref))) + return result + + def get_extra_select(self, order_by, select): + extra_select = [] + if self.query.distinct and not self.query.distinct_fields: + select_sql = [t[1] for t in select] + for expr, (sql, params, is_ref) in order_by: + without_ordering = self.ordering_parts.search(sql)[1] + if not is_ref and (without_ordering, params) not in select_sql: + extra_select.append((expr, (without_ordering, params), None)) + return extra_select + + def quote_name_unless_alias(self, name): + """ + A wrapper around connection.ops.quote_name that doesn't quote aliases + for table names. This avoids problems with some SQL dialects that treat + quoted strings specially (e.g. PostgreSQL). + """ + if name in self.quote_cache: + return self.quote_cache[name] + if ( + (name in self.query.alias_map and name not in self.query.table_map) + or name in self.query.extra_select + or ( + self.query.external_aliases.get(name) + and name not in self.query.table_map + ) + ): + self.quote_cache[name] = name + return name + r = self.connection.ops.quote_name(name) + self.quote_cache[name] = r + return r + + def compile(self, node): + vendor_impl = getattr(node, "as_" + self.connection.vendor, None) + if vendor_impl: + sql, params = vendor_impl(self, self.connection) + else: + sql, params = node.as_sql(self, self.connection) + return sql, params + + def get_combinator_sql(self, combinator, all): + features = self.connection.features + compilers = [ + query.get_compiler(self.using, self.connection, self.elide_empty) + for query in self.query.combined_queries + ] + if not features.supports_slicing_ordering_in_compound: + for compiler in compilers: + if compiler.query.is_sliced: + raise DatabaseError( + "LIMIT/OFFSET not allowed in subqueries of compound statements." + ) + if compiler.get_order_by(): + raise DatabaseError( + "ORDER BY not allowed in subqueries of compound statements." + ) + elif self.query.is_sliced and combinator == "union": + for compiler in compilers: + # A sliced union cannot have its parts elided as some of them + # might be sliced as well and in the event where only a single + # part produces a non-empty resultset it might be impossible to + # generate valid SQL. + compiler.elide_empty = False + parts = () + for compiler in compilers: + try: + # If the columns list is limited, then all combined queries + # must have the same columns list. Set the selects defined on + # the query on all combined queries, if not already set. + if not compiler.query.values_select and self.query.values_select: + compiler.query = compiler.query.clone() + compiler.query.set_values( + ( + *self.query.extra_select, + *self.query.values_select, + *self.query.annotation_select, + ) + ) + part_sql, part_args = compiler.as_sql(with_col_aliases=True) + if compiler.query.combinator: + # Wrap in a subquery if wrapping in parentheses isn't + # supported. + if not features.supports_parentheses_in_compound: + part_sql = "SELECT * FROM ({})".format(part_sql) + # Add parentheses when combining with compound query if not + # already added for all compound queries. + elif ( + self.query.subquery + or not features.supports_slicing_ordering_in_compound + ): + part_sql = "({})".format(part_sql) + elif ( + self.query.subquery + and features.supports_slicing_ordering_in_compound + ): + part_sql = "({})".format(part_sql) + parts += ((part_sql, part_args),) + except EmptyResultSet: + # Omit the empty queryset with UNION and with DIFFERENCE if the + # first queryset is nonempty. + if combinator == "union" or (combinator == "difference" and parts): + continue + raise + if not parts: + raise EmptyResultSet + combinator_sql = self.connection.ops.set_operators[combinator] + if all and combinator == "union": + combinator_sql += " ALL" + braces = "{}" + if not self.query.subquery and features.supports_slicing_ordering_in_compound: + braces = "({})" + sql_parts, args_parts = zip( + *((braces.format(sql), args) for sql, args in parts) + ) + result = [" {} ".format(combinator_sql).join(sql_parts)] + params = [] + for part in args_parts: + params.extend(part) + return result, params + + def get_qualify_sql(self): + where_parts = [] + if self.where: + where_parts.append(self.where) + if self.having: + where_parts.append(self.having) + inner_query = self.query.clone() + inner_query.subquery = True + inner_query.where = inner_query.where.__class__(where_parts) + # Augment the inner query with any window function references that + # might have been masked via values() and alias(). If any masked + # aliases are added they'll be masked again to avoid fetching + # the data in the `if qual_aliases` branch below. + select = { + expr: alias for expr, _, alias in self.get_select(with_col_aliases=True)[0] + } + select_aliases = set(select.values()) + qual_aliases = set() + replacements = {} + + def collect_replacements(expressions): + while expressions: + expr = expressions.pop() + if expr in replacements: + continue + elif select_alias := select.get(expr): + replacements[expr] = select_alias + elif isinstance(expr, Lookup): + expressions.extend(expr.get_source_expressions()) + elif isinstance(expr, Ref): + if expr.refs not in select_aliases: + expressions.extend(expr.get_source_expressions()) + else: + num_qual_alias = len(qual_aliases) + select_alias = f"qual{num_qual_alias}" + qual_aliases.add(select_alias) + inner_query.add_annotation(expr, select_alias) + replacements[expr] = select_alias + + collect_replacements(list(self.qualify.leaves())) + self.qualify = self.qualify.replace_expressions( + {expr: Ref(alias, expr) for expr, alias in replacements.items()} + ) + order_by = [] + for order_by_expr, *_ in self.get_order_by(): + collect_replacements(order_by_expr.get_source_expressions()) + order_by.append( + order_by_expr.replace_expressions( + {expr: Ref(alias, expr) for expr, alias in replacements.items()} + ) + ) + inner_query_compiler = inner_query.get_compiler( + self.using, connection=self.connection, elide_empty=self.elide_empty + ) + inner_sql, inner_params = inner_query_compiler.as_sql( + # The limits must be applied to the outer query to avoid pruning + # results too eagerly. + with_limits=False, + # Force unique aliasing of selected columns to avoid collisions + # and make rhs predicates referencing easier. + with_col_aliases=True, + ) + qualify_sql, qualify_params = self.compile(self.qualify) + result = [ + "SELECT * FROM (", + inner_sql, + ")", + self.connection.ops.quote_name("qualify"), + "WHERE", + qualify_sql, + ] + if qual_aliases: + # If some select aliases were unmasked for filtering purposes they + # must be masked back. + cols = [self.connection.ops.quote_name(alias) for alias in select.values()] + result = [ + "SELECT", + ", ".join(cols), + "FROM (", + *result, + ")", + self.connection.ops.quote_name("qualify_mask"), + ] + params = list(inner_params) + qualify_params + # As the SQL spec is unclear on whether or not derived tables + # ordering must propagate it has to be explicitly repeated on the + # outer-most query to ensure it's preserved. + if order_by: + ordering_sqls = [] + for ordering in order_by: + ordering_sql, ordering_params = self.compile(ordering) + ordering_sqls.append(ordering_sql) + params.extend(ordering_params) + result.extend(["ORDER BY", ", ".join(ordering_sqls)]) + return result, params + + def as_sql(self, with_limits=True, with_col_aliases=False): + """ + Create the SQL for this query. Return the SQL string and list of + parameters. + + If 'with_limits' is False, any limit/offset information is not included + in the query. + """ + refcounts_before = self.query.alias_refcount.copy() + try: + combinator = self.query.combinator + extra_select, order_by, group_by = self.pre_sql_setup( + with_col_aliases=with_col_aliases or bool(combinator), + ) + for_update_part = None + # Is a LIMIT/OFFSET clause needed? + with_limit_offset = with_limits and self.query.is_sliced + combinator = self.query.combinator + features = self.connection.features + if combinator: + if not getattr(features, "supports_select_{}".format(combinator)): + raise NotSupportedError( + "{} is not supported on this database backend.".format( + combinator + ) + ) + result, params = self.get_combinator_sql( + combinator, self.query.combinator_all + ) + elif self.qualify: + result, params = self.get_qualify_sql() + order_by = None + else: + distinct_fields, distinct_params = self.get_distinct() + # This must come after 'select', 'ordering', and 'distinct' + # (see docstring of get_from_clause() for details). + from_, f_params = self.get_from_clause() + try: + where, w_params = ( + self.compile(self.where) if self.where is not None else ("", []) + ) + except EmptyResultSet: + if self.elide_empty: + raise + # Use a predicate that's always False. + where, w_params = "0 = 1", [] + except FullResultSet: + where, w_params = "", [] + try: + having, h_params = ( + self.compile(self.having) + if self.having is not None + else ("", []) + ) + except FullResultSet: + having, h_params = "", [] + result = ["SELECT"] + params = [] + + if self.query.distinct: + distinct_result, distinct_params = self.connection.ops.distinct_sql( + distinct_fields, + distinct_params, + ) + result += distinct_result + params += distinct_params + + out_cols = [] + for _, (s_sql, s_params), alias in self.select + extra_select: + if alias: + s_sql = "%s AS %s" % ( + s_sql, + self.connection.ops.quote_name(alias), + ) + params.extend(s_params) + out_cols.append(s_sql) + + result += [", ".join(out_cols)] + if from_: + result += ["FROM", *from_] + elif self.connection.features.bare_select_suffix: + result += [self.connection.features.bare_select_suffix] + params.extend(f_params) + + if self.query.select_for_update and features.has_select_for_update: + if ( + self.connection.get_autocommit() + # Don't raise an exception when database doesn't + # support transactions, as it's a noop. + and features.supports_transactions + ): + raise TransactionManagementError( + "select_for_update cannot be used outside of a transaction." + ) + + if ( + with_limit_offset + and not features.supports_select_for_update_with_limit + ): + raise NotSupportedError( + "LIMIT/OFFSET is not supported with " + "select_for_update on this database backend." + ) + nowait = self.query.select_for_update_nowait + skip_locked = self.query.select_for_update_skip_locked + of = self.query.select_for_update_of + no_key = self.query.select_for_no_key_update + # If it's a NOWAIT/SKIP LOCKED/OF/NO KEY query but the + # backend doesn't support it, raise NotSupportedError to + # prevent a possible deadlock. + if nowait and not features.has_select_for_update_nowait: + raise NotSupportedError( + "NOWAIT is not supported on this database backend." + ) + elif skip_locked and not features.has_select_for_update_skip_locked: + raise NotSupportedError( + "SKIP LOCKED is not supported on this database backend." + ) + elif of and not features.has_select_for_update_of: + raise NotSupportedError( + "FOR UPDATE OF is not supported on this database backend." + ) + elif no_key and not features.has_select_for_no_key_update: + raise NotSupportedError( + "FOR NO KEY UPDATE is not supported on this " + "database backend." + ) + for_update_part = self.connection.ops.for_update_sql( + nowait=nowait, + skip_locked=skip_locked, + of=self.get_select_for_update_of_arguments(), + no_key=no_key, + ) + + if for_update_part and features.for_update_after_from: + result.append(for_update_part) + + if where: + result.append("WHERE %s" % where) + params.extend(w_params) + + grouping = [] + for g_sql, g_params in group_by: + grouping.append(g_sql) + params.extend(g_params) + if grouping: + if distinct_fields: + raise NotImplementedError( + "annotate() + distinct(fields) is not implemented." + ) + order_by = order_by or self.connection.ops.force_no_ordering() + result.append("GROUP BY %s" % ", ".join(grouping)) + if self._meta_ordering: + order_by = None + if having: + result.append("HAVING %s" % having) + params.extend(h_params) + + if self.query.explain_info: + result.insert( + 0, + self.connection.ops.explain_query_prefix( + self.query.explain_info.format, + **self.query.explain_info.options, + ), + ) + + if order_by: + ordering = [] + for _, (o_sql, o_params, _) in order_by: + ordering.append(o_sql) + params.extend(o_params) + order_by_sql = "ORDER BY %s" % ", ".join(ordering) + if combinator and features.requires_compound_order_by_subquery: + result = ["SELECT * FROM (", *result, ")", order_by_sql] + else: + result.append(order_by_sql) + + if with_limit_offset: + result.append( + self.connection.ops.limit_offset_sql( + self.query.low_mark, self.query.high_mark + ) + ) + + if for_update_part and not features.for_update_after_from: + result.append(for_update_part) + + if self.query.subquery and extra_select: + # If the query is used as a subquery, the extra selects would + # result in more columns than the left-hand side expression is + # expecting. This can happen when a subquery uses a combination + # of order_by() and distinct(), forcing the ordering expressions + # to be selected as well. Wrap the query in another subquery + # to exclude extraneous selects. + sub_selects = [] + sub_params = [] + for index, (select, _, alias) in enumerate(self.select, start=1): + if alias: + sub_selects.append( + "%s.%s" + % ( + self.connection.ops.quote_name("subquery"), + self.connection.ops.quote_name(alias), + ) + ) + else: + select_clone = select.relabeled_clone( + {select.alias: "subquery"} + ) + subselect, subparams = select_clone.as_sql( + self, self.connection + ) + sub_selects.append(subselect) + sub_params.extend(subparams) + return "SELECT %s FROM (%s) subquery" % ( + ", ".join(sub_selects), + " ".join(result), + ), tuple(sub_params + params) + + return " ".join(result), tuple(params) + finally: + # Finally do cleanup - get rid of the joins we created above. + self.query.reset_refcounts(refcounts_before) + + def get_default_columns( + self, select_mask, start_alias=None, opts=None, from_parent=None + ): + """ + Compute the default columns for selecting every field in the base + model. Will sometimes be called to pull in related models (e.g. via + select_related), in which case "opts" and "start_alias" will be given + to provide a starting point for the traversal. + + Return a list of strings, quoted appropriately for use in SQL + directly, as well as a set of aliases used in the select statement (if + 'as_pairs' is True, return a list of (alias, col_name) pairs instead + of strings as the first component and None as the second component). + """ + result = [] + if opts is None: + if (opts := self.query.get_meta()) is None: + return result + start_alias = start_alias or self.query.get_initial_alias() + # The 'seen_models' is used to optimize checking the needed parent + # alias for a given field. This also includes None -> start_alias to + # be used by local fields. + seen_models = {None: start_alias} + + for field in opts.concrete_fields: + model = field.model._meta.concrete_model + # A proxy model will have a different model and concrete_model. We + # will assign None if the field belongs to this model. + if model == opts.model: + model = None + if ( + from_parent + and model is not None + and issubclass( + from_parent._meta.concrete_model, model._meta.concrete_model + ) + ): + # Avoid loading data for already loaded parents. + # We end up here in the case select_related() resolution + # proceeds from parent model to child model. In that case the + # parent model data is already present in the SELECT clause, + # and we want to avoid reloading the same data again. + continue + if select_mask and field not in select_mask: + continue + alias = self.query.join_parent_model(opts, model, start_alias, seen_models) + column = field.get_col(alias) + result.append(column) + return result + + def get_distinct(self): + """ + Return a quoted list of fields to use in DISTINCT ON part of the query. + + This method can alter the tables in the query, and thus it must be + called before get_from_clause(). + """ + result = [] + params = [] + opts = self.query.get_meta() + + for name in self.query.distinct_fields: + parts = name.split(LOOKUP_SEP) + _, targets, alias, joins, path, _, transform_function = self._setup_joins( + parts, opts, None + ) + targets, alias, _ = self.query.trim_joins(targets, joins, path) + for target in targets: + if name in self.query.annotation_select: + result.append(self.connection.ops.quote_name(name)) + else: + r, p = self.compile(transform_function(target, alias)) + result.append(r) + params.append(p) + return result, params + + def find_ordering_name( + self, name, opts, alias=None, default_order="ASC", already_seen=None + ): + """ + Return the table alias (the name might be ambiguous, the alias will + not be) and column name for ordering by the given 'name' parameter. + The 'name' is of the form 'field1__field2__...__fieldN'. + """ + name, order = get_order_dir(name, default_order) + descending = order == "DESC" + pieces = name.split(LOOKUP_SEP) + ( + field, + targets, + alias, + joins, + path, + opts, + transform_function, + ) = self._setup_joins(pieces, opts, alias) + + # If we get to this point and the field is a relation to another model, + # append the default ordering for that model unless it is the pk + # shortcut or the attribute name of the field that is specified or + # there are transforms to process. + if ( + field.is_relation + and opts.ordering + and getattr(field, "attname", None) != pieces[-1] + and name != "pk" + and not getattr(transform_function, "has_transforms", False) + ): + # Firstly, avoid infinite loops. + already_seen = already_seen or set() + join_tuple = tuple( + getattr(self.query.alias_map[j], "join_cols", None) for j in joins + ) + if join_tuple in already_seen: + raise FieldError("Infinite loop caused by ordering.") + already_seen.add(join_tuple) + + results = [] + for item in opts.ordering: + if hasattr(item, "resolve_expression") and not isinstance( + item, OrderBy + ): + item = item.desc() if descending else item.asc() + if isinstance(item, OrderBy): + results.append( + (item.prefix_references(f"{name}{LOOKUP_SEP}"), False) + ) + continue + results.extend( + (expr.prefix_references(f"{name}{LOOKUP_SEP}"), is_ref) + for expr, is_ref in self.find_ordering_name( + item, opts, alias, order, already_seen + ) + ) + return results + targets, alias, _ = self.query.trim_joins(targets, joins, path) + return [ + (OrderBy(transform_function(t, alias), descending=descending), False) + for t in targets + ] + + def _setup_joins(self, pieces, opts, alias): + """ + Helper method for get_order_by() and get_distinct(). + + get_ordering() and get_distinct() must produce same target columns on + same input, as the prefixes of get_ordering() and get_distinct() must + match. Executing SQL where this is not true is an error. + """ + alias = alias or self.query.get_initial_alias() + field, targets, opts, joins, path, transform_function = self.query.setup_joins( + pieces, opts, alias + ) + alias = joins[-1] + return field, targets, alias, joins, path, opts, transform_function + + def get_from_clause(self): + """ + Return a list of strings that are joined together to go after the + "FROM" part of the query, as well as a list any extra parameters that + need to be included. Subclasses, can override this to create a + from-clause via a "select". + + This should only be called after any SQL construction methods that + might change the tables that are needed. This means the select columns, + ordering, and distinct must be done first. + """ + result = [] + params = [] + for alias in tuple(self.query.alias_map): + if not self.query.alias_refcount[alias]: + continue + try: + from_clause = self.query.alias_map[alias] + except KeyError: + # Extra tables can end up in self.tables, but not in the + # alias_map if they aren't in a join. That's OK. We skip them. + continue + clause_sql, clause_params = self.compile(from_clause) + result.append(clause_sql) + params.extend(clause_params) + for t in self.query.extra_tables: + alias, _ = self.query.table_alias(t) + # Only add the alias if it's not already present (the table_alias() + # call increments the refcount, so an alias refcount of one means + # this is the only reference). + if ( + alias not in self.query.alias_map + or self.query.alias_refcount[alias] == 1 + ): + result.append(", %s" % self.quote_name_unless_alias(alias)) + return result, params + + def get_related_selections( + self, + select, + select_mask, + opts=None, + root_alias=None, + cur_depth=1, + requested=None, + restricted=None, + ): + """ + Fill in the information needed for a select_related query. The current + depth is measured as the number of connections away from the root model + (for example, cur_depth=1 means we are looking at models with direct + connections to the root model). + """ + + def _get_field_choices(): + direct_choices = (f.name for f in opts.fields if f.is_relation) + reverse_choices = ( + f.field.related_query_name() + for f in opts.related_objects + if f.field.unique + ) + return chain( + direct_choices, reverse_choices, self.query._filtered_relations + ) + + related_klass_infos = [] + if not restricted and cur_depth > self.query.max_depth: + # We've recursed far enough; bail out. + return related_klass_infos + + if not opts: + opts = self.query.get_meta() + root_alias = self.query.get_initial_alias() + + # Setup for the case when only particular related fields should be + # included in the related selection. + fields_found = set() + if requested is None: + restricted = isinstance(self.query.select_related, dict) + if restricted: + requested = self.query.select_related + + def get_related_klass_infos(klass_info, related_klass_infos): + klass_info["related_klass_infos"] = related_klass_infos + + for f in opts.fields: + fields_found.add(f.name) + + if restricted: + next = requested.get(f.name, {}) + if not f.is_relation: + # If a non-related field is used like a relation, + # or if a single non-relational field is given. + if next or f.name in requested: + raise FieldError( + "Non-relational field given in select_related: '%s'. " + "Choices are: %s" + % ( + f.name, + ", ".join(_get_field_choices()) or "(none)", + ) + ) + else: + next = False + + if not select_related_descend(f, restricted, requested, select_mask): + continue + related_select_mask = select_mask.get(f) or {} + klass_info = { + "model": f.remote_field.model, + "field": f, + "reverse": False, + "local_setter": f.set_cached_value, + "remote_setter": f.remote_field.set_cached_value + if f.unique + else lambda x, y: None, + "from_parent": False, + } + related_klass_infos.append(klass_info) + select_fields = [] + _, _, _, joins, _, _ = self.query.setup_joins([f.name], opts, root_alias) + alias = joins[-1] + columns = self.get_default_columns( + related_select_mask, start_alias=alias, opts=f.remote_field.model._meta + ) + for col in columns: + select_fields.append(len(select)) + select.append((col, None)) + klass_info["select_fields"] = select_fields + next_klass_infos = self.get_related_selections( + select, + related_select_mask, + f.remote_field.model._meta, + alias, + cur_depth + 1, + next, + restricted, + ) + get_related_klass_infos(klass_info, next_klass_infos) + + if restricted: + related_fields = [ + (o.field, o.related_model) + for o in opts.related_objects + if o.field.unique and not o.many_to_many + ] + for related_field, model in related_fields: + related_select_mask = select_mask.get(related_field) or {} + if not select_related_descend( + related_field, + restricted, + requested, + related_select_mask, + reverse=True, + ): + continue + + related_field_name = related_field.related_query_name() + fields_found.add(related_field_name) + + join_info = self.query.setup_joins( + [related_field_name], opts, root_alias + ) + alias = join_info.joins[-1] + from_parent = issubclass(model, opts.model) and model is not opts.model + klass_info = { + "model": model, + "field": related_field, + "reverse": True, + "local_setter": related_field.remote_field.set_cached_value, + "remote_setter": related_field.set_cached_value, + "from_parent": from_parent, + } + related_klass_infos.append(klass_info) + select_fields = [] + columns = self.get_default_columns( + related_select_mask, + start_alias=alias, + opts=model._meta, + from_parent=opts.model, + ) + for col in columns: + select_fields.append(len(select)) + select.append((col, None)) + klass_info["select_fields"] = select_fields + next = requested.get(related_field.related_query_name(), {}) + next_klass_infos = self.get_related_selections( + select, + related_select_mask, + model._meta, + alias, + cur_depth + 1, + next, + restricted, + ) + get_related_klass_infos(klass_info, next_klass_infos) + + def local_setter(final_field, obj, from_obj): + # Set a reverse fk object when relation is non-empty. + if from_obj: + final_field.remote_field.set_cached_value(from_obj, obj) + + def local_setter_noop(obj, from_obj): + pass + + def remote_setter(name, obj, from_obj): + setattr(from_obj, name, obj) + + for name in list(requested): + # Filtered relations work only on the topmost level. + if cur_depth > 1: + break + if name in self.query._filtered_relations: + fields_found.add(name) + final_field, _, join_opts, joins, _, _ = self.query.setup_joins( + [name], opts, root_alias + ) + model = join_opts.model + alias = joins[-1] + from_parent = ( + issubclass(model, opts.model) and model is not opts.model + ) + klass_info = { + "model": model, + "field": final_field, + "reverse": True, + "local_setter": ( + partial(local_setter, final_field) + if len(joins) <= 2 + else local_setter_noop + ), + "remote_setter": partial(remote_setter, name), + "from_parent": from_parent, + } + related_klass_infos.append(klass_info) + select_fields = [] + field_select_mask = select_mask.get((name, final_field)) or {} + columns = self.get_default_columns( + field_select_mask, + start_alias=alias, + opts=model._meta, + from_parent=opts.model, + ) + for col in columns: + select_fields.append(len(select)) + select.append((col, None)) + klass_info["select_fields"] = select_fields + next_requested = requested.get(name, {}) + next_klass_infos = self.get_related_selections( + select, + field_select_mask, + opts=model._meta, + root_alias=alias, + cur_depth=cur_depth + 1, + requested=next_requested, + restricted=restricted, + ) + get_related_klass_infos(klass_info, next_klass_infos) + fields_not_found = set(requested).difference(fields_found) + if fields_not_found: + invalid_fields = ("'%s'" % s for s in fields_not_found) + raise FieldError( + "Invalid field name(s) given in select_related: %s. " + "Choices are: %s" + % ( + ", ".join(invalid_fields), + ", ".join(_get_field_choices()) or "(none)", + ) + ) + return related_klass_infos + + def get_select_for_update_of_arguments(self): + """ + Return a quoted list of arguments for the SELECT FOR UPDATE OF part of + the query. + """ + + def _get_parent_klass_info(klass_info): + concrete_model = klass_info["model"]._meta.concrete_model + for parent_model, parent_link in concrete_model._meta.parents.items(): + parent_list = parent_model._meta.get_parent_list() + yield { + "model": parent_model, + "field": parent_link, + "reverse": False, + "select_fields": [ + select_index + for select_index in klass_info["select_fields"] + # Selected columns from a model or its parents. + if ( + self.select[select_index][0].target.model == parent_model + or self.select[select_index][0].target.model in parent_list + ) + ], + } + + def _get_first_selected_col_from_model(klass_info): + """ + Find the first selected column from a model. If it doesn't exist, + don't lock a model. + + select_fields is filled recursively, so it also contains fields + from the parent models. + """ + concrete_model = klass_info["model"]._meta.concrete_model + for select_index in klass_info["select_fields"]: + if self.select[select_index][0].target.model == concrete_model: + return self.select[select_index][0] + + def _get_field_choices(): + """Yield all allowed field paths in breadth-first search order.""" + queue = collections.deque([(None, self.klass_info)]) + while queue: + parent_path, klass_info = queue.popleft() + if parent_path is None: + path = [] + yield "self" + else: + field = klass_info["field"] + if klass_info["reverse"]: + field = field.remote_field + path = parent_path + [field.name] + yield LOOKUP_SEP.join(path) + queue.extend( + (path, klass_info) + for klass_info in _get_parent_klass_info(klass_info) + ) + queue.extend( + (path, klass_info) + for klass_info in klass_info.get("related_klass_infos", []) + ) + + if not self.klass_info: + return [] + result = [] + invalid_names = [] + for name in self.query.select_for_update_of: + klass_info = self.klass_info + if name == "self": + col = _get_first_selected_col_from_model(klass_info) + else: + for part in name.split(LOOKUP_SEP): + klass_infos = ( + *klass_info.get("related_klass_infos", []), + *_get_parent_klass_info(klass_info), + ) + for related_klass_info in klass_infos: + field = related_klass_info["field"] + if related_klass_info["reverse"]: + field = field.remote_field + if field.name == part: + klass_info = related_klass_info + break + else: + klass_info = None + break + if klass_info is None: + invalid_names.append(name) + continue + col = _get_first_selected_col_from_model(klass_info) + if col is not None: + if self.connection.features.select_for_update_of_column: + result.append(self.compile(col)[0]) + else: + result.append(self.quote_name_unless_alias(col.alias)) + if invalid_names: + raise FieldError( + "Invalid field name(s) given in select_for_update(of=(...)): %s. " + "Only relational fields followed in the query are allowed. " + "Choices are: %s." + % ( + ", ".join(invalid_names), + ", ".join(_get_field_choices()), + ) + ) + return result + + def get_converters(self, expressions): + converters = {} + for i, expression in enumerate(expressions): + if expression: + backend_converters = self.connection.ops.get_db_converters(expression) + field_converters = expression.get_db_converters(self.connection) + if backend_converters or field_converters: + converters[i] = (backend_converters + field_converters, expression) + return converters + + def apply_converters(self, rows, converters): + connection = self.connection + converters = list(converters.items()) + for row in map(list, rows): + for pos, (convs, expression) in converters: + value = row[pos] + for converter in convs: + value = converter(value, expression, connection) + row[pos] = value + yield row + + def results_iter( + self, + results=None, + tuple_expected=False, + chunked_fetch=False, + chunk_size=GET_ITERATOR_CHUNK_SIZE, + ): + """Return an iterator over the results from executing this query.""" + if results is None: + results = self.execute_sql( + MULTI, chunked_fetch=chunked_fetch, chunk_size=chunk_size + ) + fields = [s[0] for s in self.select[0 : self.col_count]] + converters = self.get_converters(fields) + rows = chain.from_iterable(results) + if converters: + rows = self.apply_converters(rows, converters) + if tuple_expected: + rows = map(tuple, rows) + return rows + + def has_results(self): + """ + Backends (e.g. NoSQL) can override this in order to use optimized + versions of "query has any results." + """ + return bool(self.execute_sql(SINGLE)) + + def execute_sql( + self, result_type=MULTI, chunked_fetch=False, chunk_size=GET_ITERATOR_CHUNK_SIZE + ): + """ + Run the query against the database and return the result(s). The + return value is a single data item if result_type is SINGLE, or an + iterator over the results if the result_type is MULTI. + + result_type is either MULTI (use fetchmany() to retrieve all rows), + SINGLE (only retrieve a single row), or None. In this last case, the + cursor is returned if any query is executed, since it's used by + subclasses such as InsertQuery). It's possible, however, that no query + is needed, as the filters describe an empty set. In that case, None is + returned, to avoid any unnecessary database interaction. + """ + result_type = result_type or NO_RESULTS + try: + sql, params = self.as_sql() + if not sql: + raise EmptyResultSet + except EmptyResultSet: + if result_type == MULTI: + return iter([]) + else: + return + if chunked_fetch: + cursor = self.connection.chunked_cursor() + else: + cursor = self.connection.cursor() + try: + cursor.execute(sql, params) + except Exception: + # Might fail for server-side cursors (e.g. connection closed) + cursor.close() + raise + + if result_type == CURSOR: + # Give the caller the cursor to process and close. + return cursor + if result_type == SINGLE: + try: + val = cursor.fetchone() + if val: + return val[0 : self.col_count] + return val + finally: + # done with the cursor + cursor.close() + if result_type == NO_RESULTS: + cursor.close() + return + + result = cursor_iter( + cursor, + self.connection.features.empty_fetchmany_value, + self.col_count if self.has_extra_select else None, + chunk_size, + ) + if not chunked_fetch or not self.connection.features.can_use_chunked_reads: + # If we are using non-chunked reads, we return the same data + # structure as normally, but ensure it is all read into memory + # before going any further. Use chunked_fetch if requested, + # unless the database doesn't support it. + return list(result) + return result + + def as_subquery_condition(self, alias, columns, compiler): + qn = compiler.quote_name_unless_alias + qn2 = self.connection.ops.quote_name + + for index, select_col in enumerate(self.query.select): + lhs_sql, lhs_params = self.compile(select_col) + rhs = "%s.%s" % (qn(alias), qn2(columns[index])) + self.query.where.add(RawSQL("%s = %s" % (lhs_sql, rhs), lhs_params), AND) + + sql, params = self.as_sql() + return "EXISTS (%s)" % sql, params + + def explain_query(self): + result = list(self.execute_sql()) + # Some backends return 1 item tuples with strings, and others return + # tuples with integers and strings. Flatten them out into strings. + format_ = self.query.explain_info.format + output_formatter = json.dumps if format_ and format_.lower() == "json" else str + for row in result[0]: + if not isinstance(row, str): + yield " ".join(output_formatter(c) for c in row) + else: + yield row + + +class SQLInsertCompiler(SQLCompiler): + returning_fields = None + returning_params = () + + def field_as_sql(self, field, val): + """ + Take a field and a value intended to be saved on that field, and + return placeholder SQL and accompanying params. Check for raw values, + expressions, and fields with get_placeholder() defined in that order. + + When field is None, consider the value raw and use it as the + placeholder, with no corresponding parameters returned. + """ + if field is None: + # A field value of None means the value is raw. + sql, params = val, [] + elif hasattr(val, "as_sql"): + # This is an expression, let's compile it. + sql, params = self.compile(val) + elif hasattr(field, "get_placeholder"): + # Some fields (e.g. geo fields) need special munging before + # they can be inserted. + sql, params = field.get_placeholder(val, self, self.connection), [val] + else: + # Return the common case for the placeholder + sql, params = "%s", [val] + + # The following hook is only used by Oracle Spatial, which sometimes + # needs to yield 'NULL' and [] as its placeholder and params instead + # of '%s' and [None]. The 'NULL' placeholder is produced earlier by + # OracleOperations.get_geom_placeholder(). The following line removes + # the corresponding None parameter. See ticket #10888. + params = self.connection.ops.modify_insert_params(sql, params) + + return sql, params + + def prepare_value(self, field, value): + """ + Prepare a value to be used in a query by resolving it if it is an + expression and otherwise calling the field's get_db_prep_save(). + """ + if hasattr(value, "resolve_expression"): + value = value.resolve_expression( + self.query, allow_joins=False, for_save=True + ) + # Don't allow values containing Col expressions. They refer to + # existing columns on a row, but in the case of insert the row + # doesn't exist yet. + if value.contains_column_references: + raise ValueError( + 'Failed to insert expression "%s" on %s. F() expressions ' + "can only be used to update, not to insert." % (value, field) + ) + if value.contains_aggregate: + raise FieldError( + "Aggregate functions are not allowed in this query " + "(%s=%r)." % (field.name, value) + ) + if value.contains_over_clause: + raise FieldError( + "Window expressions are not allowed in this query (%s=%r)." + % (field.name, value) + ) + return field.get_db_prep_save(value, connection=self.connection) + + def pre_save_val(self, field, obj): + """ + Get the given field's value off the given obj. pre_save() is used for + things like auto_now on DateTimeField. Skip it if this is a raw query. + """ + if self.query.raw: + return getattr(obj, field.attname) + return field.pre_save(obj, add=True) + + def assemble_as_sql(self, fields, value_rows): + """ + Take a sequence of N fields and a sequence of M rows of values, and + generate placeholder SQL and parameters for each field and value. + Return a pair containing: + * a sequence of M rows of N SQL placeholder strings, and + * a sequence of M rows of corresponding parameter values. + + Each placeholder string may contain any number of '%s' interpolation + strings, and each parameter row will contain exactly as many params + as the total number of '%s's in the corresponding placeholder row. + """ + if not value_rows: + return [], [] + + # list of (sql, [params]) tuples for each object to be saved + # Shape: [n_objs][n_fields][2] + rows_of_fields_as_sql = ( + (self.field_as_sql(field, v) for field, v in zip(fields, row)) + for row in value_rows + ) + + # tuple like ([sqls], [[params]s]) for each object to be saved + # Shape: [n_objs][2][n_fields] + sql_and_param_pair_rows = (zip(*row) for row in rows_of_fields_as_sql) + + # Extract separate lists for placeholders and params. + # Each of these has shape [n_objs][n_fields] + placeholder_rows, param_rows = zip(*sql_and_param_pair_rows) + + # Params for each field are still lists, and need to be flattened. + param_rows = [[p for ps in row for p in ps] for row in param_rows] + + return placeholder_rows, param_rows + + def as_sql(self): + # We don't need quote_name_unless_alias() here, since these are all + # going to be column names (so we can avoid the extra overhead). + qn = self.connection.ops.quote_name + opts = self.query.get_meta() + insert_statement = self.connection.ops.insert_statement( + on_conflict=self.query.on_conflict, + ) + result = ["%s %s" % (insert_statement, qn(opts.db_table))] + fields = self.query.fields or [opts.pk] + result.append("(%s)" % ", ".join(qn(f.column) for f in fields)) + + if self.query.fields: + value_rows = [ + [ + self.prepare_value(field, self.pre_save_val(field, obj)) + for field in fields + ] + for obj in self.query.objs + ] + else: + # An empty object. + value_rows = [ + [self.connection.ops.pk_default_value()] for _ in self.query.objs + ] + fields = [None] + + # Currently the backends just accept values when generating bulk + # queries and generate their own placeholders. Doing that isn't + # necessary and it should be possible to use placeholders and + # expressions in bulk inserts too. + can_bulk = ( + not self.returning_fields and self.connection.features.has_bulk_insert + ) + + placeholder_rows, param_rows = self.assemble_as_sql(fields, value_rows) + + on_conflict_suffix_sql = self.connection.ops.on_conflict_suffix_sql( + fields, + self.query.on_conflict, + (f.column for f in self.query.update_fields), + (f.column for f in self.query.unique_fields), + ) + if ( + self.returning_fields + and self.connection.features.can_return_columns_from_insert + ): + if self.connection.features.can_return_rows_from_bulk_insert: + result.append( + self.connection.ops.bulk_insert_sql(fields, placeholder_rows) + ) + params = param_rows + else: + result.append("VALUES (%s)" % ", ".join(placeholder_rows[0])) + params = [param_rows[0]] + if on_conflict_suffix_sql: + result.append(on_conflict_suffix_sql) + # Skip empty r_sql to allow subclasses to customize behavior for + # 3rd party backends. Refs #19096. + r_sql, self.returning_params = self.connection.ops.return_insert_columns( + self.returning_fields + ) + if r_sql: + result.append(r_sql) + params += [self.returning_params] + return [(" ".join(result), tuple(chain.from_iterable(params)))] + + if can_bulk: + result.append(self.connection.ops.bulk_insert_sql(fields, placeholder_rows)) + if on_conflict_suffix_sql: + result.append(on_conflict_suffix_sql) + return [(" ".join(result), tuple(p for ps in param_rows for p in ps))] + else: + if on_conflict_suffix_sql: + result.append(on_conflict_suffix_sql) + return [ + (" ".join(result + ["VALUES (%s)" % ", ".join(p)]), vals) + for p, vals in zip(placeholder_rows, param_rows) + ] + + def execute_sql(self, returning_fields=None): + assert not ( + returning_fields + and len(self.query.objs) != 1 + and not self.connection.features.can_return_rows_from_bulk_insert + ) + opts = self.query.get_meta() + self.returning_fields = returning_fields + with self.connection.cursor() as cursor: + for sql, params in self.as_sql(): + cursor.execute(sql, params) + if not self.returning_fields: + return [] + if ( + self.connection.features.can_return_rows_from_bulk_insert + and len(self.query.objs) > 1 + ): + rows = self.connection.ops.fetch_returned_insert_rows(cursor) + elif self.connection.features.can_return_columns_from_insert: + assert len(self.query.objs) == 1 + rows = [ + self.connection.ops.fetch_returned_insert_columns( + cursor, + self.returning_params, + ) + ] + else: + rows = [ + ( + self.connection.ops.last_insert_id( + cursor, + opts.db_table, + opts.pk.column, + ), + ) + ] + cols = [field.get_col(opts.db_table) for field in self.returning_fields] + converters = self.get_converters(cols) + if converters: + rows = list(self.apply_converters(rows, converters)) + return rows + + +class SQLDeleteCompiler(SQLCompiler): + @cached_property + def single_alias(self): + # Ensure base table is in aliases. + self.query.get_initial_alias() + return sum(self.query.alias_refcount[t] > 0 for t in self.query.alias_map) == 1 + + @classmethod + def _expr_refs_base_model(cls, expr, base_model): + if isinstance(expr, Query): + return expr.model == base_model + if not hasattr(expr, "get_source_expressions"): + return False + return any( + cls._expr_refs_base_model(source_expr, base_model) + for source_expr in expr.get_source_expressions() + ) + + @cached_property + def contains_self_reference_subquery(self): + return any( + self._expr_refs_base_model(expr, self.query.model) + for expr in chain( + self.query.annotations.values(), self.query.where.children + ) + ) + + def _as_sql(self, query): + delete = "DELETE FROM %s" % self.quote_name_unless_alias(query.base_table) + try: + where, params = self.compile(query.where) + except FullResultSet: + return delete, () + return f"{delete} WHERE {where}", tuple(params) + + def as_sql(self): + """ + Create the SQL for this query. Return the SQL string and list of + parameters. + """ + if self.single_alias and not self.contains_self_reference_subquery: + return self._as_sql(self.query) + innerq = self.query.clone() + innerq.__class__ = Query + innerq.clear_select_clause() + pk = self.query.model._meta.pk + innerq.select = [pk.get_col(self.query.get_initial_alias())] + outerq = Query(self.query.model) + if not self.connection.features.update_can_self_select: + # Force the materialization of the inner query to allow reference + # to the target table on MySQL. + sql, params = innerq.get_compiler(connection=self.connection).as_sql() + innerq = RawSQL("SELECT * FROM (%s) subquery" % sql, params) + outerq.add_filter("pk__in", innerq) + return self._as_sql(outerq) + + +class SQLUpdateCompiler(SQLCompiler): + def as_sql(self): + """ + Create the SQL for this query. Return the SQL string and list of + parameters. + """ + self.pre_sql_setup() + if not self.query.values: + return "", () + qn = self.quote_name_unless_alias + values, update_params = [], [] + for field, model, val in self.query.values: + if hasattr(val, "resolve_expression"): + val = val.resolve_expression( + self.query, allow_joins=False, for_save=True + ) + if val.contains_aggregate: + raise FieldError( + "Aggregate functions are not allowed in this query " + "(%s=%r)." % (field.name, val) + ) + if val.contains_over_clause: + raise FieldError( + "Window expressions are not allowed in this query " + "(%s=%r)." % (field.name, val) + ) + elif hasattr(val, "prepare_database_save"): + if field.remote_field: + val = val.prepare_database_save(field) + else: + raise TypeError( + "Tried to update field %s with a model instance, %r. " + "Use a value compatible with %s." + % (field, val, field.__class__.__name__) + ) + val = field.get_db_prep_save(val, connection=self.connection) + + # Getting the placeholder for the field. + if hasattr(field, "get_placeholder"): + placeholder = field.get_placeholder(val, self, self.connection) + else: + placeholder = "%s" + name = field.column + if hasattr(val, "as_sql"): + sql, params = self.compile(val) + values.append("%s = %s" % (qn(name), placeholder % sql)) + update_params.extend(params) + elif val is not None: + values.append("%s = %s" % (qn(name), placeholder)) + update_params.append(val) + else: + values.append("%s = NULL" % qn(name)) + table = self.query.base_table + result = [ + "UPDATE %s SET" % qn(table), + ", ".join(values), + ] + try: + where, params = self.compile(self.query.where) + except FullResultSet: + params = [] + else: + result.append("WHERE %s" % where) + return " ".join(result), tuple(update_params + params) + + def execute_sql(self, result_type): + """ + Execute the specified update. Return the number of rows affected by + the primary update query. The "primary update query" is the first + non-empty query that is executed. Row counts for any subsequent, + related queries are not available. + """ + cursor = super().execute_sql(result_type) + try: + rows = cursor.rowcount if cursor else 0 + is_empty = cursor is None + finally: + if cursor: + cursor.close() + for query in self.query.get_related_updates(): + aux_rows = query.get_compiler(self.using).execute_sql(result_type) + if is_empty and aux_rows: + rows = aux_rows + is_empty = False + return rows + + def pre_sql_setup(self): + """ + If the update depends on results from other tables, munge the "where" + conditions to match the format required for (portable) SQL updates. + + If multiple updates are required, pull out the id values to update at + this point so that they don't change as a result of the progressive + updates. + """ + refcounts_before = self.query.alias_refcount.copy() + # Ensure base table is in the query + self.query.get_initial_alias() + count = self.query.count_active_tables() + if not self.query.related_updates and count == 1: + return + query = self.query.chain(klass=Query) + query.select_related = False + query.clear_ordering(force=True) + query.extra = {} + query.select = [] + meta = query.get_meta() + fields = [meta.pk.name] + related_ids_index = [] + for related in self.query.related_updates: + if all( + path.join_field.primary_key for path in meta.get_path_to_parent(related) + ): + # If a primary key chain exists to the targeted related update, + # then the meta.pk value can be used for it. + related_ids_index.append((related, 0)) + else: + # This branch will only be reached when updating a field of an + # ancestor that is not part of the primary key chain of a MTI + # tree. + related_ids_index.append((related, len(fields))) + fields.append(related._meta.pk.name) + query.add_fields(fields) + super().pre_sql_setup() + + must_pre_select = ( + count > 1 and not self.connection.features.update_can_self_select + ) + + # Now we adjust the current query: reset the where clause and get rid + # of all the tables we don't need (since they're in the sub-select). + self.query.clear_where() + if self.query.related_updates or must_pre_select: + # Either we're using the idents in multiple update queries (so + # don't want them to change), or the db backend doesn't support + # selecting from the updating table (e.g. MySQL). + idents = [] + related_ids = collections.defaultdict(list) + for rows in query.get_compiler(self.using).execute_sql(MULTI): + idents.extend(r[0] for r in rows) + for parent, index in related_ids_index: + related_ids[parent].extend(r[index] for r in rows) + self.query.add_filter("pk__in", idents) + self.query.related_ids = related_ids + else: + # The fast path. Filters and updates in one query. + self.query.add_filter("pk__in", query) + self.query.reset_refcounts(refcounts_before) + + +class SQLAggregateCompiler(SQLCompiler): + def as_sql(self): + """ + Create the SQL for this query. Return the SQL string and list of + parameters. + """ + sql, params = [], [] + for annotation in self.query.annotation_select.values(): + ann_sql, ann_params = self.compile(annotation) + ann_sql, ann_params = annotation.select_format(self, ann_sql, ann_params) + sql.append(ann_sql) + params.extend(ann_params) + self.col_count = len(self.query.annotation_select) + sql = ", ".join(sql) + params = tuple(params) + + inner_query_sql, inner_query_params = self.query.inner_query.get_compiler( + self.using, + elide_empty=self.elide_empty, + ).as_sql(with_col_aliases=True) + sql = "SELECT %s FROM (%s) subquery" % (sql, inner_query_sql) + params += inner_query_params + return sql, params + + +def cursor_iter(cursor, sentinel, col_count, itersize): + """ + Yield blocks of rows from a cursor and ensure the cursor is closed when + done. + """ + try: + for rows in iter((lambda: cursor.fetchmany(itersize)), sentinel): + yield rows if col_count is None else [r[:col_count] for r in rows] + finally: + cursor.close() diff --git a/virt/lib/python3.9/site-packages/django/forms/fields 3.py b/virt/lib/python3.9/site-packages/django/forms/fields 3.py new file mode 100644 index 00000000..65cd9675 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/forms/fields 3.py @@ -0,0 +1,1391 @@ +""" +Field classes. +""" + +import copy +import datetime +import json +import math +import operator +import os +import re +import uuid +from decimal import Decimal, DecimalException +from io import BytesIO +from urllib.parse import urlsplit, urlunsplit + +from django.core import validators +from django.core.exceptions import ValidationError +from django.forms.boundfield import BoundField +from django.forms.utils import from_current_timezone, to_current_timezone +from django.forms.widgets import ( + FILE_INPUT_CONTRADICTION, + CheckboxInput, + ClearableFileInput, + DateInput, + DateTimeInput, + EmailInput, + FileInput, + HiddenInput, + MultipleHiddenInput, + NullBooleanSelect, + NumberInput, + Select, + SelectMultiple, + SplitDateTimeWidget, + SplitHiddenDateTimeWidget, + Textarea, + TextInput, + TimeInput, + URLInput, +) +from django.utils import formats +from django.utils.dateparse import parse_datetime, parse_duration +from django.utils.duration import duration_string +from django.utils.ipv6 import clean_ipv6_address +from django.utils.regex_helper import _lazy_re_compile +from django.utils.translation import gettext_lazy as _ +from django.utils.translation import ngettext_lazy + +__all__ = ( + "Field", + "CharField", + "IntegerField", + "DateField", + "TimeField", + "DateTimeField", + "DurationField", + "RegexField", + "EmailField", + "FileField", + "ImageField", + "URLField", + "BooleanField", + "NullBooleanField", + "ChoiceField", + "MultipleChoiceField", + "ComboField", + "MultiValueField", + "FloatField", + "DecimalField", + "SplitDateTimeField", + "GenericIPAddressField", + "FilePathField", + "JSONField", + "SlugField", + "TypedChoiceField", + "TypedMultipleChoiceField", + "UUIDField", +) + + +class Field: + widget = TextInput # Default widget to use when rendering this type of Field. + hidden_widget = ( + HiddenInput # Default widget to use when rendering this as "hidden". + ) + default_validators = [] # Default set of validators + # Add an 'invalid' entry to default_error_message if you want a specific + # field error message not raised by the field validators. + default_error_messages = { + "required": _("This field is required."), + } + empty_values = list(validators.EMPTY_VALUES) + + def __init__( + self, + *, + required=True, + widget=None, + label=None, + initial=None, + help_text="", + error_messages=None, + show_hidden_initial=False, + validators=(), + localize=False, + disabled=False, + label_suffix=None, + ): + # required -- Boolean that specifies whether the field is required. + # True by default. + # widget -- A Widget class, or instance of a Widget class, that should + # be used for this Field when displaying it. Each Field has a + # default Widget that it'll use if you don't specify this. In + # most cases, the default widget is TextInput. + # label -- A verbose name for this field, for use in displaying this + # field in a form. By default, Django will use a "pretty" + # version of the form field name, if the Field is part of a + # Form. + # initial -- A value to use in this Field's initial display. This value + # is *not* used as a fallback if data isn't given. + # help_text -- An optional string to use as "help text" for this Field. + # error_messages -- An optional dictionary to override the default + # messages that the field will raise. + # show_hidden_initial -- Boolean that specifies if it is needed to render a + # hidden widget with initial value after widget. + # validators -- List of additional validators to use + # localize -- Boolean that specifies if the field should be localized. + # disabled -- Boolean that specifies whether the field is disabled, that + # is its widget is shown in the form but not editable. + # label_suffix -- Suffix to be added to the label. Overrides + # form's label_suffix. + self.required, self.label, self.initial = required, label, initial + self.show_hidden_initial = show_hidden_initial + self.help_text = help_text + self.disabled = disabled + self.label_suffix = label_suffix + widget = widget or self.widget + if isinstance(widget, type): + widget = widget() + else: + widget = copy.deepcopy(widget) + + # Trigger the localization machinery if needed. + self.localize = localize + if self.localize: + widget.is_localized = True + + # Let the widget know whether it should display as required. + widget.is_required = self.required + + # Hook into self.widget_attrs() for any Field-specific HTML attributes. + extra_attrs = self.widget_attrs(widget) + if extra_attrs: + widget.attrs.update(extra_attrs) + + self.widget = widget + + messages = {} + for c in reversed(self.__class__.__mro__): + messages.update(getattr(c, "default_error_messages", {})) + messages.update(error_messages or {}) + self.error_messages = messages + + self.validators = [*self.default_validators, *validators] + + super().__init__() + + def prepare_value(self, value): + return value + + def to_python(self, value): + return value + + def validate(self, value): + if value in self.empty_values and self.required: + raise ValidationError(self.error_messages["required"], code="required") + + def run_validators(self, value): + if value in self.empty_values: + return + errors = [] + for v in self.validators: + try: + v(value) + except ValidationError as e: + if hasattr(e, "code") and e.code in self.error_messages: + e.message = self.error_messages[e.code] + errors.extend(e.error_list) + if errors: + raise ValidationError(errors) + + def clean(self, value): + """ + Validate the given value and return its "cleaned" value as an + appropriate Python object. Raise ValidationError for any errors. + """ + value = self.to_python(value) + self.validate(value) + self.run_validators(value) + return value + + def bound_data(self, data, initial): + """ + Return the value that should be shown for this field on render of a + bound form, given the submitted POST data for the field and the initial + data, if any. + + For most fields, this will simply be data; FileFields need to handle it + a bit differently. + """ + if self.disabled: + return initial + return data + + def widget_attrs(self, widget): + """ + Given a Widget instance (*not* a Widget class), return a dictionary of + any HTML attributes that should be added to the Widget, based on this + Field. + """ + return {} + + def has_changed(self, initial, data): + """Return True if data differs from initial.""" + # Always return False if the field is disabled since self.bound_data + # always uses the initial value in this case. + if self.disabled: + return False + try: + data = self.to_python(data) + if hasattr(self, "_coerce"): + return self._coerce(data) != self._coerce(initial) + except ValidationError: + return True + # For purposes of seeing whether something has changed, None is + # the same as an empty string, if the data or initial value we get + # is None, replace it with ''. + initial_value = initial if initial is not None else "" + data_value = data if data is not None else "" + return initial_value != data_value + + def get_bound_field(self, form, field_name): + """ + Return a BoundField instance that will be used when accessing the form + field in a template. + """ + return BoundField(form, self, field_name) + + def __deepcopy__(self, memo): + result = copy.copy(self) + memo[id(self)] = result + result.widget = copy.deepcopy(self.widget, memo) + result.error_messages = self.error_messages.copy() + result.validators = self.validators[:] + return result + + +class CharField(Field): + def __init__( + self, *, max_length=None, min_length=None, strip=True, empty_value="", **kwargs + ): + self.max_length = max_length + self.min_length = min_length + self.strip = strip + self.empty_value = empty_value + super().__init__(**kwargs) + if min_length is not None: + self.validators.append(validators.MinLengthValidator(int(min_length))) + if max_length is not None: + self.validators.append(validators.MaxLengthValidator(int(max_length))) + self.validators.append(validators.ProhibitNullCharactersValidator()) + + def to_python(self, value): + """Return a string.""" + if value not in self.empty_values: + value = str(value) + if self.strip: + value = value.strip() + if value in self.empty_values: + return self.empty_value + return value + + def widget_attrs(self, widget): + attrs = super().widget_attrs(widget) + if self.max_length is not None and not widget.is_hidden: + # The HTML attribute is maxlength, not max_length. + attrs["maxlength"] = str(self.max_length) + if self.min_length is not None and not widget.is_hidden: + # The HTML attribute is minlength, not min_length. + attrs["minlength"] = str(self.min_length) + return attrs + + +class IntegerField(Field): + widget = NumberInput + default_error_messages = { + "invalid": _("Enter a whole number."), + } + re_decimal = _lazy_re_compile(r"\.0*\s*$") + + def __init__(self, *, max_value=None, min_value=None, step_size=None, **kwargs): + self.max_value, self.min_value, self.step_size = max_value, min_value, step_size + if kwargs.get("localize") and self.widget == NumberInput: + # Localized number input is not well supported on most browsers + kwargs.setdefault("widget", super().widget) + super().__init__(**kwargs) + + if max_value is not None: + self.validators.append(validators.MaxValueValidator(max_value)) + if min_value is not None: + self.validators.append(validators.MinValueValidator(min_value)) + if step_size is not None: + self.validators.append(validators.StepValueValidator(step_size)) + + def to_python(self, value): + """ + Validate that int() can be called on the input. Return the result + of int() or None for empty values. + """ + value = super().to_python(value) + if value in self.empty_values: + return None + if self.localize: + value = formats.sanitize_separators(value) + # Strip trailing decimal and zeros. + try: + value = int(self.re_decimal.sub("", str(value))) + except (ValueError, TypeError): + raise ValidationError(self.error_messages["invalid"], code="invalid") + return value + + def widget_attrs(self, widget): + attrs = super().widget_attrs(widget) + if isinstance(widget, NumberInput): + if self.min_value is not None: + attrs["min"] = self.min_value + if self.max_value is not None: + attrs["max"] = self.max_value + if self.step_size is not None: + attrs["step"] = self.step_size + return attrs + + +class FloatField(IntegerField): + default_error_messages = { + "invalid": _("Enter a number."), + } + + def to_python(self, value): + """ + Validate that float() can be called on the input. Return the result + of float() or None for empty values. + """ + value = super(IntegerField, self).to_python(value) + if value in self.empty_values: + return None + if self.localize: + value = formats.sanitize_separators(value) + try: + value = float(value) + except (ValueError, TypeError): + raise ValidationError(self.error_messages["invalid"], code="invalid") + return value + + def validate(self, value): + super().validate(value) + if value in self.empty_values: + return + if not math.isfinite(value): + raise ValidationError(self.error_messages["invalid"], code="invalid") + + def widget_attrs(self, widget): + attrs = super().widget_attrs(widget) + if isinstance(widget, NumberInput) and "step" not in widget.attrs: + if self.step_size is not None: + step = str(self.step_size) + else: + step = "any" + attrs.setdefault("step", step) + return attrs + + +class DecimalField(IntegerField): + default_error_messages = { + "invalid": _("Enter a number."), + } + + def __init__( + self, + *, + max_value=None, + min_value=None, + max_digits=None, + decimal_places=None, + **kwargs, + ): + self.max_digits, self.decimal_places = max_digits, decimal_places + super().__init__(max_value=max_value, min_value=min_value, **kwargs) + self.validators.append(validators.DecimalValidator(max_digits, decimal_places)) + + def to_python(self, value): + """ + Validate that the input is a decimal number. Return a Decimal + instance or None for empty values. Ensure that there are no more + than max_digits in the number and no more than decimal_places digits + after the decimal point. + """ + if value in self.empty_values: + return None + if self.localize: + value = formats.sanitize_separators(value) + try: + value = Decimal(str(value)) + except DecimalException: + raise ValidationError(self.error_messages["invalid"], code="invalid") + return value + + def validate(self, value): + super().validate(value) + if value in self.empty_values: + return + if not value.is_finite(): + raise ValidationError( + self.error_messages["invalid"], + code="invalid", + params={"value": value}, + ) + + def widget_attrs(self, widget): + attrs = super().widget_attrs(widget) + if isinstance(widget, NumberInput) and "step" not in widget.attrs: + if self.decimal_places is not None: + # Use exponential notation for small values since they might + # be parsed as 0 otherwise. ref #20765 + step = str(Decimal(1).scaleb(-self.decimal_places)).lower() + else: + step = "any" + attrs.setdefault("step", step) + return attrs + + +class BaseTemporalField(Field): + def __init__(self, *, input_formats=None, **kwargs): + super().__init__(**kwargs) + if input_formats is not None: + self.input_formats = input_formats + + def to_python(self, value): + value = value.strip() + # Try to strptime against each input format. + for format in self.input_formats: + try: + return self.strptime(value, format) + except (ValueError, TypeError): + continue + raise ValidationError(self.error_messages["invalid"], code="invalid") + + def strptime(self, value, format): + raise NotImplementedError("Subclasses must define this method.") + + +class DateField(BaseTemporalField): + widget = DateInput + input_formats = formats.get_format_lazy("DATE_INPUT_FORMATS") + default_error_messages = { + "invalid": _("Enter a valid date."), + } + + def to_python(self, value): + """ + Validate that the input can be converted to a date. Return a Python + datetime.date object. + """ + if value in self.empty_values: + return None + if isinstance(value, datetime.datetime): + return value.date() + if isinstance(value, datetime.date): + return value + return super().to_python(value) + + def strptime(self, value, format): + return datetime.datetime.strptime(value, format).date() + + +class TimeField(BaseTemporalField): + widget = TimeInput + input_formats = formats.get_format_lazy("TIME_INPUT_FORMATS") + default_error_messages = {"invalid": _("Enter a valid time.")} + + def to_python(self, value): + """ + Validate that the input can be converted to a time. Return a Python + datetime.time object. + """ + if value in self.empty_values: + return None + if isinstance(value, datetime.time): + return value + return super().to_python(value) + + def strptime(self, value, format): + return datetime.datetime.strptime(value, format).time() + + +class DateTimeFormatsIterator: + def __iter__(self): + yield from formats.get_format("DATETIME_INPUT_FORMATS") + yield from formats.get_format("DATE_INPUT_FORMATS") + + +class DateTimeField(BaseTemporalField): + widget = DateTimeInput + input_formats = DateTimeFormatsIterator() + default_error_messages = { + "invalid": _("Enter a valid date/time."), + } + + def prepare_value(self, value): + if isinstance(value, datetime.datetime): + value = to_current_timezone(value) + return value + + def to_python(self, value): + """ + Validate that the input can be converted to a datetime. Return a + Python datetime.datetime object. + """ + if value in self.empty_values: + return None + if isinstance(value, datetime.datetime): + return from_current_timezone(value) + if isinstance(value, datetime.date): + result = datetime.datetime(value.year, value.month, value.day) + return from_current_timezone(result) + try: + result = parse_datetime(value.strip()) + except ValueError: + raise ValidationError(self.error_messages["invalid"], code="invalid") + if not result: + result = super().to_python(value) + return from_current_timezone(result) + + def strptime(self, value, format): + return datetime.datetime.strptime(value, format) + + +class DurationField(Field): + default_error_messages = { + "invalid": _("Enter a valid duration."), + "overflow": _("The number of days must be between {min_days} and {max_days}."), + } + + def prepare_value(self, value): + if isinstance(value, datetime.timedelta): + return duration_string(value) + return value + + def to_python(self, value): + if value in self.empty_values: + return None + if isinstance(value, datetime.timedelta): + return value + try: + value = parse_duration(str(value)) + except OverflowError: + raise ValidationError( + self.error_messages["overflow"].format( + min_days=datetime.timedelta.min.days, + max_days=datetime.timedelta.max.days, + ), + code="overflow", + ) + if value is None: + raise ValidationError(self.error_messages["invalid"], code="invalid") + return value + + +class RegexField(CharField): + def __init__(self, regex, **kwargs): + """ + regex can be either a string or a compiled regular expression object. + """ + kwargs.setdefault("strip", False) + super().__init__(**kwargs) + self._set_regex(regex) + + def _get_regex(self): + return self._regex + + def _set_regex(self, regex): + if isinstance(regex, str): + regex = re.compile(regex) + self._regex = regex + if ( + hasattr(self, "_regex_validator") + and self._regex_validator in self.validators + ): + self.validators.remove(self._regex_validator) + self._regex_validator = validators.RegexValidator(regex=regex) + self.validators.append(self._regex_validator) + + regex = property(_get_regex, _set_regex) + + +class EmailField(CharField): + widget = EmailInput + default_validators = [validators.validate_email] + + def __init__(self, **kwargs): + # The default maximum length of an email is 320 characters per RFC 3696 + # section 3. + kwargs.setdefault("max_length", 320) + super().__init__(strip=True, **kwargs) + + +class FileField(Field): + widget = ClearableFileInput + default_error_messages = { + "invalid": _("No file was submitted. Check the encoding type on the form."), + "missing": _("No file was submitted."), + "empty": _("The submitted file is empty."), + "max_length": ngettext_lazy( + "Ensure this filename has at most %(max)d character (it has %(length)d).", + "Ensure this filename has at most %(max)d characters (it has %(length)d).", + "max", + ), + "contradiction": _( + "Please either submit a file or check the clear checkbox, not both." + ), + } + + def __init__(self, *, max_length=None, allow_empty_file=False, **kwargs): + self.max_length = max_length + self.allow_empty_file = allow_empty_file + super().__init__(**kwargs) + + def to_python(self, data): + if data in self.empty_values: + return None + + # UploadedFile objects should have name and size attributes. + try: + file_name = data.name + file_size = data.size + except AttributeError: + raise ValidationError(self.error_messages["invalid"], code="invalid") + + if self.max_length is not None and len(file_name) > self.max_length: + params = {"max": self.max_length, "length": len(file_name)} + raise ValidationError( + self.error_messages["max_length"], code="max_length", params=params + ) + if not file_name: + raise ValidationError(self.error_messages["invalid"], code="invalid") + if not self.allow_empty_file and not file_size: + raise ValidationError(self.error_messages["empty"], code="empty") + + return data + + def clean(self, data, initial=None): + # If the widget got contradictory inputs, we raise a validation error + if data is FILE_INPUT_CONTRADICTION: + raise ValidationError( + self.error_messages["contradiction"], code="contradiction" + ) + # False means the field value should be cleared; further validation is + # not needed. + if data is False: + if not self.required: + return False + # If the field is required, clearing is not possible (the widget + # shouldn't return False data in that case anyway). False is not + # in self.empty_value; if a False value makes it this far + # it should be validated from here on out as None (so it will be + # caught by the required check). + data = None + if not data and initial: + return initial + return super().clean(data) + + def bound_data(self, _, initial): + return initial + + def has_changed(self, initial, data): + return not self.disabled and data is not None + + +class ImageField(FileField): + default_validators = [validators.validate_image_file_extension] + default_error_messages = { + "invalid_image": _( + "Upload a valid image. The file you uploaded was either not an " + "image or a corrupted image." + ), + } + + def to_python(self, data): + """ + Check that the file-upload field data contains a valid image (GIF, JPG, + PNG, etc. -- whatever Pillow supports). + """ + f = super().to_python(data) + if f is None: + return None + + from PIL import Image + + # We need to get a file object for Pillow. We might have a path or we might + # have to read the data into memory. + if hasattr(data, "temporary_file_path"): + file = data.temporary_file_path() + else: + if hasattr(data, "read"): + file = BytesIO(data.read()) + else: + file = BytesIO(data["content"]) + + try: + # load() could spot a truncated JPEG, but it loads the entire + # image in memory, which is a DoS vector. See #3848 and #18520. + image = Image.open(file) + # verify() must be called immediately after the constructor. + image.verify() + + # Annotating so subclasses can reuse it for their own validation + f.image = image + # Pillow doesn't detect the MIME type of all formats. In those + # cases, content_type will be None. + f.content_type = Image.MIME.get(image.format) + except Exception as exc: + # Pillow doesn't recognize it as an image. + raise ValidationError( + self.error_messages["invalid_image"], + code="invalid_image", + ) from exc + if hasattr(f, "seek") and callable(f.seek): + f.seek(0) + return f + + def widget_attrs(self, widget): + attrs = super().widget_attrs(widget) + if isinstance(widget, FileInput) and "accept" not in widget.attrs: + attrs.setdefault("accept", "image/*") + return attrs + + +class URLField(CharField): + widget = URLInput + default_error_messages = { + "invalid": _("Enter a valid URL."), + } + default_validators = [validators.URLValidator()] + + def __init__(self, **kwargs): + super().__init__(strip=True, **kwargs) + + def to_python(self, value): + def split_url(url): + """ + Return a list of url parts via urlparse.urlsplit(), or raise + ValidationError for some malformed URLs. + """ + try: + return list(urlsplit(url)) + except ValueError: + # urlparse.urlsplit can raise a ValueError with some + # misformatted URLs. + raise ValidationError(self.error_messages["invalid"], code="invalid") + + value = super().to_python(value) + if value: + url_fields = split_url(value) + if not url_fields[0]: + # If no URL scheme given, assume http:// + url_fields[0] = "http" + if not url_fields[1]: + # Assume that if no domain is provided, that the path segment + # contains the domain. + url_fields[1] = url_fields[2] + url_fields[2] = "" + # Rebuild the url_fields list, since the domain segment may now + # contain the path too. + url_fields = split_url(urlunsplit(url_fields)) + value = urlunsplit(url_fields) + return value + + +class BooleanField(Field): + widget = CheckboxInput + + def to_python(self, value): + """Return a Python boolean object.""" + # Explicitly check for the string 'False', which is what a hidden field + # will submit for False. Also check for '0', since this is what + # RadioSelect will provide. Because bool("True") == bool('1') == True, + # we don't need to handle that explicitly. + if isinstance(value, str) and value.lower() in ("false", "0"): + value = False + else: + value = bool(value) + return super().to_python(value) + + def validate(self, value): + if not value and self.required: + raise ValidationError(self.error_messages["required"], code="required") + + def has_changed(self, initial, data): + if self.disabled: + return False + # Sometimes data or initial may be a string equivalent of a boolean + # so we should run it through to_python first to get a boolean value + return self.to_python(initial) != self.to_python(data) + + +class NullBooleanField(BooleanField): + """ + A field whose valid values are None, True, and False. Clean invalid values + to None. + """ + + widget = NullBooleanSelect + + def to_python(self, value): + """ + Explicitly check for the string 'True' and 'False', which is what a + hidden field will submit for True and False, for 'true' and 'false', + which are likely to be returned by JavaScript serializations of forms, + and for '1' and '0', which is what a RadioField will submit. Unlike + the Booleanfield, this field must check for True because it doesn't + use the bool() function. + """ + if value in (True, "True", "true", "1"): + return True + elif value in (False, "False", "false", "0"): + return False + else: + return None + + def validate(self, value): + pass + + +class CallableChoiceIterator: + def __init__(self, choices_func): + self.choices_func = choices_func + + def __iter__(self): + yield from self.choices_func() + + +class ChoiceField(Field): + widget = Select + default_error_messages = { + "invalid_choice": _( + "Select a valid choice. %(value)s is not one of the available choices." + ), + } + + def __init__(self, *, choices=(), **kwargs): + super().__init__(**kwargs) + self.choices = choices + + def __deepcopy__(self, memo): + result = super().__deepcopy__(memo) + result._choices = copy.deepcopy(self._choices, memo) + return result + + def _get_choices(self): + return self._choices + + def _set_choices(self, value): + # Setting choices also sets the choices on the widget. + # choices can be any iterable, but we call list() on it because + # it will be consumed more than once. + if callable(value): + value = CallableChoiceIterator(value) + else: + value = list(value) + + self._choices = self.widget.choices = value + + choices = property(_get_choices, _set_choices) + + def to_python(self, value): + """Return a string.""" + if value in self.empty_values: + return "" + return str(value) + + def validate(self, value): + """Validate that the input is in self.choices.""" + super().validate(value) + if value and not self.valid_value(value): + raise ValidationError( + self.error_messages["invalid_choice"], + code="invalid_choice", + params={"value": value}, + ) + + def valid_value(self, value): + """Check to see if the provided value is a valid choice.""" + text_value = str(value) + for k, v in self.choices: + if isinstance(v, (list, tuple)): + # This is an optgroup, so look inside the group for options + for k2, v2 in v: + if value == k2 or text_value == str(k2): + return True + else: + if value == k or text_value == str(k): + return True + return False + + +class TypedChoiceField(ChoiceField): + def __init__(self, *, coerce=lambda val: val, empty_value="", **kwargs): + self.coerce = coerce + self.empty_value = empty_value + super().__init__(**kwargs) + + def _coerce(self, value): + """ + Validate that the value can be coerced to the right type (if not empty). + """ + if value == self.empty_value or value in self.empty_values: + return self.empty_value + try: + value = self.coerce(value) + except (ValueError, TypeError, ValidationError): + raise ValidationError( + self.error_messages["invalid_choice"], + code="invalid_choice", + params={"value": value}, + ) + return value + + def clean(self, value): + value = super().clean(value) + return self._coerce(value) + + +class MultipleChoiceField(ChoiceField): + hidden_widget = MultipleHiddenInput + widget = SelectMultiple + default_error_messages = { + "invalid_choice": _( + "Select a valid choice. %(value)s is not one of the available choices." + ), + "invalid_list": _("Enter a list of values."), + } + + def to_python(self, value): + if not value: + return [] + elif not isinstance(value, (list, tuple)): + raise ValidationError( + self.error_messages["invalid_list"], code="invalid_list" + ) + return [str(val) for val in value] + + def validate(self, value): + """Validate that the input is a list or tuple.""" + if self.required and not value: + raise ValidationError(self.error_messages["required"], code="required") + # Validate that each value in the value list is in self.choices. + for val in value: + if not self.valid_value(val): + raise ValidationError( + self.error_messages["invalid_choice"], + code="invalid_choice", + params={"value": val}, + ) + + def has_changed(self, initial, data): + if self.disabled: + return False + if initial is None: + initial = [] + if data is None: + data = [] + if len(initial) != len(data): + return True + initial_set = {str(value) for value in initial} + data_set = {str(value) for value in data} + return data_set != initial_set + + +class TypedMultipleChoiceField(MultipleChoiceField): + def __init__(self, *, coerce=lambda val: val, **kwargs): + self.coerce = coerce + self.empty_value = kwargs.pop("empty_value", []) + super().__init__(**kwargs) + + def _coerce(self, value): + """ + Validate that the values are in self.choices and can be coerced to the + right type. + """ + if value == self.empty_value or value in self.empty_values: + return self.empty_value + new_value = [] + for choice in value: + try: + new_value.append(self.coerce(choice)) + except (ValueError, TypeError, ValidationError): + raise ValidationError( + self.error_messages["invalid_choice"], + code="invalid_choice", + params={"value": choice}, + ) + return new_value + + def clean(self, value): + value = super().clean(value) + return self._coerce(value) + + def validate(self, value): + if value != self.empty_value: + super().validate(value) + elif self.required: + raise ValidationError(self.error_messages["required"], code="required") + + +class ComboField(Field): + """ + A Field whose clean() method calls multiple Field clean() methods. + """ + + def __init__(self, fields, **kwargs): + super().__init__(**kwargs) + # Set 'required' to False on the individual fields, because the + # required validation will be handled by ComboField, not by those + # individual fields. + for f in fields: + f.required = False + self.fields = fields + + def clean(self, value): + """ + Validate the given value against all of self.fields, which is a + list of Field instances. + """ + super().clean(value) + for field in self.fields: + value = field.clean(value) + return value + + +class MultiValueField(Field): + """ + Aggregate the logic of multiple Fields. + + Its clean() method takes a "decompressed" list of values, which are then + cleaned into a single value according to self.fields. Each value in + this list is cleaned by the corresponding field -- the first value is + cleaned by the first field, the second value is cleaned by the second + field, etc. Once all fields are cleaned, the list of clean values is + "compressed" into a single value. + + Subclasses should not have to implement clean(). Instead, they must + implement compress(), which takes a list of valid values and returns a + "compressed" version of those values -- a single value. + + You'll probably want to use this with MultiWidget. + """ + + default_error_messages = { + "invalid": _("Enter a list of values."), + "incomplete": _("Enter a complete value."), + } + + def __init__(self, fields, *, require_all_fields=True, **kwargs): + self.require_all_fields = require_all_fields + super().__init__(**kwargs) + for f in fields: + f.error_messages.setdefault("incomplete", self.error_messages["incomplete"]) + if self.disabled: + f.disabled = True + if self.require_all_fields: + # Set 'required' to False on the individual fields, because the + # required validation will be handled by MultiValueField, not + # by those individual fields. + f.required = False + self.fields = fields + + def __deepcopy__(self, memo): + result = super().__deepcopy__(memo) + result.fields = tuple(x.__deepcopy__(memo) for x in self.fields) + return result + + def validate(self, value): + pass + + def clean(self, value): + """ + Validate every value in the given list. A value is validated against + the corresponding Field in self.fields. + + For example, if this MultiValueField was instantiated with + fields=(DateField(), TimeField()), clean() would call + DateField.clean(value[0]) and TimeField.clean(value[1]). + """ + clean_data = [] + errors = [] + if self.disabled and not isinstance(value, list): + value = self.widget.decompress(value) + if not value or isinstance(value, (list, tuple)): + if not value or not [v for v in value if v not in self.empty_values]: + if self.required: + raise ValidationError( + self.error_messages["required"], code="required" + ) + else: + return self.compress([]) + else: + raise ValidationError(self.error_messages["invalid"], code="invalid") + for i, field in enumerate(self.fields): + try: + field_value = value[i] + except IndexError: + field_value = None + if field_value in self.empty_values: + if self.require_all_fields: + # Raise a 'required' error if the MultiValueField is + # required and any field is empty. + if self.required: + raise ValidationError( + self.error_messages["required"], code="required" + ) + elif field.required: + # Otherwise, add an 'incomplete' error to the list of + # collected errors and skip field cleaning, if a required + # field is empty. + if field.error_messages["incomplete"] not in errors: + errors.append(field.error_messages["incomplete"]) + continue + try: + clean_data.append(field.clean(field_value)) + except ValidationError as e: + # Collect all validation errors in a single list, which we'll + # raise at the end of clean(), rather than raising a single + # exception for the first error we encounter. Skip duplicates. + errors.extend(m for m in e.error_list if m not in errors) + if errors: + raise ValidationError(errors) + + out = self.compress(clean_data) + self.validate(out) + self.run_validators(out) + return out + + def compress(self, data_list): + """ + Return a single value for the given list of values. The values can be + assumed to be valid. + + For example, if this MultiValueField was instantiated with + fields=(DateField(), TimeField()), this might return a datetime + object created by combining the date and time in data_list. + """ + raise NotImplementedError("Subclasses must implement this method.") + + def has_changed(self, initial, data): + if self.disabled: + return False + if initial is None: + initial = ["" for x in range(0, len(data))] + else: + if not isinstance(initial, list): + initial = self.widget.decompress(initial) + for field, initial, data in zip(self.fields, initial, data): + try: + initial = field.to_python(initial) + except ValidationError: + return True + if field.has_changed(initial, data): + return True + return False + + +class FilePathField(ChoiceField): + def __init__( + self, + path, + *, + match=None, + recursive=False, + allow_files=True, + allow_folders=False, + **kwargs, + ): + self.path, self.match, self.recursive = path, match, recursive + self.allow_files, self.allow_folders = allow_files, allow_folders + super().__init__(choices=(), **kwargs) + + if self.required: + self.choices = [] + else: + self.choices = [("", "---------")] + + if self.match is not None: + self.match_re = re.compile(self.match) + + if recursive: + for root, dirs, files in sorted(os.walk(self.path)): + if self.allow_files: + for f in sorted(files): + if self.match is None or self.match_re.search(f): + f = os.path.join(root, f) + self.choices.append((f, f.replace(path, "", 1))) + if self.allow_folders: + for f in sorted(dirs): + if f == "__pycache__": + continue + if self.match is None or self.match_re.search(f): + f = os.path.join(root, f) + self.choices.append((f, f.replace(path, "", 1))) + else: + choices = [] + with os.scandir(self.path) as entries: + for f in entries: + if f.name == "__pycache__": + continue + if ( + (self.allow_files and f.is_file()) + or (self.allow_folders and f.is_dir()) + ) and (self.match is None or self.match_re.search(f.name)): + choices.append((f.path, f.name)) + choices.sort(key=operator.itemgetter(1)) + self.choices.extend(choices) + + self.widget.choices = self.choices + + +class SplitDateTimeField(MultiValueField): + widget = SplitDateTimeWidget + hidden_widget = SplitHiddenDateTimeWidget + default_error_messages = { + "invalid_date": _("Enter a valid date."), + "invalid_time": _("Enter a valid time."), + } + + def __init__(self, *, input_date_formats=None, input_time_formats=None, **kwargs): + errors = self.default_error_messages.copy() + if "error_messages" in kwargs: + errors.update(kwargs["error_messages"]) + localize = kwargs.get("localize", False) + fields = ( + DateField( + input_formats=input_date_formats, + error_messages={"invalid": errors["invalid_date"]}, + localize=localize, + ), + TimeField( + input_formats=input_time_formats, + error_messages={"invalid": errors["invalid_time"]}, + localize=localize, + ), + ) + super().__init__(fields, **kwargs) + + def compress(self, data_list): + if data_list: + # Raise a validation error if time or date is empty + # (possible if SplitDateTimeField has required=False). + if data_list[0] in self.empty_values: + raise ValidationError( + self.error_messages["invalid_date"], code="invalid_date" + ) + if data_list[1] in self.empty_values: + raise ValidationError( + self.error_messages["invalid_time"], code="invalid_time" + ) + result = datetime.datetime.combine(*data_list) + return from_current_timezone(result) + return None + + +class GenericIPAddressField(CharField): + def __init__(self, *, protocol="both", unpack_ipv4=False, **kwargs): + self.unpack_ipv4 = unpack_ipv4 + self.default_validators = validators.ip_address_validators( + protocol, unpack_ipv4 + )[0] + super().__init__(**kwargs) + + def to_python(self, value): + if value in self.empty_values: + return "" + value = value.strip() + if value and ":" in value: + return clean_ipv6_address(value, self.unpack_ipv4) + return value + + +class SlugField(CharField): + default_validators = [validators.validate_slug] + + def __init__(self, *, allow_unicode=False, **kwargs): + self.allow_unicode = allow_unicode + if self.allow_unicode: + self.default_validators = [validators.validate_unicode_slug] + super().__init__(**kwargs) + + +class UUIDField(CharField): + default_error_messages = { + "invalid": _("Enter a valid UUID."), + } + + def prepare_value(self, value): + if isinstance(value, uuid.UUID): + return str(value) + return value + + def to_python(self, value): + value = super().to_python(value) + if value in self.empty_values: + return None + if not isinstance(value, uuid.UUID): + try: + value = uuid.UUID(value) + except ValueError: + raise ValidationError(self.error_messages["invalid"], code="invalid") + return value + + +class InvalidJSONInput(str): + pass + + +class JSONString(str): + pass + + +class JSONField(CharField): + default_error_messages = { + "invalid": _("Enter a valid JSON."), + } + widget = Textarea + + def __init__(self, encoder=None, decoder=None, **kwargs): + self.encoder = encoder + self.decoder = decoder + super().__init__(**kwargs) + + def to_python(self, value): + if self.disabled: + return value + if value in self.empty_values: + return None + elif isinstance(value, (list, dict, int, float, JSONString)): + return value + try: + converted = json.loads(value, cls=self.decoder) + except json.JSONDecodeError: + raise ValidationError( + self.error_messages["invalid"], + code="invalid", + params={"value": value}, + ) + if isinstance(converted, str): + return JSONString(converted) + else: + return converted + + def bound_data(self, data, initial): + if self.disabled: + return initial + if data is None: + return None + try: + return json.loads(data, cls=self.decoder) + except json.JSONDecodeError: + return InvalidJSONInput(data) + + def prepare_value(self, value): + if isinstance(value, InvalidJSONInput): + return value + return json.dumps(value, ensure_ascii=False, cls=self.encoder) + + def has_changed(self, initial, data): + if super().has_changed(initial, data): + return True + # For purposes of seeing whether something has changed, True isn't the + # same as 1 and the order of keys doesn't matter. + return json.dumps(initial, sort_keys=True, cls=self.encoder) != json.dumps( + self.to_python(data), sort_keys=True, cls=self.encoder + ) diff --git a/virt/lib/python3.9/site-packages/django/forms/formsets 3.py b/virt/lib/python3.9/site-packages/django/forms/formsets 3.py new file mode 100644 index 00000000..68ea07c9 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/forms/formsets 3.py @@ -0,0 +1,579 @@ +from django.core.exceptions import ValidationError +from django.forms import Form +from django.forms.fields import BooleanField, IntegerField +from django.forms.renderers import get_default_renderer +from django.forms.utils import ErrorList, RenderableFormMixin +from django.forms.widgets import CheckboxInput, HiddenInput, NumberInput +from django.utils.functional import cached_property +from django.utils.translation import gettext_lazy as _ +from django.utils.translation import ngettext_lazy + +__all__ = ("BaseFormSet", "formset_factory", "all_valid") + +# special field names +TOTAL_FORM_COUNT = "TOTAL_FORMS" +INITIAL_FORM_COUNT = "INITIAL_FORMS" +MIN_NUM_FORM_COUNT = "MIN_NUM_FORMS" +MAX_NUM_FORM_COUNT = "MAX_NUM_FORMS" +ORDERING_FIELD_NAME = "ORDER" +DELETION_FIELD_NAME = "DELETE" + +# default minimum number of forms in a formset +DEFAULT_MIN_NUM = 0 + +# default maximum number of forms in a formset, to prevent memory exhaustion +DEFAULT_MAX_NUM = 1000 + + +class ManagementForm(Form): + """ + Keep track of how many form instances are displayed on the page. If adding + new forms via JavaScript, you should increment the count field of this form + as well. + """ + + template_name = "django/forms/div.html" # RemovedInDjango50Warning. + + TOTAL_FORMS = IntegerField(widget=HiddenInput) + INITIAL_FORMS = IntegerField(widget=HiddenInput) + # MIN_NUM_FORM_COUNT and MAX_NUM_FORM_COUNT are output with the rest of the + # management form, but only for the convenience of client-side code. The + # POST value of them returned from the client is not checked. + MIN_NUM_FORMS = IntegerField(required=False, widget=HiddenInput) + MAX_NUM_FORMS = IntegerField(required=False, widget=HiddenInput) + + def clean(self): + cleaned_data = super().clean() + # When the management form is invalid, we don't know how many forms + # were submitted. + cleaned_data.setdefault(TOTAL_FORM_COUNT, 0) + cleaned_data.setdefault(INITIAL_FORM_COUNT, 0) + return cleaned_data + + +class BaseFormSet(RenderableFormMixin): + """ + A collection of instances of the same Form class. + """ + + deletion_widget = CheckboxInput + ordering_widget = NumberInput + default_error_messages = { + "missing_management_form": _( + "ManagementForm data is missing or has been tampered with. Missing fields: " + "%(field_names)s. You may need to file a bug report if the issue persists." + ), + "too_many_forms": ngettext_lazy( + "Please submit at most %(num)d form.", + "Please submit at most %(num)d forms.", + "num", + ), + "too_few_forms": ngettext_lazy( + "Please submit at least %(num)d form.", + "Please submit at least %(num)d forms.", + "num", + ), + } + + template_name_div = "django/forms/formsets/div.html" + template_name_p = "django/forms/formsets/p.html" + template_name_table = "django/forms/formsets/table.html" + template_name_ul = "django/forms/formsets/ul.html" + + def __init__( + self, + data=None, + files=None, + auto_id="id_%s", + prefix=None, + initial=None, + error_class=ErrorList, + form_kwargs=None, + error_messages=None, + ): + self.is_bound = data is not None or files is not None + self.prefix = prefix or self.get_default_prefix() + self.auto_id = auto_id + self.data = data or {} + self.files = files or {} + self.initial = initial + self.form_kwargs = form_kwargs or {} + self.error_class = error_class + self._errors = None + self._non_form_errors = None + + messages = {} + for cls in reversed(type(self).__mro__): + messages.update(getattr(cls, "default_error_messages", {})) + if error_messages is not None: + messages.update(error_messages) + self.error_messages = messages + + def __iter__(self): + """Yield the forms in the order they should be rendered.""" + return iter(self.forms) + + def __getitem__(self, index): + """Return the form at the given index, based on the rendering order.""" + return self.forms[index] + + def __len__(self): + return len(self.forms) + + def __bool__(self): + """ + Return True since all formsets have a management form which is not + included in the length. + """ + return True + + def __repr__(self): + if self._errors is None: + is_valid = "Unknown" + else: + is_valid = ( + self.is_bound + and not self._non_form_errors + and not any(form_errors for form_errors in self._errors) + ) + return "<%s: bound=%s valid=%s total_forms=%s>" % ( + self.__class__.__qualname__, + self.is_bound, + is_valid, + self.total_form_count(), + ) + + @cached_property + def management_form(self): + """Return the ManagementForm instance for this FormSet.""" + if self.is_bound: + form = ManagementForm( + self.data, + auto_id=self.auto_id, + prefix=self.prefix, + renderer=self.renderer, + ) + form.full_clean() + else: + form = ManagementForm( + auto_id=self.auto_id, + prefix=self.prefix, + initial={ + TOTAL_FORM_COUNT: self.total_form_count(), + INITIAL_FORM_COUNT: self.initial_form_count(), + MIN_NUM_FORM_COUNT: self.min_num, + MAX_NUM_FORM_COUNT: self.max_num, + }, + renderer=self.renderer, + ) + return form + + def total_form_count(self): + """Return the total number of forms in this FormSet.""" + if self.is_bound: + # return absolute_max if it is lower than the actual total form + # count in the data; this is DoS protection to prevent clients + # from forcing the server to instantiate arbitrary numbers of + # forms + return min( + self.management_form.cleaned_data[TOTAL_FORM_COUNT], self.absolute_max + ) + else: + initial_forms = self.initial_form_count() + total_forms = max(initial_forms, self.min_num) + self.extra + # Allow all existing related objects/inlines to be displayed, + # but don't allow extra beyond max_num. + if initial_forms > self.max_num >= 0: + total_forms = initial_forms + elif total_forms > self.max_num >= 0: + total_forms = self.max_num + return total_forms + + def initial_form_count(self): + """Return the number of forms that are required in this FormSet.""" + if self.is_bound: + return self.management_form.cleaned_data[INITIAL_FORM_COUNT] + else: + # Use the length of the initial data if it's there, 0 otherwise. + initial_forms = len(self.initial) if self.initial else 0 + return initial_forms + + @cached_property + def forms(self): + """Instantiate forms at first property access.""" + # DoS protection is included in total_form_count() + return [ + self._construct_form(i, **self.get_form_kwargs(i)) + for i in range(self.total_form_count()) + ] + + def get_form_kwargs(self, index): + """ + Return additional keyword arguments for each individual formset form. + + index will be None if the form being constructed is a new empty + form. + """ + return self.form_kwargs.copy() + + def _construct_form(self, i, **kwargs): + """Instantiate and return the i-th form instance in a formset.""" + defaults = { + "auto_id": self.auto_id, + "prefix": self.add_prefix(i), + "error_class": self.error_class, + # Don't render the HTML 'required' attribute as it may cause + # incorrect validation for extra, optional, and deleted + # forms in the formset. + "use_required_attribute": False, + "renderer": self.renderer, + } + if self.is_bound: + defaults["data"] = self.data + defaults["files"] = self.files + if self.initial and "initial" not in kwargs: + try: + defaults["initial"] = self.initial[i] + except IndexError: + pass + # Allow extra forms to be empty, unless they're part of + # the minimum forms. + if i >= self.initial_form_count() and i >= self.min_num: + defaults["empty_permitted"] = True + defaults.update(kwargs) + form = self.form(**defaults) + self.add_fields(form, i) + return form + + @property + def initial_forms(self): + """Return a list of all the initial forms in this formset.""" + return self.forms[: self.initial_form_count()] + + @property + def extra_forms(self): + """Return a list of all the extra forms in this formset.""" + return self.forms[self.initial_form_count() :] + + @property + def empty_form(self): + form_kwargs = { + **self.get_form_kwargs(None), + "auto_id": self.auto_id, + "prefix": self.add_prefix("__prefix__"), + "empty_permitted": True, + "use_required_attribute": False, + "renderer": self.renderer, + } + form = self.form(**form_kwargs) + self.add_fields(form, None) + return form + + @property + def cleaned_data(self): + """ + Return a list of form.cleaned_data dicts for every form in self.forms. + """ + if not self.is_valid(): + raise AttributeError( + "'%s' object has no attribute 'cleaned_data'" % self.__class__.__name__ + ) + return [form.cleaned_data for form in self.forms] + + @property + def deleted_forms(self): + """Return a list of forms that have been marked for deletion.""" + if not self.is_valid() or not self.can_delete: + return [] + # construct _deleted_form_indexes which is just a list of form indexes + # that have had their deletion widget set to True + if not hasattr(self, "_deleted_form_indexes"): + self._deleted_form_indexes = [] + for i, form in enumerate(self.forms): + # if this is an extra form and hasn't changed, don't consider it + if i >= self.initial_form_count() and not form.has_changed(): + continue + if self._should_delete_form(form): + self._deleted_form_indexes.append(i) + return [self.forms[i] for i in self._deleted_form_indexes] + + @property + def ordered_forms(self): + """ + Return a list of form in the order specified by the incoming data. + Raise an AttributeError if ordering is not allowed. + """ + if not self.is_valid() or not self.can_order: + raise AttributeError( + "'%s' object has no attribute 'ordered_forms'" % self.__class__.__name__ + ) + # Construct _ordering, which is a list of (form_index, order_field_value) + # tuples. After constructing this list, we'll sort it by order_field_value + # so we have a way to get to the form indexes in the order specified + # by the form data. + if not hasattr(self, "_ordering"): + self._ordering = [] + for i, form in enumerate(self.forms): + # if this is an extra form and hasn't changed, don't consider it + if i >= self.initial_form_count() and not form.has_changed(): + continue + # don't add data marked for deletion to self.ordered_data + if self.can_delete and self._should_delete_form(form): + continue + self._ordering.append((i, form.cleaned_data[ORDERING_FIELD_NAME])) + # After we're done populating self._ordering, sort it. + # A sort function to order things numerically ascending, but + # None should be sorted below anything else. Allowing None as + # a comparison value makes it so we can leave ordering fields + # blank. + + def compare_ordering_key(k): + if k[1] is None: + return (1, 0) # +infinity, larger than any number + return (0, k[1]) + + self._ordering.sort(key=compare_ordering_key) + # Return a list of form.cleaned_data dicts in the order specified by + # the form data. + return [self.forms[i[0]] for i in self._ordering] + + @classmethod + def get_default_prefix(cls): + return "form" + + @classmethod + def get_deletion_widget(cls): + return cls.deletion_widget + + @classmethod + def get_ordering_widget(cls): + return cls.ordering_widget + + def non_form_errors(self): + """ + Return an ErrorList of errors that aren't associated with a particular + form -- i.e., from formset.clean(). Return an empty ErrorList if there + are none. + """ + if self._non_form_errors is None: + self.full_clean() + return self._non_form_errors + + @property + def errors(self): + """Return a list of form.errors for every form in self.forms.""" + if self._errors is None: + self.full_clean() + return self._errors + + def total_error_count(self): + """Return the number of errors across all forms in the formset.""" + return len(self.non_form_errors()) + sum( + len(form_errors) for form_errors in self.errors + ) + + def _should_delete_form(self, form): + """Return whether or not the form was marked for deletion.""" + return form.cleaned_data.get(DELETION_FIELD_NAME, False) + + def is_valid(self): + """Return True if every form in self.forms is valid.""" + if not self.is_bound: + return False + # Accessing errors triggers a full clean the first time only. + self.errors + # List comprehension ensures is_valid() is called for all forms. + # Forms due to be deleted shouldn't cause the formset to be invalid. + forms_valid = all( + [ + form.is_valid() + for form in self.forms + if not (self.can_delete and self._should_delete_form(form)) + ] + ) + return forms_valid and not self.non_form_errors() + + def full_clean(self): + """ + Clean all of self.data and populate self._errors and + self._non_form_errors. + """ + self._errors = [] + self._non_form_errors = self.error_class( + error_class="nonform", renderer=self.renderer + ) + empty_forms_count = 0 + + if not self.is_bound: # Stop further processing. + return + + if not self.management_form.is_valid(): + error = ValidationError( + self.error_messages["missing_management_form"], + params={ + "field_names": ", ".join( + self.management_form.add_prefix(field_name) + for field_name in self.management_form.errors + ), + }, + code="missing_management_form", + ) + self._non_form_errors.append(error) + + for i, form in enumerate(self.forms): + # Empty forms are unchanged forms beyond those with initial data. + if not form.has_changed() and i >= self.initial_form_count(): + empty_forms_count += 1 + # Accessing errors calls full_clean() if necessary. + # _should_delete_form() requires cleaned_data. + form_errors = form.errors + if self.can_delete and self._should_delete_form(form): + continue + self._errors.append(form_errors) + try: + if ( + self.validate_max + and self.total_form_count() - len(self.deleted_forms) > self.max_num + ) or self.management_form.cleaned_data[ + TOTAL_FORM_COUNT + ] > self.absolute_max: + raise ValidationError( + self.error_messages["too_many_forms"] % {"num": self.max_num}, + code="too_many_forms", + ) + if ( + self.validate_min + and self.total_form_count() + - len(self.deleted_forms) + - empty_forms_count + < self.min_num + ): + raise ValidationError( + self.error_messages["too_few_forms"] % {"num": self.min_num}, + code="too_few_forms", + ) + # Give self.clean() a chance to do cross-form validation. + self.clean() + except ValidationError as e: + self._non_form_errors = self.error_class( + e.error_list, + error_class="nonform", + renderer=self.renderer, + ) + + def clean(self): + """ + Hook for doing any extra formset-wide cleaning after Form.clean() has + been called on every form. Any ValidationError raised by this method + will not be associated with a particular form; it will be accessible + via formset.non_form_errors() + """ + pass + + def has_changed(self): + """Return True if data in any form differs from initial.""" + return any(form.has_changed() for form in self) + + def add_fields(self, form, index): + """A hook for adding extra fields on to each form instance.""" + initial_form_count = self.initial_form_count() + if self.can_order: + # Only pre-fill the ordering field for initial forms. + if index is not None and index < initial_form_count: + form.fields[ORDERING_FIELD_NAME] = IntegerField( + label=_("Order"), + initial=index + 1, + required=False, + widget=self.get_ordering_widget(), + ) + else: + form.fields[ORDERING_FIELD_NAME] = IntegerField( + label=_("Order"), + required=False, + widget=self.get_ordering_widget(), + ) + if self.can_delete and ( + self.can_delete_extra or (index is not None and index < initial_form_count) + ): + form.fields[DELETION_FIELD_NAME] = BooleanField( + label=_("Delete"), + required=False, + widget=self.get_deletion_widget(), + ) + + def add_prefix(self, index): + return "%s-%s" % (self.prefix, index) + + def is_multipart(self): + """ + Return True if the formset needs to be multipart, i.e. it + has FileInput, or False otherwise. + """ + if self.forms: + return self.forms[0].is_multipart() + else: + return self.empty_form.is_multipart() + + @property + def media(self): + # All the forms on a FormSet are the same, so you only need to + # interrogate the first form for media. + if self.forms: + return self.forms[0].media + else: + return self.empty_form.media + + @property + def template_name(self): + return self.renderer.formset_template_name + + def get_context(self): + return {"formset": self} + + +def formset_factory( + form, + formset=BaseFormSet, + extra=1, + can_order=False, + can_delete=False, + max_num=None, + validate_max=False, + min_num=None, + validate_min=False, + absolute_max=None, + can_delete_extra=True, + renderer=None, +): + """Return a FormSet for the given form class.""" + if min_num is None: + min_num = DEFAULT_MIN_NUM + if max_num is None: + max_num = DEFAULT_MAX_NUM + # absolute_max is a hard limit on forms instantiated, to prevent + # memory-exhaustion attacks. Default to max_num + DEFAULT_MAX_NUM + # (which is 2 * DEFAULT_MAX_NUM if max_num is None in the first place). + if absolute_max is None: + absolute_max = max_num + DEFAULT_MAX_NUM + if max_num > absolute_max: + raise ValueError("'absolute_max' must be greater or equal to 'max_num'.") + attrs = { + "form": form, + "extra": extra, + "can_order": can_order, + "can_delete": can_delete, + "can_delete_extra": can_delete_extra, + "min_num": min_num, + "max_num": max_num, + "absolute_max": absolute_max, + "validate_min": validate_min, + "validate_max": validate_max, + "renderer": renderer or get_default_renderer(), + } + return type(form.__name__ + "FormSet", (formset,), attrs) + + +def all_valid(formsets): + """Validate every formset and return True if all are valid.""" + # List comprehension ensures is_valid() is called for all formsets. + return all([formset.is_valid() for formset in formsets]) diff --git a/virt/lib/python3.9/site-packages/django/forms/models 3.py b/virt/lib/python3.9/site-packages/django/forms/models 3.py new file mode 100644 index 00000000..88a90936 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/forms/models 3.py @@ -0,0 +1,1660 @@ +""" +Helper functions for creating Form classes from Django models +and database field objects. +""" +from itertools import chain + +from django.core.exceptions import ( + NON_FIELD_ERRORS, + FieldError, + ImproperlyConfigured, + ValidationError, +) +from django.db.models.utils import AltersData +from django.forms.fields import ChoiceField, Field +from django.forms.forms import BaseForm, DeclarativeFieldsMetaclass +from django.forms.formsets import BaseFormSet, formset_factory +from django.forms.utils import ErrorList +from django.forms.widgets import ( + HiddenInput, + MultipleHiddenInput, + RadioSelect, + SelectMultiple, +) +from django.utils.text import capfirst, get_text_list +from django.utils.translation import gettext +from django.utils.translation import gettext_lazy as _ + +__all__ = ( + "ModelForm", + "BaseModelForm", + "model_to_dict", + "fields_for_model", + "ModelChoiceField", + "ModelMultipleChoiceField", + "ALL_FIELDS", + "BaseModelFormSet", + "modelformset_factory", + "BaseInlineFormSet", + "inlineformset_factory", + "modelform_factory", +) + +ALL_FIELDS = "__all__" + + +def construct_instance(form, instance, fields=None, exclude=None): + """ + Construct and return a model instance from the bound ``form``'s + ``cleaned_data``, but do not save the returned instance to the database. + """ + from django.db import models + + opts = instance._meta + + cleaned_data = form.cleaned_data + file_field_list = [] + for f in opts.fields: + if ( + not f.editable + or isinstance(f, models.AutoField) + or f.name not in cleaned_data + ): + continue + if fields is not None and f.name not in fields: + continue + if exclude and f.name in exclude: + continue + # Leave defaults for fields that aren't in POST data, except for + # checkbox inputs because they don't appear in POST data if not checked. + if ( + f.has_default() + and form[f.name].field.widget.value_omitted_from_data( + form.data, form.files, form.add_prefix(f.name) + ) + and cleaned_data.get(f.name) in form[f.name].field.empty_values + ): + continue + # Defer saving file-type fields until after the other fields, so a + # callable upload_to can use the values from other fields. + if isinstance(f, models.FileField): + file_field_list.append(f) + else: + f.save_form_data(instance, cleaned_data[f.name]) + + for f in file_field_list: + f.save_form_data(instance, cleaned_data[f.name]) + + return instance + + +# ModelForms ################################################################# + + +def model_to_dict(instance, fields=None, exclude=None): + """ + Return a dict containing the data in ``instance`` suitable for passing as + a Form's ``initial`` keyword argument. + + ``fields`` is an optional list of field names. If provided, return only the + named. + + ``exclude`` is an optional list of field names. If provided, exclude the + named from the returned dict, even if they are listed in the ``fields`` + argument. + """ + opts = instance._meta + data = {} + for f in chain(opts.concrete_fields, opts.private_fields, opts.many_to_many): + if not getattr(f, "editable", False): + continue + if fields is not None and f.name not in fields: + continue + if exclude and f.name in exclude: + continue + data[f.name] = f.value_from_object(instance) + return data + + +def apply_limit_choices_to_to_formfield(formfield): + """Apply limit_choices_to to the formfield's queryset if needed.""" + from django.db.models import Exists, OuterRef, Q + + if hasattr(formfield, "queryset") and hasattr(formfield, "get_limit_choices_to"): + limit_choices_to = formfield.get_limit_choices_to() + if limit_choices_to: + complex_filter = limit_choices_to + if not isinstance(complex_filter, Q): + complex_filter = Q(**limit_choices_to) + complex_filter &= Q(pk=OuterRef("pk")) + # Use Exists() to avoid potential duplicates. + formfield.queryset = formfield.queryset.filter( + Exists(formfield.queryset.model._base_manager.filter(complex_filter)), + ) + + +def fields_for_model( + model, + fields=None, + exclude=None, + widgets=None, + formfield_callback=None, + localized_fields=None, + labels=None, + help_texts=None, + error_messages=None, + field_classes=None, + *, + apply_limit_choices_to=True, +): + """ + Return a dictionary containing form fields for the given model. + + ``fields`` is an optional list of field names. If provided, return only the + named fields. + + ``exclude`` is an optional list of field names. If provided, exclude the + named fields from the returned fields, even if they are listed in the + ``fields`` argument. + + ``widgets`` is a dictionary of model field names mapped to a widget. + + ``formfield_callback`` is a callable that takes a model field and returns + a form field. + + ``localized_fields`` is a list of names of fields which should be localized. + + ``labels`` is a dictionary of model field names mapped to a label. + + ``help_texts`` is a dictionary of model field names mapped to a help text. + + ``error_messages`` is a dictionary of model field names mapped to a + dictionary of error messages. + + ``field_classes`` is a dictionary of model field names mapped to a form + field class. + + ``apply_limit_choices_to`` is a boolean indicating if limit_choices_to + should be applied to a field's queryset. + """ + field_dict = {} + ignored = [] + opts = model._meta + # Avoid circular import + from django.db.models import Field as ModelField + + sortable_private_fields = [ + f for f in opts.private_fields if isinstance(f, ModelField) + ] + for f in sorted( + chain(opts.concrete_fields, sortable_private_fields, opts.many_to_many) + ): + if not getattr(f, "editable", False): + if ( + fields is not None + and f.name in fields + and (exclude is None or f.name not in exclude) + ): + raise FieldError( + "'%s' cannot be specified for %s model form as it is a " + "non-editable field" % (f.name, model.__name__) + ) + continue + if fields is not None and f.name not in fields: + continue + if exclude and f.name in exclude: + continue + + kwargs = {} + if widgets and f.name in widgets: + kwargs["widget"] = widgets[f.name] + if localized_fields == ALL_FIELDS or ( + localized_fields and f.name in localized_fields + ): + kwargs["localize"] = True + if labels and f.name in labels: + kwargs["label"] = labels[f.name] + if help_texts and f.name in help_texts: + kwargs["help_text"] = help_texts[f.name] + if error_messages and f.name in error_messages: + kwargs["error_messages"] = error_messages[f.name] + if field_classes and f.name in field_classes: + kwargs["form_class"] = field_classes[f.name] + + if formfield_callback is None: + formfield = f.formfield(**kwargs) + elif not callable(formfield_callback): + raise TypeError("formfield_callback must be a function or callable") + else: + formfield = formfield_callback(f, **kwargs) + + if formfield: + if apply_limit_choices_to: + apply_limit_choices_to_to_formfield(formfield) + field_dict[f.name] = formfield + else: + ignored.append(f.name) + if fields: + field_dict = { + f: field_dict.get(f) + for f in fields + if (not exclude or f not in exclude) and f not in ignored + } + return field_dict + + +class ModelFormOptions: + def __init__(self, options=None): + self.model = getattr(options, "model", None) + self.fields = getattr(options, "fields", None) + self.exclude = getattr(options, "exclude", None) + self.widgets = getattr(options, "widgets", None) + self.localized_fields = getattr(options, "localized_fields", None) + self.labels = getattr(options, "labels", None) + self.help_texts = getattr(options, "help_texts", None) + self.error_messages = getattr(options, "error_messages", None) + self.field_classes = getattr(options, "field_classes", None) + self.formfield_callback = getattr(options, "formfield_callback", None) + + +class ModelFormMetaclass(DeclarativeFieldsMetaclass): + def __new__(mcs, name, bases, attrs): + new_class = super().__new__(mcs, name, bases, attrs) + + if bases == (BaseModelForm,): + return new_class + + opts = new_class._meta = ModelFormOptions(getattr(new_class, "Meta", None)) + + # We check if a string was passed to `fields` or `exclude`, + # which is likely to be a mistake where the user typed ('foo') instead + # of ('foo',) + for opt in ["fields", "exclude", "localized_fields"]: + value = getattr(opts, opt) + if isinstance(value, str) and value != ALL_FIELDS: + msg = ( + "%(model)s.Meta.%(opt)s cannot be a string. " + "Did you mean to type: ('%(value)s',)?" + % { + "model": new_class.__name__, + "opt": opt, + "value": value, + } + ) + raise TypeError(msg) + + if opts.model: + # If a model is defined, extract form fields from it. + if opts.fields is None and opts.exclude is None: + raise ImproperlyConfigured( + "Creating a ModelForm without either the 'fields' attribute " + "or the 'exclude' attribute is prohibited; form %s " + "needs updating." % name + ) + + if opts.fields == ALL_FIELDS: + # Sentinel for fields_for_model to indicate "get the list of + # fields from the model" + opts.fields = None + + fields = fields_for_model( + opts.model, + opts.fields, + opts.exclude, + opts.widgets, + opts.formfield_callback, + opts.localized_fields, + opts.labels, + opts.help_texts, + opts.error_messages, + opts.field_classes, + # limit_choices_to will be applied during ModelForm.__init__(). + apply_limit_choices_to=False, + ) + + # make sure opts.fields doesn't specify an invalid field + none_model_fields = {k for k, v in fields.items() if not v} + missing_fields = none_model_fields.difference(new_class.declared_fields) + if missing_fields: + message = "Unknown field(s) (%s) specified for %s" + message %= (", ".join(missing_fields), opts.model.__name__) + raise FieldError(message) + # Override default model fields with any custom declared ones + # (plus, include all the other declared fields). + fields.update(new_class.declared_fields) + else: + fields = new_class.declared_fields + + new_class.base_fields = fields + + return new_class + + +class BaseModelForm(BaseForm, AltersData): + def __init__( + self, + data=None, + files=None, + auto_id="id_%s", + prefix=None, + initial=None, + error_class=ErrorList, + label_suffix=None, + empty_permitted=False, + instance=None, + use_required_attribute=None, + renderer=None, + ): + opts = self._meta + if opts.model is None: + raise ValueError("ModelForm has no model class specified.") + if instance is None: + # if we didn't get an instance, instantiate a new one + self.instance = opts.model() + object_data = {} + else: + self.instance = instance + object_data = model_to_dict(instance, opts.fields, opts.exclude) + # if initial was provided, it should override the values from instance + if initial is not None: + object_data.update(initial) + # self._validate_unique will be set to True by BaseModelForm.clean(). + # It is False by default so overriding self.clean() and failing to call + # super will stop validate_unique from being called. + self._validate_unique = False + super().__init__( + data, + files, + auto_id, + prefix, + object_data, + error_class, + label_suffix, + empty_permitted, + use_required_attribute=use_required_attribute, + renderer=renderer, + ) + for formfield in self.fields.values(): + apply_limit_choices_to_to_formfield(formfield) + + def _get_validation_exclusions(self): + """ + For backwards-compatibility, exclude several types of fields from model + validation. See tickets #12507, #12521, #12553. + """ + exclude = set() + # Build up a list of fields that should be excluded from model field + # validation and unique checks. + for f in self.instance._meta.fields: + field = f.name + # Exclude fields that aren't on the form. The developer may be + # adding these values to the model after form validation. + if field not in self.fields: + exclude.add(f.name) + + # Don't perform model validation on fields that were defined + # manually on the form and excluded via the ModelForm's Meta + # class. See #12901. + elif self._meta.fields and field not in self._meta.fields: + exclude.add(f.name) + elif self._meta.exclude and field in self._meta.exclude: + exclude.add(f.name) + + # Exclude fields that failed form validation. There's no need for + # the model fields to validate them as well. + elif field in self._errors: + exclude.add(f.name) + + # Exclude empty fields that are not required by the form, if the + # underlying model field is required. This keeps the model field + # from raising a required error. Note: don't exclude the field from + # validation if the model field allows blanks. If it does, the blank + # value may be included in a unique check, so cannot be excluded + # from validation. + else: + form_field = self.fields[field] + field_value = self.cleaned_data.get(field) + if ( + not f.blank + and not form_field.required + and field_value in form_field.empty_values + ): + exclude.add(f.name) + return exclude + + def clean(self): + self._validate_unique = True + return self.cleaned_data + + def _update_errors(self, errors): + # Override any validation error messages defined at the model level + # with those defined at the form level. + opts = self._meta + + # Allow the model generated by construct_instance() to raise + # ValidationError and have them handled in the same way as others. + if hasattr(errors, "error_dict"): + error_dict = errors.error_dict + else: + error_dict = {NON_FIELD_ERRORS: errors} + + for field, messages in error_dict.items(): + if ( + field == NON_FIELD_ERRORS + and opts.error_messages + and NON_FIELD_ERRORS in opts.error_messages + ): + error_messages = opts.error_messages[NON_FIELD_ERRORS] + elif field in self.fields: + error_messages = self.fields[field].error_messages + else: + continue + + for message in messages: + if ( + isinstance(message, ValidationError) + and message.code in error_messages + ): + message.message = error_messages[message.code] + + self.add_error(None, errors) + + def _post_clean(self): + opts = self._meta + + exclude = self._get_validation_exclusions() + + # Foreign Keys being used to represent inline relationships + # are excluded from basic field value validation. This is for two + # reasons: firstly, the value may not be supplied (#12507; the + # case of providing new values to the admin); secondly the + # object being referred to may not yet fully exist (#12749). + # However, these fields *must* be included in uniqueness checks, + # so this can't be part of _get_validation_exclusions(). + for name, field in self.fields.items(): + if isinstance(field, InlineForeignKeyField): + exclude.add(name) + + try: + self.instance = construct_instance( + self, self.instance, opts.fields, opts.exclude + ) + except ValidationError as e: + self._update_errors(e) + + try: + self.instance.full_clean(exclude=exclude, validate_unique=False) + except ValidationError as e: + self._update_errors(e) + + # Validate uniqueness if needed. + if self._validate_unique: + self.validate_unique() + + def validate_unique(self): + """ + Call the instance's validate_unique() method and update the form's + validation errors if any were raised. + """ + exclude = self._get_validation_exclusions() + try: + self.instance.validate_unique(exclude=exclude) + except ValidationError as e: + self._update_errors(e) + + def _save_m2m(self): + """ + Save the many-to-many fields and generic relations for this form. + """ + cleaned_data = self.cleaned_data + exclude = self._meta.exclude + fields = self._meta.fields + opts = self.instance._meta + # Note that for historical reasons we want to include also + # private_fields here. (GenericRelation was previously a fake + # m2m field). + for f in chain(opts.many_to_many, opts.private_fields): + if not hasattr(f, "save_form_data"): + continue + if fields and f.name not in fields: + continue + if exclude and f.name in exclude: + continue + if f.name in cleaned_data: + f.save_form_data(self.instance, cleaned_data[f.name]) + + def save(self, commit=True): + """ + Save this form's self.instance object if commit=True. Otherwise, add + a save_m2m() method to the form which can be called after the instance + is saved manually at a later time. Return the model instance. + """ + if self.errors: + raise ValueError( + "The %s could not be %s because the data didn't validate." + % ( + self.instance._meta.object_name, + "created" if self.instance._state.adding else "changed", + ) + ) + if commit: + # If committing, save the instance and the m2m data immediately. + self.instance.save() + self._save_m2m() + else: + # If not committing, add a method to the form to allow deferred + # saving of m2m data. + self.save_m2m = self._save_m2m + return self.instance + + save.alters_data = True + + +class ModelForm(BaseModelForm, metaclass=ModelFormMetaclass): + pass + + +def modelform_factory( + model, + form=ModelForm, + fields=None, + exclude=None, + formfield_callback=None, + widgets=None, + localized_fields=None, + labels=None, + help_texts=None, + error_messages=None, + field_classes=None, +): + """ + Return a ModelForm containing form fields for the given model. You can + optionally pass a `form` argument to use as a starting point for + constructing the ModelForm. + + ``fields`` is an optional list of field names. If provided, include only + the named fields in the returned fields. If omitted or '__all__', use all + fields. + + ``exclude`` is an optional list of field names. If provided, exclude the + named fields from the returned fields, even if they are listed in the + ``fields`` argument. + + ``widgets`` is a dictionary of model field names mapped to a widget. + + ``localized_fields`` is a list of names of fields which should be localized. + + ``formfield_callback`` is a callable that takes a model field and returns + a form field. + + ``labels`` is a dictionary of model field names mapped to a label. + + ``help_texts`` is a dictionary of model field names mapped to a help text. + + ``error_messages`` is a dictionary of model field names mapped to a + dictionary of error messages. + + ``field_classes`` is a dictionary of model field names mapped to a form + field class. + """ + # Create the inner Meta class. FIXME: ideally, we should be able to + # construct a ModelForm without creating and passing in a temporary + # inner class. + + # Build up a list of attributes that the Meta object will have. + attrs = {"model": model} + if fields is not None: + attrs["fields"] = fields + if exclude is not None: + attrs["exclude"] = exclude + if widgets is not None: + attrs["widgets"] = widgets + if localized_fields is not None: + attrs["localized_fields"] = localized_fields + if labels is not None: + attrs["labels"] = labels + if help_texts is not None: + attrs["help_texts"] = help_texts + if error_messages is not None: + attrs["error_messages"] = error_messages + if field_classes is not None: + attrs["field_classes"] = field_classes + + # If parent form class already has an inner Meta, the Meta we're + # creating needs to inherit from the parent's inner meta. + bases = (form.Meta,) if hasattr(form, "Meta") else () + Meta = type("Meta", bases, attrs) + if formfield_callback: + Meta.formfield_callback = staticmethod(formfield_callback) + # Give this new form class a reasonable name. + class_name = model.__name__ + "Form" + + # Class attributes for the new form class. + form_class_attrs = {"Meta": Meta} + + if getattr(Meta, "fields", None) is None and getattr(Meta, "exclude", None) is None: + raise ImproperlyConfigured( + "Calling modelform_factory without defining 'fields' or " + "'exclude' explicitly is prohibited." + ) + + # Instantiate type(form) in order to use the same metaclass as form. + return type(form)(class_name, (form,), form_class_attrs) + + +# ModelFormSets ############################################################## + + +class BaseModelFormSet(BaseFormSet, AltersData): + """ + A ``FormSet`` for editing a queryset and/or adding new objects to it. + """ + + model = None + edit_only = False + + # Set of fields that must be unique among forms of this set. + unique_fields = set() + + def __init__( + self, + data=None, + files=None, + auto_id="id_%s", + prefix=None, + queryset=None, + *, + initial=None, + **kwargs, + ): + self.queryset = queryset + self.initial_extra = initial + super().__init__( + **{ + "data": data, + "files": files, + "auto_id": auto_id, + "prefix": prefix, + **kwargs, + } + ) + + def initial_form_count(self): + """Return the number of forms that are required in this FormSet.""" + if not self.is_bound: + return len(self.get_queryset()) + return super().initial_form_count() + + def _existing_object(self, pk): + if not hasattr(self, "_object_dict"): + self._object_dict = {o.pk: o for o in self.get_queryset()} + return self._object_dict.get(pk) + + def _get_to_python(self, field): + """ + If the field is a related field, fetch the concrete field's (that + is, the ultimate pointed-to field's) to_python. + """ + while field.remote_field is not None: + field = field.remote_field.get_related_field() + return field.to_python + + def _construct_form(self, i, **kwargs): + pk_required = i < self.initial_form_count() + if pk_required: + if self.is_bound: + pk_key = "%s-%s" % (self.add_prefix(i), self.model._meta.pk.name) + try: + pk = self.data[pk_key] + except KeyError: + # The primary key is missing. The user may have tampered + # with POST data. + pass + else: + to_python = self._get_to_python(self.model._meta.pk) + try: + pk = to_python(pk) + except ValidationError: + # The primary key exists but is an invalid value. The + # user may have tampered with POST data. + pass + else: + kwargs["instance"] = self._existing_object(pk) + else: + kwargs["instance"] = self.get_queryset()[i] + elif self.initial_extra: + # Set initial values for extra forms + try: + kwargs["initial"] = self.initial_extra[i - self.initial_form_count()] + except IndexError: + pass + form = super()._construct_form(i, **kwargs) + if pk_required: + form.fields[self.model._meta.pk.name].required = True + return form + + def get_queryset(self): + if not hasattr(self, "_queryset"): + if self.queryset is not None: + qs = self.queryset + else: + qs = self.model._default_manager.get_queryset() + + # If the queryset isn't already ordered we need to add an + # artificial ordering here to make sure that all formsets + # constructed from this queryset have the same form order. + if not qs.ordered: + qs = qs.order_by(self.model._meta.pk.name) + + # Removed queryset limiting here. As per discussion re: #13023 + # on django-dev, max_num should not prevent existing + # related objects/inlines from being displayed. + self._queryset = qs + return self._queryset + + def save_new(self, form, commit=True): + """Save and return a new model instance for the given form.""" + return form.save(commit=commit) + + def save_existing(self, form, instance, commit=True): + """Save and return an existing model instance for the given form.""" + return form.save(commit=commit) + + def delete_existing(self, obj, commit=True): + """Deletes an existing model instance.""" + if commit: + obj.delete() + + def save(self, commit=True): + """ + Save model instances for every form, adding and changing instances + as necessary, and return the list of instances. + """ + if not commit: + self.saved_forms = [] + + def save_m2m(): + for form in self.saved_forms: + form.save_m2m() + + self.save_m2m = save_m2m + if self.edit_only: + return self.save_existing_objects(commit) + else: + return self.save_existing_objects(commit) + self.save_new_objects(commit) + + save.alters_data = True + + def clean(self): + self.validate_unique() + + def validate_unique(self): + # Collect unique_checks and date_checks to run from all the forms. + all_unique_checks = set() + all_date_checks = set() + forms_to_delete = self.deleted_forms + valid_forms = [ + form + for form in self.forms + if form.is_valid() and form not in forms_to_delete + ] + for form in valid_forms: + exclude = form._get_validation_exclusions() + unique_checks, date_checks = form.instance._get_unique_checks( + exclude=exclude, + include_meta_constraints=True, + ) + all_unique_checks.update(unique_checks) + all_date_checks.update(date_checks) + + errors = [] + # Do each of the unique checks (unique and unique_together) + for uclass, unique_check in all_unique_checks: + seen_data = set() + for form in valid_forms: + # Get the data for the set of fields that must be unique among + # the forms. + row_data = ( + field if field in self.unique_fields else form.cleaned_data[field] + for field in unique_check + if field in form.cleaned_data + ) + # Reduce Model instances to their primary key values + row_data = tuple( + d._get_pk_val() if hasattr(d, "_get_pk_val") + # Prevent "unhashable type: list" errors later on. + else tuple(d) if isinstance(d, list) else d + for d in row_data + ) + if row_data and None not in row_data: + # if we've already seen it then we have a uniqueness failure + if row_data in seen_data: + # poke error messages into the right places and mark + # the form as invalid + errors.append(self.get_unique_error_message(unique_check)) + form._errors[NON_FIELD_ERRORS] = self.error_class( + [self.get_form_error()], + renderer=self.renderer, + ) + # Remove the data from the cleaned_data dict since it + # was invalid. + for field in unique_check: + if field in form.cleaned_data: + del form.cleaned_data[field] + # mark the data as seen + seen_data.add(row_data) + # iterate over each of the date checks now + for date_check in all_date_checks: + seen_data = set() + uclass, lookup, field, unique_for = date_check + for form in valid_forms: + # see if we have data for both fields + if ( + form.cleaned_data + and form.cleaned_data[field] is not None + and form.cleaned_data[unique_for] is not None + ): + # if it's a date lookup we need to get the data for all the fields + if lookup == "date": + date = form.cleaned_data[unique_for] + date_data = (date.year, date.month, date.day) + # otherwise it's just the attribute on the date/datetime + # object + else: + date_data = (getattr(form.cleaned_data[unique_for], lookup),) + data = (form.cleaned_data[field],) + date_data + # if we've already seen it then we have a uniqueness failure + if data in seen_data: + # poke error messages into the right places and mark + # the form as invalid + errors.append(self.get_date_error_message(date_check)) + form._errors[NON_FIELD_ERRORS] = self.error_class( + [self.get_form_error()], + renderer=self.renderer, + ) + # Remove the data from the cleaned_data dict since it + # was invalid. + del form.cleaned_data[field] + # mark the data as seen + seen_data.add(data) + + if errors: + raise ValidationError(errors) + + def get_unique_error_message(self, unique_check): + if len(unique_check) == 1: + return gettext("Please correct the duplicate data for %(field)s.") % { + "field": unique_check[0], + } + else: + return gettext( + "Please correct the duplicate data for %(field)s, which must be unique." + ) % { + "field": get_text_list(unique_check, _("and")), + } + + def get_date_error_message(self, date_check): + return gettext( + "Please correct the duplicate data for %(field_name)s " + "which must be unique for the %(lookup)s in %(date_field)s." + ) % { + "field_name": date_check[2], + "date_field": date_check[3], + "lookup": str(date_check[1]), + } + + def get_form_error(self): + return gettext("Please correct the duplicate values below.") + + def save_existing_objects(self, commit=True): + self.changed_objects = [] + self.deleted_objects = [] + if not self.initial_forms: + return [] + + saved_instances = [] + forms_to_delete = self.deleted_forms + for form in self.initial_forms: + obj = form.instance + # If the pk is None, it means either: + # 1. The object is an unexpected empty model, created by invalid + # POST data such as an object outside the formset's queryset. + # 2. The object was already deleted from the database. + if obj.pk is None: + continue + if form in forms_to_delete: + self.deleted_objects.append(obj) + self.delete_existing(obj, commit=commit) + elif form.has_changed(): + self.changed_objects.append((obj, form.changed_data)) + saved_instances.append(self.save_existing(form, obj, commit=commit)) + if not commit: + self.saved_forms.append(form) + return saved_instances + + def save_new_objects(self, commit=True): + self.new_objects = [] + for form in self.extra_forms: + if not form.has_changed(): + continue + # If someone has marked an add form for deletion, don't save the + # object. + if self.can_delete and self._should_delete_form(form): + continue + self.new_objects.append(self.save_new(form, commit=commit)) + if not commit: + self.saved_forms.append(form) + return self.new_objects + + def add_fields(self, form, index): + """Add a hidden field for the object's primary key.""" + from django.db.models import AutoField, ForeignKey, OneToOneField + + self._pk_field = pk = self.model._meta.pk + # If a pk isn't editable, then it won't be on the form, so we need to + # add it here so we can tell which object is which when we get the + # data back. Generally, pk.editable should be false, but for some + # reason, auto_created pk fields and AutoField's editable attribute is + # True, so check for that as well. + + def pk_is_not_editable(pk): + return ( + (not pk.editable) + or (pk.auto_created or isinstance(pk, AutoField)) + or ( + pk.remote_field + and pk.remote_field.parent_link + and pk_is_not_editable(pk.remote_field.model._meta.pk) + ) + ) + + if pk_is_not_editable(pk) or pk.name not in form.fields: + if form.is_bound: + # If we're adding the related instance, ignore its primary key + # as it could be an auto-generated default which isn't actually + # in the database. + pk_value = None if form.instance._state.adding else form.instance.pk + else: + try: + if index is not None: + pk_value = self.get_queryset()[index].pk + else: + pk_value = None + except IndexError: + pk_value = None + if isinstance(pk, (ForeignKey, OneToOneField)): + qs = pk.remote_field.model._default_manager.get_queryset() + else: + qs = self.model._default_manager.get_queryset() + qs = qs.using(form.instance._state.db) + if form._meta.widgets: + widget = form._meta.widgets.get(self._pk_field.name, HiddenInput) + else: + widget = HiddenInput + form.fields[self._pk_field.name] = ModelChoiceField( + qs, initial=pk_value, required=False, widget=widget + ) + super().add_fields(form, index) + + +def modelformset_factory( + model, + form=ModelForm, + formfield_callback=None, + formset=BaseModelFormSet, + extra=1, + can_delete=False, + can_order=False, + max_num=None, + fields=None, + exclude=None, + widgets=None, + validate_max=False, + localized_fields=None, + labels=None, + help_texts=None, + error_messages=None, + min_num=None, + validate_min=False, + field_classes=None, + absolute_max=None, + can_delete_extra=True, + renderer=None, + edit_only=False, +): + """Return a FormSet class for the given Django model class.""" + meta = getattr(form, "Meta", None) + if ( + getattr(meta, "fields", fields) is None + and getattr(meta, "exclude", exclude) is None + ): + raise ImproperlyConfigured( + "Calling modelformset_factory without defining 'fields' or " + "'exclude' explicitly is prohibited." + ) + + form = modelform_factory( + model, + form=form, + fields=fields, + exclude=exclude, + formfield_callback=formfield_callback, + widgets=widgets, + localized_fields=localized_fields, + labels=labels, + help_texts=help_texts, + error_messages=error_messages, + field_classes=field_classes, + ) + FormSet = formset_factory( + form, + formset, + extra=extra, + min_num=min_num, + max_num=max_num, + can_order=can_order, + can_delete=can_delete, + validate_min=validate_min, + validate_max=validate_max, + absolute_max=absolute_max, + can_delete_extra=can_delete_extra, + renderer=renderer, + ) + FormSet.model = model + FormSet.edit_only = edit_only + return FormSet + + +# InlineFormSets ############################################################# + + +class BaseInlineFormSet(BaseModelFormSet): + """A formset for child objects related to a parent.""" + + def __init__( + self, + data=None, + files=None, + instance=None, + save_as_new=False, + prefix=None, + queryset=None, + **kwargs, + ): + if instance is None: + self.instance = self.fk.remote_field.model() + else: + self.instance = instance + self.save_as_new = save_as_new + if queryset is None: + queryset = self.model._default_manager + if self.instance.pk is not None: + qs = queryset.filter(**{self.fk.name: self.instance}) + else: + qs = queryset.none() + self.unique_fields = {self.fk.name} + super().__init__(data, files, prefix=prefix, queryset=qs, **kwargs) + + # Add the generated field to form._meta.fields if it's defined to make + # sure validation isn't skipped on that field. + if self.form._meta.fields and self.fk.name not in self.form._meta.fields: + if isinstance(self.form._meta.fields, tuple): + self.form._meta.fields = list(self.form._meta.fields) + self.form._meta.fields.append(self.fk.name) + + def initial_form_count(self): + if self.save_as_new: + return 0 + return super().initial_form_count() + + def _construct_form(self, i, **kwargs): + form = super()._construct_form(i, **kwargs) + if self.save_as_new: + mutable = getattr(form.data, "_mutable", None) + # Allow modifying an immutable QueryDict. + if mutable is not None: + form.data._mutable = True + # Remove the primary key from the form's data, we are only + # creating new instances + form.data[form.add_prefix(self._pk_field.name)] = None + # Remove the foreign key from the form's data + form.data[form.add_prefix(self.fk.name)] = None + if mutable is not None: + form.data._mutable = mutable + + # Set the fk value here so that the form can do its validation. + fk_value = self.instance.pk + if self.fk.remote_field.field_name != self.fk.remote_field.model._meta.pk.name: + fk_value = getattr(self.instance, self.fk.remote_field.field_name) + fk_value = getattr(fk_value, "pk", fk_value) + setattr(form.instance, self.fk.get_attname(), fk_value) + return form + + @classmethod + def get_default_prefix(cls): + return cls.fk.remote_field.get_accessor_name(model=cls.model).replace("+", "") + + def save_new(self, form, commit=True): + # Ensure the latest copy of the related instance is present on each + # form (it may have been saved after the formset was originally + # instantiated). + setattr(form.instance, self.fk.name, self.instance) + return super().save_new(form, commit=commit) + + def add_fields(self, form, index): + super().add_fields(form, index) + if self._pk_field == self.fk: + name = self._pk_field.name + kwargs = {"pk_field": True} + else: + # The foreign key field might not be on the form, so we poke at the + # Model field to get the label, since we need that for error messages. + name = self.fk.name + kwargs = { + "label": getattr( + form.fields.get(name), "label", capfirst(self.fk.verbose_name) + ) + } + + # The InlineForeignKeyField assumes that the foreign key relation is + # based on the parent model's pk. If this isn't the case, set to_field + # to correctly resolve the initial form value. + if self.fk.remote_field.field_name != self.fk.remote_field.model._meta.pk.name: + kwargs["to_field"] = self.fk.remote_field.field_name + + # If we're adding a new object, ignore a parent's auto-generated key + # as it will be regenerated on the save request. + if self.instance._state.adding: + if kwargs.get("to_field") is not None: + to_field = self.instance._meta.get_field(kwargs["to_field"]) + else: + to_field = self.instance._meta.pk + if to_field.has_default(): + setattr(self.instance, to_field.attname, None) + + form.fields[name] = InlineForeignKeyField(self.instance, **kwargs) + + def get_unique_error_message(self, unique_check): + unique_check = [field for field in unique_check if field != self.fk.name] + return super().get_unique_error_message(unique_check) + + +def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False): + """ + Find and return the ForeignKey from model to parent if there is one + (return None if can_fail is True and no such field exists). If fk_name is + provided, assume it is the name of the ForeignKey field. Unless can_fail is + True, raise an exception if there isn't a ForeignKey from model to + parent_model. + """ + # avoid circular import + from django.db.models import ForeignKey + + opts = model._meta + if fk_name: + fks_to_parent = [f for f in opts.fields if f.name == fk_name] + if len(fks_to_parent) == 1: + fk = fks_to_parent[0] + parent_list = parent_model._meta.get_parent_list() + if ( + not isinstance(fk, ForeignKey) + or ( + # ForeignKey to proxy models. + fk.remote_field.model._meta.proxy + and fk.remote_field.model._meta.proxy_for_model not in parent_list + ) + or ( + # ForeignKey to concrete models. + not fk.remote_field.model._meta.proxy + and fk.remote_field.model != parent_model + and fk.remote_field.model not in parent_list + ) + ): + raise ValueError( + "fk_name '%s' is not a ForeignKey to '%s'." + % (fk_name, parent_model._meta.label) + ) + elif not fks_to_parent: + raise ValueError( + "'%s' has no field named '%s'." % (model._meta.label, fk_name) + ) + else: + # Try to discover what the ForeignKey from model to parent_model is + parent_list = parent_model._meta.get_parent_list() + fks_to_parent = [ + f + for f in opts.fields + if isinstance(f, ForeignKey) + and ( + f.remote_field.model == parent_model + or f.remote_field.model in parent_list + or ( + f.remote_field.model._meta.proxy + and f.remote_field.model._meta.proxy_for_model in parent_list + ) + ) + ] + if len(fks_to_parent) == 1: + fk = fks_to_parent[0] + elif not fks_to_parent: + if can_fail: + return + raise ValueError( + "'%s' has no ForeignKey to '%s'." + % ( + model._meta.label, + parent_model._meta.label, + ) + ) + else: + raise ValueError( + "'%s' has more than one ForeignKey to '%s'. You must specify " + "a 'fk_name' attribute." + % ( + model._meta.label, + parent_model._meta.label, + ) + ) + return fk + + +def inlineformset_factory( + parent_model, + model, + form=ModelForm, + formset=BaseInlineFormSet, + fk_name=None, + fields=None, + exclude=None, + extra=3, + can_order=False, + can_delete=True, + max_num=None, + formfield_callback=None, + widgets=None, + validate_max=False, + localized_fields=None, + labels=None, + help_texts=None, + error_messages=None, + min_num=None, + validate_min=False, + field_classes=None, + absolute_max=None, + can_delete_extra=True, + renderer=None, + edit_only=False, +): + """ + Return an ``InlineFormSet`` for the given kwargs. + + ``fk_name`` must be provided if ``model`` has more than one ``ForeignKey`` + to ``parent_model``. + """ + fk = _get_foreign_key(parent_model, model, fk_name=fk_name) + # enforce a max_num=1 when the foreign key to the parent model is unique. + if fk.unique: + max_num = 1 + kwargs = { + "form": form, + "formfield_callback": formfield_callback, + "formset": formset, + "extra": extra, + "can_delete": can_delete, + "can_order": can_order, + "fields": fields, + "exclude": exclude, + "min_num": min_num, + "max_num": max_num, + "widgets": widgets, + "validate_min": validate_min, + "validate_max": validate_max, + "localized_fields": localized_fields, + "labels": labels, + "help_texts": help_texts, + "error_messages": error_messages, + "field_classes": field_classes, + "absolute_max": absolute_max, + "can_delete_extra": can_delete_extra, + "renderer": renderer, + "edit_only": edit_only, + } + FormSet = modelformset_factory(model, **kwargs) + FormSet.fk = fk + return FormSet + + +# Fields ##################################################################### + + +class InlineForeignKeyField(Field): + """ + A basic integer field that deals with validating the given value to a + given parent instance in an inline. + """ + + widget = HiddenInput + default_error_messages = { + "invalid_choice": _("The inline value did not match the parent instance."), + } + + def __init__(self, parent_instance, *args, pk_field=False, to_field=None, **kwargs): + self.parent_instance = parent_instance + self.pk_field = pk_field + self.to_field = to_field + if self.parent_instance is not None: + if self.to_field: + kwargs["initial"] = getattr(self.parent_instance, self.to_field) + else: + kwargs["initial"] = self.parent_instance.pk + kwargs["required"] = False + super().__init__(*args, **kwargs) + + def clean(self, value): + if value in self.empty_values: + if self.pk_field: + return None + # if there is no value act as we did before. + return self.parent_instance + # ensure the we compare the values as equal types. + if self.to_field: + orig = getattr(self.parent_instance, self.to_field) + else: + orig = self.parent_instance.pk + if str(value) != str(orig): + raise ValidationError( + self.error_messages["invalid_choice"], code="invalid_choice" + ) + return self.parent_instance + + def has_changed(self, initial, data): + return False + + +class ModelChoiceIteratorValue: + def __init__(self, value, instance): + self.value = value + self.instance = instance + + def __str__(self): + return str(self.value) + + def __hash__(self): + return hash(self.value) + + def __eq__(self, other): + if isinstance(other, ModelChoiceIteratorValue): + other = other.value + return self.value == other + + +class ModelChoiceIterator: + def __init__(self, field): + self.field = field + self.queryset = field.queryset + + def __iter__(self): + if self.field.empty_label is not None: + yield ("", self.field.empty_label) + queryset = self.queryset + # Can't use iterator() when queryset uses prefetch_related() + if not queryset._prefetch_related_lookups: + queryset = queryset.iterator() + for obj in queryset: + yield self.choice(obj) + + def __len__(self): + # count() adds a query but uses less memory since the QuerySet results + # won't be cached. In most cases, the choices will only be iterated on, + # and __len__() won't be called. + return self.queryset.count() + (1 if self.field.empty_label is not None else 0) + + def __bool__(self): + return self.field.empty_label is not None or self.queryset.exists() + + def choice(self, obj): + return ( + ModelChoiceIteratorValue(self.field.prepare_value(obj), obj), + self.field.label_from_instance(obj), + ) + + +class ModelChoiceField(ChoiceField): + """A ChoiceField whose choices are a model QuerySet.""" + + # This class is a subclass of ChoiceField for purity, but it doesn't + # actually use any of ChoiceField's implementation. + default_error_messages = { + "invalid_choice": _( + "Select a valid choice. That choice is not one of the available choices." + ), + } + iterator = ModelChoiceIterator + + def __init__( + self, + queryset, + *, + empty_label="---------", + required=True, + widget=None, + label=None, + initial=None, + help_text="", + to_field_name=None, + limit_choices_to=None, + blank=False, + **kwargs, + ): + # Call Field instead of ChoiceField __init__() because we don't need + # ChoiceField.__init__(). + Field.__init__( + self, + required=required, + widget=widget, + label=label, + initial=initial, + help_text=help_text, + **kwargs, + ) + if (required and initial is not None) or ( + isinstance(self.widget, RadioSelect) and not blank + ): + self.empty_label = None + else: + self.empty_label = empty_label + self.queryset = queryset + self.limit_choices_to = limit_choices_to # limit the queryset later. + self.to_field_name = to_field_name + + def get_limit_choices_to(self): + """ + Return ``limit_choices_to`` for this form field. + + If it is a callable, invoke it and return the result. + """ + if callable(self.limit_choices_to): + return self.limit_choices_to() + return self.limit_choices_to + + def __deepcopy__(self, memo): + result = super(ChoiceField, self).__deepcopy__(memo) + # Need to force a new ModelChoiceIterator to be created, bug #11183 + if self.queryset is not None: + result.queryset = self.queryset.all() + return result + + def _get_queryset(self): + return self._queryset + + def _set_queryset(self, queryset): + self._queryset = None if queryset is None else queryset.all() + self.widget.choices = self.choices + + queryset = property(_get_queryset, _set_queryset) + + # this method will be used to create object labels by the QuerySetIterator. + # Override it to customize the label. + def label_from_instance(self, obj): + """ + Convert objects into strings and generate the labels for the choices + presented by this object. Subclasses can override this method to + customize the display of the choices. + """ + return str(obj) + + def _get_choices(self): + # If self._choices is set, then somebody must have manually set + # the property self.choices. In this case, just return self._choices. + if hasattr(self, "_choices"): + return self._choices + + # Otherwise, execute the QuerySet in self.queryset to determine the + # choices dynamically. Return a fresh ModelChoiceIterator that has not been + # consumed. Note that we're instantiating a new ModelChoiceIterator *each* + # time _get_choices() is called (and, thus, each time self.choices is + # accessed) so that we can ensure the QuerySet has not been consumed. This + # construct might look complicated but it allows for lazy evaluation of + # the queryset. + return self.iterator(self) + + choices = property(_get_choices, ChoiceField._set_choices) + + def prepare_value(self, value): + if hasattr(value, "_meta"): + if self.to_field_name: + return value.serializable_value(self.to_field_name) + else: + return value.pk + return super().prepare_value(value) + + def to_python(self, value): + if value in self.empty_values: + return None + try: + key = self.to_field_name or "pk" + if isinstance(value, self.queryset.model): + value = getattr(value, key) + value = self.queryset.get(**{key: value}) + except (ValueError, TypeError, self.queryset.model.DoesNotExist): + raise ValidationError( + self.error_messages["invalid_choice"], + code="invalid_choice", + params={"value": value}, + ) + return value + + def validate(self, value): + return Field.validate(self, value) + + def has_changed(self, initial, data): + if self.disabled: + return False + initial_value = initial if initial is not None else "" + data_value = data if data is not None else "" + return str(self.prepare_value(initial_value)) != str(data_value) + + +class ModelMultipleChoiceField(ModelChoiceField): + """A MultipleChoiceField whose choices are a model QuerySet.""" + + widget = SelectMultiple + hidden_widget = MultipleHiddenInput + default_error_messages = { + "invalid_list": _("Enter a list of values."), + "invalid_choice": _( + "Select a valid choice. %(value)s is not one of the available choices." + ), + "invalid_pk_value": _("“%(pk)s” is not a valid value."), + } + + def __init__(self, queryset, **kwargs): + super().__init__(queryset, empty_label=None, **kwargs) + + def to_python(self, value): + if not value: + return [] + return list(self._check_values(value)) + + def clean(self, value): + value = self.prepare_value(value) + if self.required and not value: + raise ValidationError(self.error_messages["required"], code="required") + elif not self.required and not value: + return self.queryset.none() + if not isinstance(value, (list, tuple)): + raise ValidationError( + self.error_messages["invalid_list"], + code="invalid_list", + ) + qs = self._check_values(value) + # Since this overrides the inherited ModelChoiceField.clean + # we run custom validators here + self.run_validators(value) + return qs + + def _check_values(self, value): + """ + Given a list of possible PK values, return a QuerySet of the + corresponding objects. Raise a ValidationError if a given value is + invalid (not a valid PK, not in the queryset, etc.) + """ + key = self.to_field_name or "pk" + # deduplicate given values to avoid creating many querysets or + # requiring the database backend deduplicate efficiently. + try: + value = frozenset(value) + except TypeError: + # list of lists isn't hashable, for example + raise ValidationError( + self.error_messages["invalid_list"], + code="invalid_list", + ) + for pk in value: + try: + self.queryset.filter(**{key: pk}) + except (ValueError, TypeError): + raise ValidationError( + self.error_messages["invalid_pk_value"], + code="invalid_pk_value", + params={"pk": pk}, + ) + qs = self.queryset.filter(**{"%s__in" % key: value}) + pks = {str(getattr(o, key)) for o in qs} + for val in value: + if str(val) not in pks: + raise ValidationError( + self.error_messages["invalid_choice"], + code="invalid_choice", + params={"value": val}, + ) + return qs + + def prepare_value(self, value): + if ( + hasattr(value, "__iter__") + and not isinstance(value, str) + and not hasattr(value, "_meta") + ): + prepare_value = super().prepare_value + return [prepare_value(v) for v in value] + return super().prepare_value(value) + + def has_changed(self, initial, data): + if self.disabled: + return False + if initial is None: + initial = [] + if data is None: + data = [] + if len(initial) != len(data): + return True + initial_set = {str(value) for value in self.prepare_value(initial)} + data_set = {str(value) for value in data} + return data_set != initial_set + + +def modelform_defines_fields(form_class): + return hasattr(form_class, "_meta") and ( + form_class._meta.fields is not None or form_class._meta.exclude is not None + ) diff --git a/virt/lib/python3.9/site-packages/django/forms/widgets 3.py b/virt/lib/python3.9/site-packages/django/forms/widgets 3.py new file mode 100644 index 00000000..df779e9e --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/forms/widgets 3.py @@ -0,0 +1,1202 @@ +""" +HTML Widget classes +""" + +import copy +import datetime +import warnings +from collections import defaultdict +from itertools import chain + +from django.forms.utils import to_current_timezone +from django.templatetags.static import static +from django.utils import formats +from django.utils.datastructures import OrderedSet +from django.utils.dates import MONTHS +from django.utils.formats import get_format +from django.utils.html import format_html, html_safe +from django.utils.regex_helper import _lazy_re_compile +from django.utils.safestring import mark_safe +from django.utils.topological_sort import CyclicDependencyError, stable_topological_sort +from django.utils.translation import gettext_lazy as _ + +from .renderers import get_default_renderer + +__all__ = ( + "Media", + "MediaDefiningClass", + "Widget", + "TextInput", + "NumberInput", + "EmailInput", + "URLInput", + "PasswordInput", + "HiddenInput", + "MultipleHiddenInput", + "FileInput", + "ClearableFileInput", + "Textarea", + "DateInput", + "DateTimeInput", + "TimeInput", + "CheckboxInput", + "Select", + "NullBooleanSelect", + "SelectMultiple", + "RadioSelect", + "CheckboxSelectMultiple", + "MultiWidget", + "SplitDateTimeWidget", + "SplitHiddenDateTimeWidget", + "SelectDateWidget", +) + +MEDIA_TYPES = ("css", "js") + + +class MediaOrderConflictWarning(RuntimeWarning): + pass + + +@html_safe +class Media: + def __init__(self, media=None, css=None, js=None): + if media is not None: + css = getattr(media, "css", {}) + js = getattr(media, "js", []) + else: + if css is None: + css = {} + if js is None: + js = [] + self._css_lists = [css] + self._js_lists = [js] + + def __repr__(self): + return "Media(css=%r, js=%r)" % (self._css, self._js) + + def __str__(self): + return self.render() + + @property + def _css(self): + css = defaultdict(list) + for css_list in self._css_lists: + for medium, sublist in css_list.items(): + css[medium].append(sublist) + return {medium: self.merge(*lists) for medium, lists in css.items()} + + @property + def _js(self): + return self.merge(*self._js_lists) + + def render(self): + return mark_safe( + "\n".join( + chain.from_iterable( + getattr(self, "render_" + name)() for name in MEDIA_TYPES + ) + ) + ) + + def render_js(self): + return [ + path.__html__() + if hasattr(path, "__html__") + else format_html('<script src="{}"></script>', self.absolute_path(path)) + for path in self._js + ] + + def render_css(self): + # To keep rendering order consistent, we can't just iterate over items(). + # We need to sort the keys, and iterate over the sorted list. + media = sorted(self._css) + return chain.from_iterable( + [ + path.__html__() + if hasattr(path, "__html__") + else format_html( + '<link href="{}" media="{}" rel="stylesheet">', + self.absolute_path(path), + medium, + ) + for path in self._css[medium] + ] + for medium in media + ) + + def absolute_path(self, path): + """ + Given a relative or absolute path to a static asset, return an absolute + path. An absolute path will be returned unchanged while a relative path + will be passed to django.templatetags.static.static(). + """ + if path.startswith(("http://", "https://", "/")): + return path + return static(path) + + def __getitem__(self, name): + """Return a Media object that only contains media of the given type.""" + if name in MEDIA_TYPES: + return Media(**{str(name): getattr(self, "_" + name)}) + raise KeyError('Unknown media type "%s"' % name) + + @staticmethod + def merge(*lists): + """ + Merge lists while trying to keep the relative order of the elements. + Warn if the lists have the same elements in a different relative order. + + For static assets it can be important to have them included in the DOM + in a certain order. In JavaScript you may not be able to reference a + global or in CSS you might want to override a style. + """ + dependency_graph = defaultdict(set) + all_items = OrderedSet() + for list_ in filter(None, lists): + head = list_[0] + # The first items depend on nothing but have to be part of the + # dependency graph to be included in the result. + dependency_graph.setdefault(head, set()) + for item in list_: + all_items.add(item) + # No self dependencies + if head != item: + dependency_graph[item].add(head) + head = item + try: + return stable_topological_sort(all_items, dependency_graph) + except CyclicDependencyError: + warnings.warn( + "Detected duplicate Media files in an opposite order: {}".format( + ", ".join(repr(list_) for list_ in lists) + ), + MediaOrderConflictWarning, + ) + return list(all_items) + + def __add__(self, other): + combined = Media() + combined._css_lists = self._css_lists[:] + combined._js_lists = self._js_lists[:] + for item in other._css_lists: + if item and item not in self._css_lists: + combined._css_lists.append(item) + for item in other._js_lists: + if item and item not in self._js_lists: + combined._js_lists.append(item) + return combined + + +def media_property(cls): + def _media(self): + # Get the media property of the superclass, if it exists + sup_cls = super(cls, self) + try: + base = sup_cls.media + except AttributeError: + base = Media() + + # Get the media definition for this class + definition = getattr(cls, "Media", None) + if definition: + extend = getattr(definition, "extend", True) + if extend: + if extend is True: + m = base + else: + m = Media() + for medium in extend: + m += base[medium] + return m + Media(definition) + return Media(definition) + return base + + return property(_media) + + +class MediaDefiningClass(type): + """ + Metaclass for classes that can have media definitions. + """ + + def __new__(mcs, name, bases, attrs): + new_class = super().__new__(mcs, name, bases, attrs) + + if "media" not in attrs: + new_class.media = media_property(new_class) + + return new_class + + +class Widget(metaclass=MediaDefiningClass): + needs_multipart_form = False # Determines does this widget need multipart form + is_localized = False + is_required = False + supports_microseconds = True + use_fieldset = False + + def __init__(self, attrs=None): + self.attrs = {} if attrs is None else attrs.copy() + + def __deepcopy__(self, memo): + obj = copy.copy(self) + obj.attrs = self.attrs.copy() + memo[id(self)] = obj + return obj + + @property + def is_hidden(self): + return self.input_type == "hidden" if hasattr(self, "input_type") else False + + def subwidgets(self, name, value, attrs=None): + context = self.get_context(name, value, attrs) + yield context["widget"] + + def format_value(self, value): + """ + Return a value as it should appear when rendered in a template. + """ + if value == "" or value is None: + return None + if self.is_localized: + return formats.localize_input(value) + return str(value) + + def get_context(self, name, value, attrs): + return { + "widget": { + "name": name, + "is_hidden": self.is_hidden, + "required": self.is_required, + "value": self.format_value(value), + "attrs": self.build_attrs(self.attrs, attrs), + "template_name": self.template_name, + }, + } + + def render(self, name, value, attrs=None, renderer=None): + """Render the widget as an HTML string.""" + context = self.get_context(name, value, attrs) + return self._render(self.template_name, context, renderer) + + def _render(self, template_name, context, renderer=None): + if renderer is None: + renderer = get_default_renderer() + return mark_safe(renderer.render(template_name, context)) + + def build_attrs(self, base_attrs, extra_attrs=None): + """Build an attribute dictionary.""" + return {**base_attrs, **(extra_attrs or {})} + + def value_from_datadict(self, data, files, name): + """ + Given a dictionary of data and this widget's name, return the value + of this widget or None if it's not provided. + """ + return data.get(name) + + def value_omitted_from_data(self, data, files, name): + return name not in data + + def id_for_label(self, id_): + """ + Return the HTML ID attribute of this Widget for use by a <label>, given + the ID of the field. Return an empty string if no ID is available. + + This hook is necessary because some widgets have multiple HTML + elements and, thus, multiple IDs. In that case, this method should + return an ID value that corresponds to the first ID in the widget's + tags. + """ + return id_ + + def use_required_attribute(self, initial): + return not self.is_hidden + + +class Input(Widget): + """ + Base class for all <input> widgets. + """ + + input_type = None # Subclasses must define this. + template_name = "django/forms/widgets/input.html" + + def __init__(self, attrs=None): + if attrs is not None: + attrs = attrs.copy() + self.input_type = attrs.pop("type", self.input_type) + super().__init__(attrs) + + def get_context(self, name, value, attrs): + context = super().get_context(name, value, attrs) + context["widget"]["type"] = self.input_type + return context + + +class TextInput(Input): + input_type = "text" + template_name = "django/forms/widgets/text.html" + + +class NumberInput(Input): + input_type = "number" + template_name = "django/forms/widgets/number.html" + + +class EmailInput(Input): + input_type = "email" + template_name = "django/forms/widgets/email.html" + + +class URLInput(Input): + input_type = "url" + template_name = "django/forms/widgets/url.html" + + +class PasswordInput(Input): + input_type = "password" + template_name = "django/forms/widgets/password.html" + + def __init__(self, attrs=None, render_value=False): + super().__init__(attrs) + self.render_value = render_value + + def get_context(self, name, value, attrs): + if not self.render_value: + value = None + return super().get_context(name, value, attrs) + + +class HiddenInput(Input): + input_type = "hidden" + template_name = "django/forms/widgets/hidden.html" + + +class MultipleHiddenInput(HiddenInput): + """ + Handle <input type="hidden"> for fields that have a list + of values. + """ + + template_name = "django/forms/widgets/multiple_hidden.html" + + def get_context(self, name, value, attrs): + context = super().get_context(name, value, attrs) + final_attrs = context["widget"]["attrs"] + id_ = context["widget"]["attrs"].get("id") + + subwidgets = [] + for index, value_ in enumerate(context["widget"]["value"]): + widget_attrs = final_attrs.copy() + if id_: + # An ID attribute was given. Add a numeric index as a suffix + # so that the inputs don't all have the same ID attribute. + widget_attrs["id"] = "%s_%s" % (id_, index) + widget = HiddenInput() + widget.is_required = self.is_required + subwidgets.append(widget.get_context(name, value_, widget_attrs)["widget"]) + + context["widget"]["subwidgets"] = subwidgets + return context + + def value_from_datadict(self, data, files, name): + try: + getter = data.getlist + except AttributeError: + getter = data.get + return getter(name) + + def format_value(self, value): + return [] if value is None else value + + +class FileInput(Input): + allow_multiple_selected = False + input_type = "file" + needs_multipart_form = True + template_name = "django/forms/widgets/file.html" + + def __init__(self, attrs=None): + if ( + attrs is not None + and not self.allow_multiple_selected + and attrs.get("multiple", False) + ): + raise ValueError( + "%s doesn't support uploading multiple files." + % self.__class__.__qualname__ + ) + if self.allow_multiple_selected: + if attrs is None: + attrs = {"multiple": True} + else: + attrs.setdefault("multiple", True) + super().__init__(attrs) + + def format_value(self, value): + """File input never renders a value.""" + return + + def value_from_datadict(self, data, files, name): + "File widgets take data from FILES, not POST" + getter = files.get + if self.allow_multiple_selected: + try: + getter = files.getlist + except AttributeError: + pass + return getter(name) + + def value_omitted_from_data(self, data, files, name): + return name not in files + + def use_required_attribute(self, initial): + return super().use_required_attribute(initial) and not initial + + +FILE_INPUT_CONTRADICTION = object() + + +class ClearableFileInput(FileInput): + clear_checkbox_label = _("Clear") + initial_text = _("Currently") + input_text = _("Change") + template_name = "django/forms/widgets/clearable_file_input.html" + + def clear_checkbox_name(self, name): + """ + Given the name of the file input, return the name of the clear checkbox + input. + """ + return name + "-clear" + + def clear_checkbox_id(self, name): + """ + Given the name of the clear checkbox input, return the HTML id for it. + """ + return name + "_id" + + def is_initial(self, value): + """ + Return whether value is considered to be initial value. + """ + return bool(value and getattr(value, "url", False)) + + def format_value(self, value): + """ + Return the file object if it has a defined url attribute. + """ + if self.is_initial(value): + return value + + def get_context(self, name, value, attrs): + context = super().get_context(name, value, attrs) + checkbox_name = self.clear_checkbox_name(name) + checkbox_id = self.clear_checkbox_id(checkbox_name) + context["widget"].update( + { + "checkbox_name": checkbox_name, + "checkbox_id": checkbox_id, + "is_initial": self.is_initial(value), + "input_text": self.input_text, + "initial_text": self.initial_text, + "clear_checkbox_label": self.clear_checkbox_label, + } + ) + context["widget"]["attrs"].setdefault("disabled", False) + return context + + def value_from_datadict(self, data, files, name): + upload = super().value_from_datadict(data, files, name) + if not self.is_required and CheckboxInput().value_from_datadict( + data, files, self.clear_checkbox_name(name) + ): + if upload: + # If the user contradicts themselves (uploads a new file AND + # checks the "clear" checkbox), we return a unique marker + # object that FileField will turn into a ValidationError. + return FILE_INPUT_CONTRADICTION + # False signals to clear any existing value, as opposed to just None + return False + return upload + + def value_omitted_from_data(self, data, files, name): + return ( + super().value_omitted_from_data(data, files, name) + and self.clear_checkbox_name(name) not in data + ) + + +class Textarea(Widget): + template_name = "django/forms/widgets/textarea.html" + + def __init__(self, attrs=None): + # Use slightly better defaults than HTML's 20x2 box + default_attrs = {"cols": "40", "rows": "10"} + if attrs: + default_attrs.update(attrs) + super().__init__(default_attrs) + + +class DateTimeBaseInput(TextInput): + format_key = "" + supports_microseconds = False + + def __init__(self, attrs=None, format=None): + super().__init__(attrs) + self.format = format or None + + def format_value(self, value): + return formats.localize_input( + value, self.format or formats.get_format(self.format_key)[0] + ) + + +class DateInput(DateTimeBaseInput): + format_key = "DATE_INPUT_FORMATS" + template_name = "django/forms/widgets/date.html" + + +class DateTimeInput(DateTimeBaseInput): + format_key = "DATETIME_INPUT_FORMATS" + template_name = "django/forms/widgets/datetime.html" + + +class TimeInput(DateTimeBaseInput): + format_key = "TIME_INPUT_FORMATS" + template_name = "django/forms/widgets/time.html" + + +# Defined at module level so that CheckboxInput is picklable (#17976) +def boolean_check(v): + return not (v is False or v is None or v == "") + + +class CheckboxInput(Input): + input_type = "checkbox" + template_name = "django/forms/widgets/checkbox.html" + + def __init__(self, attrs=None, check_test=None): + super().__init__(attrs) + # check_test is a callable that takes a value and returns True + # if the checkbox should be checked for that value. + self.check_test = boolean_check if check_test is None else check_test + + def format_value(self, value): + """Only return the 'value' attribute if value isn't empty.""" + if value is True or value is False or value is None or value == "": + return + return str(value) + + def get_context(self, name, value, attrs): + if self.check_test(value): + attrs = {**(attrs or {}), "checked": True} + return super().get_context(name, value, attrs) + + def value_from_datadict(self, data, files, name): + if name not in data: + # A missing value means False because HTML form submission does not + # send results for unselected checkboxes. + return False + value = data.get(name) + # Translate true and false strings to boolean values. + values = {"true": True, "false": False} + if isinstance(value, str): + value = values.get(value.lower(), value) + return bool(value) + + def value_omitted_from_data(self, data, files, name): + # HTML checkboxes don't appear in POST data if not checked, so it's + # never known if the value is actually omitted. + return False + + +class ChoiceWidget(Widget): + allow_multiple_selected = False + input_type = None + template_name = None + option_template_name = None + add_id_index = True + checked_attribute = {"checked": True} + option_inherits_attrs = True + + def __init__(self, attrs=None, choices=()): + super().__init__(attrs) + # choices can be any iterable, but we may need to render this widget + # multiple times. Thus, collapse it into a list so it can be consumed + # more than once. + self.choices = list(choices) + + def __deepcopy__(self, memo): + obj = copy.copy(self) + obj.attrs = self.attrs.copy() + obj.choices = copy.copy(self.choices) + memo[id(self)] = obj + return obj + + def subwidgets(self, name, value, attrs=None): + """ + Yield all "subwidgets" of this widget. Used to enable iterating + options from a BoundField for choice widgets. + """ + value = self.format_value(value) + yield from self.options(name, value, attrs) + + def options(self, name, value, attrs=None): + """Yield a flat list of options for this widget.""" + for group in self.optgroups(name, value, attrs): + yield from group[1] + + def optgroups(self, name, value, attrs=None): + """Return a list of optgroups for this widget.""" + groups = [] + has_selected = False + + for index, (option_value, option_label) in enumerate(self.choices): + if option_value is None: + option_value = "" + + subgroup = [] + if isinstance(option_label, (list, tuple)): + group_name = option_value + subindex = 0 + choices = option_label + else: + group_name = None + subindex = None + choices = [(option_value, option_label)] + groups.append((group_name, subgroup, index)) + + for subvalue, sublabel in choices: + selected = (not has_selected or self.allow_multiple_selected) and str( + subvalue + ) in value + has_selected |= selected + subgroup.append( + self.create_option( + name, + subvalue, + sublabel, + selected, + index, + subindex=subindex, + attrs=attrs, + ) + ) + if subindex is not None: + subindex += 1 + return groups + + def create_option( + self, name, value, label, selected, index, subindex=None, attrs=None + ): + index = str(index) if subindex is None else "%s_%s" % (index, subindex) + option_attrs = ( + self.build_attrs(self.attrs, attrs) if self.option_inherits_attrs else {} + ) + if selected: + option_attrs.update(self.checked_attribute) + if "id" in option_attrs: + option_attrs["id"] = self.id_for_label(option_attrs["id"], index) + return { + "name": name, + "value": value, + "label": label, + "selected": selected, + "index": index, + "attrs": option_attrs, + "type": self.input_type, + "template_name": self.option_template_name, + "wrap_label": True, + } + + def get_context(self, name, value, attrs): + context = super().get_context(name, value, attrs) + context["widget"]["optgroups"] = self.optgroups( + name, context["widget"]["value"], attrs + ) + return context + + def id_for_label(self, id_, index="0"): + """ + Use an incremented id for each option where the main widget + references the zero index. + """ + if id_ and self.add_id_index: + id_ = "%s_%s" % (id_, index) + return id_ + + def value_from_datadict(self, data, files, name): + getter = data.get + if self.allow_multiple_selected: + try: + getter = data.getlist + except AttributeError: + pass + return getter(name) + + def format_value(self, value): + """Return selected values as a list.""" + if value is None and self.allow_multiple_selected: + return [] + if not isinstance(value, (tuple, list)): + value = [value] + return [str(v) if v is not None else "" for v in value] + + +class Select(ChoiceWidget): + input_type = "select" + template_name = "django/forms/widgets/select.html" + option_template_name = "django/forms/widgets/select_option.html" + add_id_index = False + checked_attribute = {"selected": True} + option_inherits_attrs = False + + def get_context(self, name, value, attrs): + context = super().get_context(name, value, attrs) + if self.allow_multiple_selected: + context["widget"]["attrs"]["multiple"] = True + return context + + @staticmethod + def _choice_has_empty_value(choice): + """Return True if the choice's value is empty string or None.""" + value, _ = choice + return value is None or value == "" + + def use_required_attribute(self, initial): + """ + Don't render 'required' if the first <option> has a value, as that's + invalid HTML. + """ + use_required_attribute = super().use_required_attribute(initial) + # 'required' is always okay for <select multiple>. + if self.allow_multiple_selected: + return use_required_attribute + + first_choice = next(iter(self.choices), None) + return ( + use_required_attribute + and first_choice is not None + and self._choice_has_empty_value(first_choice) + ) + + +class NullBooleanSelect(Select): + """ + A Select Widget intended to be used with NullBooleanField. + """ + + def __init__(self, attrs=None): + choices = ( + ("unknown", _("Unknown")), + ("true", _("Yes")), + ("false", _("No")), + ) + super().__init__(attrs, choices) + + def format_value(self, value): + try: + return { + True: "true", + False: "false", + "true": "true", + "false": "false", + # For backwards compatibility with Django < 2.2. + "2": "true", + "3": "false", + }[value] + except KeyError: + return "unknown" + + def value_from_datadict(self, data, files, name): + value = data.get(name) + return { + True: True, + "True": True, + "False": False, + False: False, + "true": True, + "false": False, + # For backwards compatibility with Django < 2.2. + "2": True, + "3": False, + }.get(value) + + +class SelectMultiple(Select): + allow_multiple_selected = True + + def value_from_datadict(self, data, files, name): + try: + getter = data.getlist + except AttributeError: + getter = data.get + return getter(name) + + def value_omitted_from_data(self, data, files, name): + # An unselected <select multiple> doesn't appear in POST data, so it's + # never known if the value is actually omitted. + return False + + +class RadioSelect(ChoiceWidget): + input_type = "radio" + template_name = "django/forms/widgets/radio.html" + option_template_name = "django/forms/widgets/radio_option.html" + use_fieldset = True + + def id_for_label(self, id_, index=None): + """ + Don't include for="field_0" in <label> to improve accessibility when + using a screen reader, in addition clicking such a label would toggle + the first input. + """ + if index is None: + return "" + return super().id_for_label(id_, index) + + +class CheckboxSelectMultiple(RadioSelect): + allow_multiple_selected = True + input_type = "checkbox" + template_name = "django/forms/widgets/checkbox_select.html" + option_template_name = "django/forms/widgets/checkbox_option.html" + + def use_required_attribute(self, initial): + # Don't use the 'required' attribute because browser validation would + # require all checkboxes to be checked instead of at least one. + return False + + def value_omitted_from_data(self, data, files, name): + # HTML checkboxes don't appear in POST data if not checked, so it's + # never known if the value is actually omitted. + return False + + +class MultiWidget(Widget): + """ + A widget that is composed of multiple widgets. + + In addition to the values added by Widget.get_context(), this widget + adds a list of subwidgets to the context as widget['subwidgets']. + These can be looped over and rendered like normal widgets. + + You'll probably want to use this class with MultiValueField. + """ + + template_name = "django/forms/widgets/multiwidget.html" + use_fieldset = True + + def __init__(self, widgets, attrs=None): + if isinstance(widgets, dict): + self.widgets_names = [("_%s" % name) if name else "" for name in widgets] + widgets = widgets.values() + else: + self.widgets_names = ["_%s" % i for i in range(len(widgets))] + self.widgets = [w() if isinstance(w, type) else w for w in widgets] + super().__init__(attrs) + + @property + def is_hidden(self): + return all(w.is_hidden for w in self.widgets) + + def get_context(self, name, value, attrs): + context = super().get_context(name, value, attrs) + if self.is_localized: + for widget in self.widgets: + widget.is_localized = self.is_localized + # value is a list/tuple of values, each corresponding to a widget + # in self.widgets. + if not isinstance(value, (list, tuple)): + value = self.decompress(value) + + final_attrs = context["widget"]["attrs"] + input_type = final_attrs.pop("type", None) + id_ = final_attrs.get("id") + subwidgets = [] + for i, (widget_name, widget) in enumerate( + zip(self.widgets_names, self.widgets) + ): + if input_type is not None: + widget.input_type = input_type + widget_name = name + widget_name + try: + widget_value = value[i] + except IndexError: + widget_value = None + if id_: + widget_attrs = final_attrs.copy() + widget_attrs["id"] = "%s_%s" % (id_, i) + else: + widget_attrs = final_attrs + subwidgets.append( + widget.get_context(widget_name, widget_value, widget_attrs)["widget"] + ) + context["widget"]["subwidgets"] = subwidgets + return context + + def id_for_label(self, id_): + return "" + + def value_from_datadict(self, data, files, name): + return [ + widget.value_from_datadict(data, files, name + widget_name) + for widget_name, widget in zip(self.widgets_names, self.widgets) + ] + + def value_omitted_from_data(self, data, files, name): + return all( + widget.value_omitted_from_data(data, files, name + widget_name) + for widget_name, widget in zip(self.widgets_names, self.widgets) + ) + + def decompress(self, value): + """ + Return a list of decompressed values for the given compressed value. + The given value can be assumed to be valid, but not necessarily + non-empty. + """ + raise NotImplementedError("Subclasses must implement this method.") + + def _get_media(self): + """ + Media for a multiwidget is the combination of all media of the + subwidgets. + """ + media = Media() + for w in self.widgets: + media += w.media + return media + + media = property(_get_media) + + def __deepcopy__(self, memo): + obj = super().__deepcopy__(memo) + obj.widgets = copy.deepcopy(self.widgets) + return obj + + @property + def needs_multipart_form(self): + return any(w.needs_multipart_form for w in self.widgets) + + +class SplitDateTimeWidget(MultiWidget): + """ + A widget that splits datetime input into two <input type="text"> boxes. + """ + + supports_microseconds = False + template_name = "django/forms/widgets/splitdatetime.html" + + def __init__( + self, + attrs=None, + date_format=None, + time_format=None, + date_attrs=None, + time_attrs=None, + ): + widgets = ( + DateInput( + attrs=attrs if date_attrs is None else date_attrs, + format=date_format, + ), + TimeInput( + attrs=attrs if time_attrs is None else time_attrs, + format=time_format, + ), + ) + super().__init__(widgets) + + def decompress(self, value): + if value: + value = to_current_timezone(value) + return [value.date(), value.time()] + return [None, None] + + +class SplitHiddenDateTimeWidget(SplitDateTimeWidget): + """ + A widget that splits datetime input into two <input type="hidden"> inputs. + """ + + template_name = "django/forms/widgets/splithiddendatetime.html" + + def __init__( + self, + attrs=None, + date_format=None, + time_format=None, + date_attrs=None, + time_attrs=None, + ): + super().__init__(attrs, date_format, time_format, date_attrs, time_attrs) + for widget in self.widgets: + widget.input_type = "hidden" + + +class SelectDateWidget(Widget): + """ + A widget that splits date input into three <select> boxes. + + This also serves as an example of a Widget that has more than one HTML + element and hence implements value_from_datadict. + """ + + none_value = ("", "---") + month_field = "%s_month" + day_field = "%s_day" + year_field = "%s_year" + template_name = "django/forms/widgets/select_date.html" + input_type = "select" + select_widget = Select + date_re = _lazy_re_compile(r"(\d{4}|0)-(\d\d?)-(\d\d?)$") + use_fieldset = True + + def __init__(self, attrs=None, years=None, months=None, empty_label=None): + self.attrs = attrs or {} + + # Optional list or tuple of years to use in the "year" select box. + if years: + self.years = years + else: + this_year = datetime.date.today().year + self.years = range(this_year, this_year + 10) + + # Optional dict of months to use in the "month" select box. + if months: + self.months = months + else: + self.months = MONTHS + + # Optional string, list, or tuple to use as empty_label. + if isinstance(empty_label, (list, tuple)): + if not len(empty_label) == 3: + raise ValueError("empty_label list/tuple must have 3 elements.") + + self.year_none_value = ("", empty_label[0]) + self.month_none_value = ("", empty_label[1]) + self.day_none_value = ("", empty_label[2]) + else: + if empty_label is not None: + self.none_value = ("", empty_label) + + self.year_none_value = self.none_value + self.month_none_value = self.none_value + self.day_none_value = self.none_value + + def get_context(self, name, value, attrs): + context = super().get_context(name, value, attrs) + date_context = {} + year_choices = [(i, str(i)) for i in self.years] + if not self.is_required: + year_choices.insert(0, self.year_none_value) + year_name = self.year_field % name + date_context["year"] = self.select_widget( + attrs, choices=year_choices + ).get_context( + name=year_name, + value=context["widget"]["value"]["year"], + attrs={**context["widget"]["attrs"], "id": "id_%s" % year_name}, + ) + month_choices = list(self.months.items()) + if not self.is_required: + month_choices.insert(0, self.month_none_value) + month_name = self.month_field % name + date_context["month"] = self.select_widget( + attrs, choices=month_choices + ).get_context( + name=month_name, + value=context["widget"]["value"]["month"], + attrs={**context["widget"]["attrs"], "id": "id_%s" % month_name}, + ) + day_choices = [(i, i) for i in range(1, 32)] + if not self.is_required: + day_choices.insert(0, self.day_none_value) + day_name = self.day_field % name + date_context["day"] = self.select_widget( + attrs, + choices=day_choices, + ).get_context( + name=day_name, + value=context["widget"]["value"]["day"], + attrs={**context["widget"]["attrs"], "id": "id_%s" % day_name}, + ) + subwidgets = [] + for field in self._parse_date_fmt(): + subwidgets.append(date_context[field]["widget"]) + context["widget"]["subwidgets"] = subwidgets + return context + + def format_value(self, value): + """ + Return a dict containing the year, month, and day of the current value. + Use dict instead of a datetime to allow invalid dates such as February + 31 to display correctly. + """ + year, month, day = None, None, None + if isinstance(value, (datetime.date, datetime.datetime)): + year, month, day = value.year, value.month, value.day + elif isinstance(value, str): + match = self.date_re.match(value) + if match: + # Convert any zeros in the date to empty strings to match the + # empty option value. + year, month, day = [int(val) or "" for val in match.groups()] + else: + input_format = get_format("DATE_INPUT_FORMATS")[0] + try: + d = datetime.datetime.strptime(value, input_format) + except ValueError: + pass + else: + year, month, day = d.year, d.month, d.day + return {"year": year, "month": month, "day": day} + + @staticmethod + def _parse_date_fmt(): + fmt = get_format("DATE_FORMAT") + escaped = False + for char in fmt: + if escaped: + escaped = False + elif char == "\\": + escaped = True + elif char in "Yy": + yield "year" + elif char in "bEFMmNn": + yield "month" + elif char in "dj": + yield "day" + + def id_for_label(self, id_): + for first_select in self._parse_date_fmt(): + return "%s_%s" % (id_, first_select) + return "%s_month" % id_ + + def value_from_datadict(self, data, files, name): + y = data.get(self.year_field % name) + m = data.get(self.month_field % name) + d = data.get(self.day_field % name) + if y == m == d == "": + return None + if y is not None and m is not None and d is not None: + input_format = get_format("DATE_INPUT_FORMATS")[0] + input_format = formats.sanitize_strftime_format(input_format) + try: + date_value = datetime.date(int(y), int(m), int(d)) + except ValueError: + # Return pseudo-ISO dates with zeros for any unselected values, + # e.g. '2017-0-23'. + return "%s-%s-%s" % (y or 0, m or 0, d or 0) + return date_value.strftime(input_format) + return data.get(name) + + def value_omitted_from_data(self, data, files, name): + return not any( + ("{}_{}".format(name, interval) in data) + for interval in ("year", "month", "day") + ) diff --git a/virt/lib/python3.9/site-packages/django/template/defaulttags 3.py b/virt/lib/python3.9/site-packages/django/template/defaulttags 3.py new file mode 100644 index 00000000..cd2d3fdc --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/template/defaulttags 3.py @@ -0,0 +1,1493 @@ +"""Default tags used by the template system, available to all templates.""" +import re +import sys +import warnings +from collections import namedtuple +from datetime import datetime +from itertools import cycle as itertools_cycle +from itertools import groupby + +from django.conf import settings +from django.utils import timezone +from django.utils.html import conditional_escape, escape, format_html +from django.utils.lorem_ipsum import paragraphs, words +from django.utils.safestring import mark_safe + +from .base import ( + BLOCK_TAG_END, + BLOCK_TAG_START, + COMMENT_TAG_END, + COMMENT_TAG_START, + FILTER_SEPARATOR, + SINGLE_BRACE_END, + SINGLE_BRACE_START, + VARIABLE_ATTRIBUTE_SEPARATOR, + VARIABLE_TAG_END, + VARIABLE_TAG_START, + Node, + NodeList, + TemplateSyntaxError, + VariableDoesNotExist, + kwarg_re, + render_value_in_context, + token_kwargs, +) +from .context import Context +from .defaultfilters import date +from .library import Library +from .smartif import IfParser, Literal + +register = Library() + + +class AutoEscapeControlNode(Node): + """Implement the actions of the autoescape tag.""" + + def __init__(self, setting, nodelist): + self.setting, self.nodelist = setting, nodelist + + def render(self, context): + old_setting = context.autoescape + context.autoescape = self.setting + output = self.nodelist.render(context) + context.autoescape = old_setting + if self.setting: + return mark_safe(output) + else: + return output + + +class CommentNode(Node): + child_nodelists = () + + def render(self, context): + return "" + + +class CsrfTokenNode(Node): + child_nodelists = () + + def render(self, context): + csrf_token = context.get("csrf_token") + if csrf_token: + if csrf_token == "NOTPROVIDED": + return format_html("") + else: + return format_html( + '<input type="hidden" name="csrfmiddlewaretoken" value="{}">', + csrf_token, + ) + else: + # It's very probable that the token is missing because of + # misconfiguration, so we raise a warning + if settings.DEBUG: + warnings.warn( + "A {% csrf_token %} was used in a template, but the context " + "did not provide the value. This is usually caused by not " + "using RequestContext." + ) + return "" + + +class CycleNode(Node): + def __init__(self, cyclevars, variable_name=None, silent=False): + self.cyclevars = cyclevars + self.variable_name = variable_name + self.silent = silent + + def render(self, context): + if self not in context.render_context: + # First time the node is rendered in template + context.render_context[self] = itertools_cycle(self.cyclevars) + cycle_iter = context.render_context[self] + value = next(cycle_iter).resolve(context) + if self.variable_name: + context.set_upward(self.variable_name, value) + if self.silent: + return "" + return render_value_in_context(value, context) + + def reset(self, context): + """ + Reset the cycle iteration back to the beginning. + """ + context.render_context[self] = itertools_cycle(self.cyclevars) + + +class DebugNode(Node): + def render(self, context): + if not settings.DEBUG: + return "" + + from pprint import pformat + + output = [escape(pformat(val)) for val in context] + output.append("\n\n") + output.append(escape(pformat(sys.modules))) + return "".join(output) + + +class FilterNode(Node): + def __init__(self, filter_expr, nodelist): + self.filter_expr, self.nodelist = filter_expr, nodelist + + def render(self, context): + output = self.nodelist.render(context) + # Apply filters. + with context.push(var=output): + return self.filter_expr.resolve(context) + + +class FirstOfNode(Node): + def __init__(self, variables, asvar=None): + self.vars = variables + self.asvar = asvar + + def render(self, context): + first = "" + for var in self.vars: + value = var.resolve(context, ignore_failures=True) + if value: + first = render_value_in_context(value, context) + break + if self.asvar: + context[self.asvar] = first + return "" + return first + + +class ForNode(Node): + child_nodelists = ("nodelist_loop", "nodelist_empty") + + def __init__( + self, loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty=None + ): + self.loopvars, self.sequence = loopvars, sequence + self.is_reversed = is_reversed + self.nodelist_loop = nodelist_loop + if nodelist_empty is None: + self.nodelist_empty = NodeList() + else: + self.nodelist_empty = nodelist_empty + + def __repr__(self): + reversed_text = " reversed" if self.is_reversed else "" + return "<%s: for %s in %s, tail_len: %d%s>" % ( + self.__class__.__name__, + ", ".join(self.loopvars), + self.sequence, + len(self.nodelist_loop), + reversed_text, + ) + + def render(self, context): + if "forloop" in context: + parentloop = context["forloop"] + else: + parentloop = {} + with context.push(): + values = self.sequence.resolve(context, ignore_failures=True) + if values is None: + values = [] + if not hasattr(values, "__len__"): + values = list(values) + len_values = len(values) + if len_values < 1: + return self.nodelist_empty.render(context) + nodelist = [] + if self.is_reversed: + values = reversed(values) + num_loopvars = len(self.loopvars) + unpack = num_loopvars > 1 + # Create a forloop value in the context. We'll update counters on each + # iteration just below. + loop_dict = context["forloop"] = {"parentloop": parentloop} + for i, item in enumerate(values): + # Shortcuts for current loop iteration number. + loop_dict["counter0"] = i + loop_dict["counter"] = i + 1 + # Reverse counter iteration numbers. + loop_dict["revcounter"] = len_values - i + loop_dict["revcounter0"] = len_values - i - 1 + # Boolean values designating first and last times through loop. + loop_dict["first"] = i == 0 + loop_dict["last"] = i == len_values - 1 + + pop_context = False + if unpack: + # If there are multiple loop variables, unpack the item into + # them. + try: + len_item = len(item) + except TypeError: # not an iterable + len_item = 1 + # Check loop variable count before unpacking + if num_loopvars != len_item: + raise ValueError( + "Need {} values to unpack in for loop; got {}. ".format( + num_loopvars, len_item + ), + ) + unpacked_vars = dict(zip(self.loopvars, item)) + pop_context = True + context.update(unpacked_vars) + else: + context[self.loopvars[0]] = item + + for node in self.nodelist_loop: + nodelist.append(node.render_annotated(context)) + + if pop_context: + # Pop the loop variables pushed on to the context to avoid + # the context ending up in an inconsistent state when other + # tags (e.g., include and with) push data to context. + context.pop() + return mark_safe("".join(nodelist)) + + +class IfChangedNode(Node): + child_nodelists = ("nodelist_true", "nodelist_false") + + def __init__(self, nodelist_true, nodelist_false, *varlist): + self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false + self._varlist = varlist + + def render(self, context): + # Init state storage + state_frame = self._get_context_stack_frame(context) + state_frame.setdefault(self) + + nodelist_true_output = None + if self._varlist: + # Consider multiple parameters. This behaves like an OR evaluation + # of the multiple variables. + compare_to = [ + var.resolve(context, ignore_failures=True) for var in self._varlist + ] + else: + # The "{% ifchanged %}" syntax (without any variables) compares + # the rendered output. + compare_to = nodelist_true_output = self.nodelist_true.render(context) + + if compare_to != state_frame[self]: + state_frame[self] = compare_to + # render true block if not already rendered + return nodelist_true_output or self.nodelist_true.render(context) + elif self.nodelist_false: + return self.nodelist_false.render(context) + return "" + + def _get_context_stack_frame(self, context): + # The Context object behaves like a stack where each template tag can + # create a new scope. Find the place where to store the state to detect + # changes. + if "forloop" in context: + # Ifchanged is bound to the local for loop. + # When there is a loop-in-loop, the state is bound to the inner loop, + # so it resets when the outer loop continues. + return context["forloop"] + else: + # Using ifchanged outside loops. Effectively this is a no-op + # because the state is associated with 'self'. + return context.render_context + + +class IfNode(Node): + def __init__(self, conditions_nodelists): + self.conditions_nodelists = conditions_nodelists + + def __repr__(self): + return "<%s>" % self.__class__.__name__ + + def __iter__(self): + for _, nodelist in self.conditions_nodelists: + yield from nodelist + + @property + def nodelist(self): + return NodeList(self) + + def render(self, context): + for condition, nodelist in self.conditions_nodelists: + if condition is not None: # if / elif clause + try: + match = condition.eval(context) + except VariableDoesNotExist: + match = None + else: # else clause + match = True + + if match: + return nodelist.render(context) + + return "" + + +class LoremNode(Node): + def __init__(self, count, method, common): + self.count, self.method, self.common = count, method, common + + def render(self, context): + try: + count = int(self.count.resolve(context)) + except (ValueError, TypeError): + count = 1 + if self.method == "w": + return words(count, common=self.common) + else: + paras = paragraphs(count, common=self.common) + if self.method == "p": + paras = ["<p>%s</p>" % p for p in paras] + return "\n\n".join(paras) + + +GroupedResult = namedtuple("GroupedResult", ["grouper", "list"]) + + +class RegroupNode(Node): + def __init__(self, target, expression, var_name): + self.target, self.expression = target, expression + self.var_name = var_name + + def resolve_expression(self, obj, context): + # This method is called for each object in self.target. See regroup() + # for the reason why we temporarily put the object in the context. + context[self.var_name] = obj + return self.expression.resolve(context, ignore_failures=True) + + def render(self, context): + obj_list = self.target.resolve(context, ignore_failures=True) + if obj_list is None: + # target variable wasn't found in context; fail silently. + context[self.var_name] = [] + return "" + # List of dictionaries in the format: + # {'grouper': 'key', 'list': [list of contents]}. + context[self.var_name] = [ + GroupedResult(grouper=key, list=list(val)) + for key, val in groupby( + obj_list, lambda obj: self.resolve_expression(obj, context) + ) + ] + return "" + + +class LoadNode(Node): + child_nodelists = () + + def render(self, context): + return "" + + +class NowNode(Node): + def __init__(self, format_string, asvar=None): + self.format_string = format_string + self.asvar = asvar + + def render(self, context): + tzinfo = timezone.get_current_timezone() if settings.USE_TZ else None + formatted = date(datetime.now(tz=tzinfo), self.format_string) + + if self.asvar: + context[self.asvar] = formatted + return "" + else: + return formatted + + +class ResetCycleNode(Node): + def __init__(self, node): + self.node = node + + def render(self, context): + self.node.reset(context) + return "" + + +class SpacelessNode(Node): + def __init__(self, nodelist): + self.nodelist = nodelist + + def render(self, context): + from django.utils.html import strip_spaces_between_tags + + return strip_spaces_between_tags(self.nodelist.render(context).strip()) + + +class TemplateTagNode(Node): + mapping = { + "openblock": BLOCK_TAG_START, + "closeblock": BLOCK_TAG_END, + "openvariable": VARIABLE_TAG_START, + "closevariable": VARIABLE_TAG_END, + "openbrace": SINGLE_BRACE_START, + "closebrace": SINGLE_BRACE_END, + "opencomment": COMMENT_TAG_START, + "closecomment": COMMENT_TAG_END, + } + + def __init__(self, tagtype): + self.tagtype = tagtype + + def render(self, context): + return self.mapping.get(self.tagtype, "") + + +class URLNode(Node): + child_nodelists = () + + def __init__(self, view_name, args, kwargs, asvar): + self.view_name = view_name + self.args = args + self.kwargs = kwargs + self.asvar = asvar + + def __repr__(self): + return "<%s view_name='%s' args=%s kwargs=%s as=%s>" % ( + self.__class__.__qualname__, + self.view_name, + repr(self.args), + repr(self.kwargs), + repr(self.asvar), + ) + + def render(self, context): + from django.urls import NoReverseMatch, reverse + + args = [arg.resolve(context) for arg in self.args] + kwargs = {k: v.resolve(context) for k, v in self.kwargs.items()} + view_name = self.view_name.resolve(context) + try: + current_app = context.request.current_app + except AttributeError: + try: + current_app = context.request.resolver_match.namespace + except AttributeError: + current_app = None + # Try to look up the URL. If it fails, raise NoReverseMatch unless the + # {% url ... as var %} construct is used, in which case return nothing. + url = "" + try: + url = reverse(view_name, args=args, kwargs=kwargs, current_app=current_app) + except NoReverseMatch: + if self.asvar is None: + raise + + if self.asvar: + context[self.asvar] = url + return "" + else: + if context.autoescape: + url = conditional_escape(url) + return url + + +class VerbatimNode(Node): + def __init__(self, content): + self.content = content + + def render(self, context): + return self.content + + +class WidthRatioNode(Node): + def __init__(self, val_expr, max_expr, max_width, asvar=None): + self.val_expr = val_expr + self.max_expr = max_expr + self.max_width = max_width + self.asvar = asvar + + def render(self, context): + try: + value = self.val_expr.resolve(context) + max_value = self.max_expr.resolve(context) + max_width = int(self.max_width.resolve(context)) + except VariableDoesNotExist: + return "" + except (ValueError, TypeError): + raise TemplateSyntaxError("widthratio final argument must be a number") + try: + value = float(value) + max_value = float(max_value) + ratio = (value / max_value) * max_width + result = str(round(ratio)) + except ZeroDivisionError: + result = "0" + except (ValueError, TypeError, OverflowError): + result = "" + + if self.asvar: + context[self.asvar] = result + return "" + else: + return result + + +class WithNode(Node): + def __init__(self, var, name, nodelist, extra_context=None): + self.nodelist = nodelist + # var and name are legacy attributes, being left in case they are used + # by third-party subclasses of this Node. + self.extra_context = extra_context or {} + if name: + self.extra_context[name] = var + + def __repr__(self): + return "<%s>" % self.__class__.__name__ + + def render(self, context): + values = {key: val.resolve(context) for key, val in self.extra_context.items()} + with context.push(**values): + return self.nodelist.render(context) + + +@register.tag +def autoescape(parser, token): + """ + Force autoescape behavior for this block. + """ + # token.split_contents() isn't useful here because this tag doesn't accept + # variable as arguments. + args = token.contents.split() + if len(args) != 2: + raise TemplateSyntaxError("'autoescape' tag requires exactly one argument.") + arg = args[1] + if arg not in ("on", "off"): + raise TemplateSyntaxError("'autoescape' argument should be 'on' or 'off'") + nodelist = parser.parse(("endautoescape",)) + parser.delete_first_token() + return AutoEscapeControlNode((arg == "on"), nodelist) + + +@register.tag +def comment(parser, token): + """ + Ignore everything between ``{% comment %}`` and ``{% endcomment %}``. + """ + parser.skip_past("endcomment") + return CommentNode() + + +@register.tag +def cycle(parser, token): + """ + Cycle among the given strings each time this tag is encountered. + + Within a loop, cycles among the given strings each time through + the loop:: + + {% for o in some_list %} + <tr class="{% cycle 'row1' 'row2' %}"> + ... + </tr> + {% endfor %} + + Outside of a loop, give the values a unique name the first time you call + it, then use that name each successive time through:: + + <tr class="{% cycle 'row1' 'row2' 'row3' as rowcolors %}">...</tr> + <tr class="{% cycle rowcolors %}">...</tr> + <tr class="{% cycle rowcolors %}">...</tr> + + You can use any number of values, separated by spaces. Commas can also + be used to separate values; if a comma is used, the cycle values are + interpreted as literal strings. + + The optional flag "silent" can be used to prevent the cycle declaration + from returning any value:: + + {% for o in some_list %} + {% cycle 'row1' 'row2' as rowcolors silent %} + <tr class="{{ rowcolors }}">{% include "subtemplate.html " %}</tr> + {% endfor %} + """ + # Note: This returns the exact same node on each {% cycle name %} call; + # that is, the node object returned from {% cycle a b c as name %} and the + # one returned from {% cycle name %} are the exact same object. This + # shouldn't cause problems (heh), but if it does, now you know. + # + # Ugly hack warning: This stuffs the named template dict into parser so + # that names are only unique within each template (as opposed to using + # a global variable, which would make cycle names have to be unique across + # *all* templates. + # + # It keeps the last node in the parser to be able to reset it with + # {% resetcycle %}. + + args = token.split_contents() + + if len(args) < 2: + raise TemplateSyntaxError("'cycle' tag requires at least two arguments") + + if len(args) == 2: + # {% cycle foo %} case. + name = args[1] + if not hasattr(parser, "_named_cycle_nodes"): + raise TemplateSyntaxError( + "No named cycles in template. '%s' is not defined" % name + ) + if name not in parser._named_cycle_nodes: + raise TemplateSyntaxError("Named cycle '%s' does not exist" % name) + return parser._named_cycle_nodes[name] + + as_form = False + + if len(args) > 4: + # {% cycle ... as foo [silent] %} case. + if args[-3] == "as": + if args[-1] != "silent": + raise TemplateSyntaxError( + "Only 'silent' flag is allowed after cycle's name, not '%s'." + % args[-1] + ) + as_form = True + silent = True + args = args[:-1] + elif args[-2] == "as": + as_form = True + silent = False + + if as_form: + name = args[-1] + values = [parser.compile_filter(arg) for arg in args[1:-2]] + node = CycleNode(values, name, silent=silent) + if not hasattr(parser, "_named_cycle_nodes"): + parser._named_cycle_nodes = {} + parser._named_cycle_nodes[name] = node + else: + values = [parser.compile_filter(arg) for arg in args[1:]] + node = CycleNode(values) + parser._last_cycle_node = node + return node + + +@register.tag +def csrf_token(parser, token): + return CsrfTokenNode() + + +@register.tag +def debug(parser, token): + """ + Output a whole load of debugging information, including the current + context and imported modules. + + Sample usage:: + + <pre> + {% debug %} + </pre> + """ + return DebugNode() + + +@register.tag("filter") +def do_filter(parser, token): + """ + Filter the contents of the block through variable filters. + + Filters can also be piped through each other, and they can have + arguments -- just like in variable syntax. + + Sample usage:: + + {% filter force_escape|lower %} + This text will be HTML-escaped, and will appear in lowercase. + {% endfilter %} + + Note that the ``escape`` and ``safe`` filters are not acceptable arguments. + Instead, use the ``autoescape`` tag to manage autoescaping for blocks of + template code. + """ + # token.split_contents() isn't useful here because this tag doesn't accept + # variable as arguments. + _, rest = token.contents.split(None, 1) + filter_expr = parser.compile_filter("var|%s" % (rest)) + for func, unused in filter_expr.filters: + filter_name = getattr(func, "_filter_name", None) + if filter_name in ("escape", "safe"): + raise TemplateSyntaxError( + '"filter %s" is not permitted. Use the "autoescape" tag instead.' + % filter_name + ) + nodelist = parser.parse(("endfilter",)) + parser.delete_first_token() + return FilterNode(filter_expr, nodelist) + + +@register.tag +def firstof(parser, token): + """ + Output the first variable passed that is not False. + + Output nothing if all the passed variables are False. + + Sample usage:: + + {% firstof var1 var2 var3 as myvar %} + + This is equivalent to:: + + {% if var1 %} + {{ var1 }} + {% elif var2 %} + {{ var2 }} + {% elif var3 %} + {{ var3 }} + {% endif %} + + but much cleaner! + + You can also use a literal string as a fallback value in case all + passed variables are False:: + + {% firstof var1 var2 var3 "fallback value" %} + + If you want to disable auto-escaping of variables you can use:: + + {% autoescape off %} + {% firstof var1 var2 var3 "<strong>fallback value</strong>" %} + {% autoescape %} + + Or if only some variables should be escaped, you can use:: + + {% firstof var1 var2|safe var3 "<strong>fallback value</strong>"|safe %} + """ + bits = token.split_contents()[1:] + asvar = None + if not bits: + raise TemplateSyntaxError("'firstof' statement requires at least one argument") + + if len(bits) >= 2 and bits[-2] == "as": + asvar = bits[-1] + bits = bits[:-2] + return FirstOfNode([parser.compile_filter(bit) for bit in bits], asvar) + + +@register.tag("for") +def do_for(parser, token): + """ + Loop over each item in an array. + + For example, to display a list of athletes given ``athlete_list``:: + + <ul> + {% for athlete in athlete_list %} + <li>{{ athlete.name }}</li> + {% endfor %} + </ul> + + You can loop over a list in reverse by using + ``{% for obj in list reversed %}``. + + You can also unpack multiple values from a two-dimensional array:: + + {% for key,value in dict.items %} + {{ key }}: {{ value }} + {% endfor %} + + The ``for`` tag can take an optional ``{% empty %}`` clause that will + be displayed if the given array is empty or could not be found:: + + <ul> + {% for athlete in athlete_list %} + <li>{{ athlete.name }}</li> + {% empty %} + <li>Sorry, no athletes in this list.</li> + {% endfor %} + <ul> + + The above is equivalent to -- but shorter, cleaner, and possibly faster + than -- the following:: + + <ul> + {% if athlete_list %} + {% for athlete in athlete_list %} + <li>{{ athlete.name }}</li> + {% endfor %} + {% else %} + <li>Sorry, no athletes in this list.</li> + {% endif %} + </ul> + + The for loop sets a number of variables available within the loop: + + ========================== ================================================ + Variable Description + ========================== ================================================ + ``forloop.counter`` The current iteration of the loop (1-indexed) + ``forloop.counter0`` The current iteration of the loop (0-indexed) + ``forloop.revcounter`` The number of iterations from the end of the + loop (1-indexed) + ``forloop.revcounter0`` The number of iterations from the end of the + loop (0-indexed) + ``forloop.first`` True if this is the first time through the loop + ``forloop.last`` True if this is the last time through the loop + ``forloop.parentloop`` For nested loops, this is the loop "above" the + current one + ========================== ================================================ + """ + bits = token.split_contents() + if len(bits) < 4: + raise TemplateSyntaxError( + "'for' statements should have at least four words: %s" % token.contents + ) + + is_reversed = bits[-1] == "reversed" + in_index = -3 if is_reversed else -2 + if bits[in_index] != "in": + raise TemplateSyntaxError( + "'for' statements should use the format" + " 'for x in y': %s" % token.contents + ) + + invalid_chars = frozenset((" ", '"', "'", FILTER_SEPARATOR)) + loopvars = re.split(r" *, *", " ".join(bits[1:in_index])) + for var in loopvars: + if not var or not invalid_chars.isdisjoint(var): + raise TemplateSyntaxError( + "'for' tag received an invalid argument: %s" % token.contents + ) + + sequence = parser.compile_filter(bits[in_index + 1]) + nodelist_loop = parser.parse( + ( + "empty", + "endfor", + ) + ) + token = parser.next_token() + if token.contents == "empty": + nodelist_empty = parser.parse(("endfor",)) + parser.delete_first_token() + else: + nodelist_empty = None + return ForNode(loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty) + + +class TemplateLiteral(Literal): + def __init__(self, value, text): + self.value = value + self.text = text # for better error messages + + def display(self): + return self.text + + def eval(self, context): + return self.value.resolve(context, ignore_failures=True) + + +class TemplateIfParser(IfParser): + error_class = TemplateSyntaxError + + def __init__(self, parser, *args, **kwargs): + self.template_parser = parser + super().__init__(*args, **kwargs) + + def create_var(self, value): + return TemplateLiteral(self.template_parser.compile_filter(value), value) + + +@register.tag("if") +def do_if(parser, token): + """ + Evaluate a variable, and if that variable is "true" (i.e., exists, is not + empty, and is not a false boolean value), output the contents of the block: + + :: + + {% if athlete_list %} + Number of athletes: {{ athlete_list|count }} + {% elif athlete_in_locker_room_list %} + Athletes should be out of the locker room soon! + {% else %} + No athletes. + {% endif %} + + In the above, if ``athlete_list`` is not empty, the number of athletes will + be displayed by the ``{{ athlete_list|count }}`` variable. + + The ``if`` tag may take one or several `` {% elif %}`` clauses, as well as + an ``{% else %}`` clause that will be displayed if all previous conditions + fail. These clauses are optional. + + ``if`` tags may use ``or``, ``and`` or ``not`` to test a number of + variables or to negate a given variable:: + + {% if not athlete_list %} + There are no athletes. + {% endif %} + + {% if athlete_list or coach_list %} + There are some athletes or some coaches. + {% endif %} + + {% if athlete_list and coach_list %} + Both athletes and coaches are available. + {% endif %} + + {% if not athlete_list or coach_list %} + There are no athletes, or there are some coaches. + {% endif %} + + {% if athlete_list and not coach_list %} + There are some athletes and absolutely no coaches. + {% endif %} + + Comparison operators are also available, and the use of filters is also + allowed, for example:: + + {% if articles|length >= 5 %}...{% endif %} + + Arguments and operators _must_ have a space between them, so + ``{% if 1>2 %}`` is not a valid if tag. + + All supported operators are: ``or``, ``and``, ``in``, ``not in`` + ``==``, ``!=``, ``>``, ``>=``, ``<`` and ``<=``. + + Operator precedence follows Python. + """ + # {% if ... %} + bits = token.split_contents()[1:] + condition = TemplateIfParser(parser, bits).parse() + nodelist = parser.parse(("elif", "else", "endif")) + conditions_nodelists = [(condition, nodelist)] + token = parser.next_token() + + # {% elif ... %} (repeatable) + while token.contents.startswith("elif"): + bits = token.split_contents()[1:] + condition = TemplateIfParser(parser, bits).parse() + nodelist = parser.parse(("elif", "else", "endif")) + conditions_nodelists.append((condition, nodelist)) + token = parser.next_token() + + # {% else %} (optional) + if token.contents == "else": + nodelist = parser.parse(("endif",)) + conditions_nodelists.append((None, nodelist)) + token = parser.next_token() + + # {% endif %} + if token.contents != "endif": + raise TemplateSyntaxError( + 'Malformed template tag at line {}: "{}"'.format( + token.lineno, token.contents + ) + ) + + return IfNode(conditions_nodelists) + + +@register.tag +def ifchanged(parser, token): + """ + Check if a value has changed from the last iteration of a loop. + + The ``{% ifchanged %}`` block tag is used within a loop. It has two + possible uses. + + 1. Check its own rendered contents against its previous state and only + displays the content if it has changed. For example, this displays a + list of days, only displaying the month if it changes:: + + <h1>Archive for {{ year }}</h1> + + {% for date in days %} + {% ifchanged %}<h3>{{ date|date:"F" }}</h3>{% endifchanged %} + <a href="{{ date|date:"M/d"|lower }}/">{{ date|date:"j" }}</a> + {% endfor %} + + 2. If given one or more variables, check whether any variable has changed. + For example, the following shows the date every time it changes, while + showing the hour if either the hour or the date has changed:: + + {% for date in days %} + {% ifchanged date.date %} {{ date.date }} {% endifchanged %} + {% ifchanged date.hour date.date %} + {{ date.hour }} + {% endifchanged %} + {% endfor %} + """ + bits = token.split_contents() + nodelist_true = parser.parse(("else", "endifchanged")) + token = parser.next_token() + if token.contents == "else": + nodelist_false = parser.parse(("endifchanged",)) + parser.delete_first_token() + else: + nodelist_false = NodeList() + values = [parser.compile_filter(bit) for bit in bits[1:]] + return IfChangedNode(nodelist_true, nodelist_false, *values) + + +def find_library(parser, name): + try: + return parser.libraries[name] + except KeyError: + raise TemplateSyntaxError( + "'%s' is not a registered tag library. Must be one of:\n%s" + % ( + name, + "\n".join(sorted(parser.libraries)), + ), + ) + + +def load_from_library(library, label, names): + """ + Return a subset of tags and filters from a library. + """ + subset = Library() + for name in names: + found = False + if name in library.tags: + found = True + subset.tags[name] = library.tags[name] + if name in library.filters: + found = True + subset.filters[name] = library.filters[name] + if found is False: + raise TemplateSyntaxError( + "'%s' is not a valid tag or filter in tag library '%s'" + % ( + name, + label, + ), + ) + return subset + + +@register.tag +def load(parser, token): + """ + Load a custom template tag library into the parser. + + For example, to load the template tags in + ``django/templatetags/news/photos.py``:: + + {% load news.photos %} + + Can also be used to load an individual tag/filter from + a library:: + + {% load byline from news %} + """ + # token.split_contents() isn't useful here because this tag doesn't accept + # variable as arguments. + bits = token.contents.split() + if len(bits) >= 4 and bits[-2] == "from": + # from syntax is used; load individual tags from the library + name = bits[-1] + lib = find_library(parser, name) + subset = load_from_library(lib, name, bits[1:-2]) + parser.add_library(subset) + else: + # one or more libraries are specified; load and add them to the parser + for name in bits[1:]: + lib = find_library(parser, name) + parser.add_library(lib) + return LoadNode() + + +@register.tag +def lorem(parser, token): + """ + Create random Latin text useful for providing test data in templates. + + Usage format:: + + {% lorem [count] [method] [random] %} + + ``count`` is a number (or variable) containing the number of paragraphs or + words to generate (default is 1). + + ``method`` is either ``w`` for words, ``p`` for HTML paragraphs, ``b`` for + plain-text paragraph blocks (default is ``b``). + + ``random`` is the word ``random``, which if given, does not use the common + paragraph (starting "Lorem ipsum dolor sit amet, consectetuer..."). + + Examples: + + * ``{% lorem %}`` outputs the common "lorem ipsum" paragraph + * ``{% lorem 3 p %}`` outputs the common "lorem ipsum" paragraph + and two random paragraphs each wrapped in HTML ``<p>`` tags + * ``{% lorem 2 w random %}`` outputs two random latin words + """ + bits = list(token.split_contents()) + tagname = bits[0] + # Random bit + common = bits[-1] != "random" + if not common: + bits.pop() + # Method bit + if bits[-1] in ("w", "p", "b"): + method = bits.pop() + else: + method = "b" + # Count bit + if len(bits) > 1: + count = bits.pop() + else: + count = "1" + count = parser.compile_filter(count) + if len(bits) != 1: + raise TemplateSyntaxError("Incorrect format for %r tag" % tagname) + return LoremNode(count, method, common) + + +@register.tag +def now(parser, token): + """ + Display the date, formatted according to the given string. + + Use the same format as PHP's ``date()`` function; see https://php.net/date + for all the possible values. + + Sample usage:: + + It is {% now "jS F Y H:i" %} + """ + bits = token.split_contents() + asvar = None + if len(bits) == 4 and bits[-2] == "as": + asvar = bits[-1] + bits = bits[:-2] + if len(bits) != 2: + raise TemplateSyntaxError("'now' statement takes one argument") + format_string = bits[1][1:-1] + return NowNode(format_string, asvar) + + +@register.tag +def regroup(parser, token): + """ + Regroup a list of alike objects by a common attribute. + + This complex tag is best illustrated by use of an example: say that + ``musicians`` is a list of ``Musician`` objects that have ``name`` and + ``instrument`` attributes, and you'd like to display a list that + looks like: + + * Guitar: + * Django Reinhardt + * Emily Remler + * Piano: + * Lovie Austin + * Bud Powell + * Trumpet: + * Duke Ellington + + The following snippet of template code would accomplish this dubious task:: + + {% regroup musicians by instrument as grouped %} + <ul> + {% for group in grouped %} + <li>{{ group.grouper }} + <ul> + {% for musician in group.list %} + <li>{{ musician.name }}</li> + {% endfor %} + </ul> + {% endfor %} + </ul> + + As you can see, ``{% regroup %}`` populates a variable with a list of + objects with ``grouper`` and ``list`` attributes. ``grouper`` contains the + item that was grouped by; ``list`` contains the list of objects that share + that ``grouper``. In this case, ``grouper`` would be ``Guitar``, ``Piano`` + and ``Trumpet``, and ``list`` is the list of musicians who play this + instrument. + + Note that ``{% regroup %}`` does not work when the list to be grouped is not + sorted by the key you are grouping by! This means that if your list of + musicians was not sorted by instrument, you'd need to make sure it is sorted + before using it, i.e.:: + + {% regroup musicians|dictsort:"instrument" by instrument as grouped %} + """ + bits = token.split_contents() + if len(bits) != 6: + raise TemplateSyntaxError("'regroup' tag takes five arguments") + target = parser.compile_filter(bits[1]) + if bits[2] != "by": + raise TemplateSyntaxError("second argument to 'regroup' tag must be 'by'") + if bits[4] != "as": + raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must be 'as'") + var_name = bits[5] + # RegroupNode will take each item in 'target', put it in the context under + # 'var_name', evaluate 'var_name'.'expression' in the current context, and + # group by the resulting value. After all items are processed, it will + # save the final result in the context under 'var_name', thus clearing the + # temporary values. This hack is necessary because the template engine + # doesn't provide a context-aware equivalent of Python's getattr. + expression = parser.compile_filter( + var_name + VARIABLE_ATTRIBUTE_SEPARATOR + bits[3] + ) + return RegroupNode(target, expression, var_name) + + +@register.tag +def resetcycle(parser, token): + """ + Reset a cycle tag. + + If an argument is given, reset the last rendered cycle tag whose name + matches the argument, else reset the last rendered cycle tag (named or + unnamed). + """ + args = token.split_contents() + + if len(args) > 2: + raise TemplateSyntaxError("%r tag accepts at most one argument." % args[0]) + + if len(args) == 2: + name = args[1] + try: + return ResetCycleNode(parser._named_cycle_nodes[name]) + except (AttributeError, KeyError): + raise TemplateSyntaxError("Named cycle '%s' does not exist." % name) + try: + return ResetCycleNode(parser._last_cycle_node) + except AttributeError: + raise TemplateSyntaxError("No cycles in template.") + + +@register.tag +def spaceless(parser, token): + """ + Remove whitespace between HTML tags, including tab and newline characters. + + Example usage:: + + {% spaceless %} + <p> + <a href="foo/">Foo</a> + </p> + {% endspaceless %} + + This example returns this HTML:: + + <p><a href="foo/">Foo</a></p> + + Only space between *tags* is normalized -- not space between tags and text. + In this example, the space around ``Hello`` isn't stripped:: + + {% spaceless %} + <strong> + Hello + </strong> + {% endspaceless %} + """ + nodelist = parser.parse(("endspaceless",)) + parser.delete_first_token() + return SpacelessNode(nodelist) + + +@register.tag +def templatetag(parser, token): + """ + Output one of the bits used to compose template tags. + + Since the template system has no concept of "escaping", to display one of + the bits used in template tags, you must use the ``{% templatetag %}`` tag. + + The argument tells which template bit to output: + + ================== ======= + Argument Outputs + ================== ======= + ``openblock`` ``{%`` + ``closeblock`` ``%}`` + ``openvariable`` ``{{`` + ``closevariable`` ``}}`` + ``openbrace`` ``{`` + ``closebrace`` ``}`` + ``opencomment`` ``{#`` + ``closecomment`` ``#}`` + ================== ======= + """ + # token.split_contents() isn't useful here because this tag doesn't accept + # variable as arguments. + bits = token.contents.split() + if len(bits) != 2: + raise TemplateSyntaxError("'templatetag' statement takes one argument") + tag = bits[1] + if tag not in TemplateTagNode.mapping: + raise TemplateSyntaxError( + "Invalid templatetag argument: '%s'." + " Must be one of: %s" % (tag, list(TemplateTagNode.mapping)) + ) + return TemplateTagNode(tag) + + +@register.tag +def url(parser, token): + r""" + Return an absolute URL matching the given view with its parameters. + + This is a way to define links that aren't tied to a particular URL + configuration:: + + {% url "url_name" arg1 arg2 %} + + or + + {% url "url_name" name1=value1 name2=value2 %} + + The first argument is a URL pattern name. Other arguments are + space-separated values that will be filled in place of positional and + keyword arguments in the URL. Don't mix positional and keyword arguments. + All arguments for the URL must be present. + + For example, if you have a view ``app_name.views.client_details`` taking + the client's id and the corresponding line in a URLconf looks like this:: + + path('client/<int:id>/', views.client_details, name='client-detail-view') + + and this app's URLconf is included into the project's URLconf under some + path:: + + path('clients/', include('app_name.urls')) + + then in a template you can create a link for a certain client like this:: + + {% url "client-detail-view" client.id %} + + The URL will look like ``/clients/client/123/``. + + The first argument may also be the name of a template variable that will be + evaluated to obtain the view name or the URL name, e.g.:: + + {% with url_name="client-detail-view" %} + {% url url_name client.id %} + {% endwith %} + """ + bits = token.split_contents() + if len(bits) < 2: + raise TemplateSyntaxError( + "'%s' takes at least one argument, a URL pattern name." % bits[0] + ) + viewname = parser.compile_filter(bits[1]) + args = [] + kwargs = {} + asvar = None + bits = bits[2:] + if len(bits) >= 2 and bits[-2] == "as": + asvar = bits[-1] + bits = bits[:-2] + + for bit in bits: + match = kwarg_re.match(bit) + if not match: + raise TemplateSyntaxError("Malformed arguments to url tag") + name, value = match.groups() + if name: + kwargs[name] = parser.compile_filter(value) + else: + args.append(parser.compile_filter(value)) + + return URLNode(viewname, args, kwargs, asvar) + + +@register.tag +def verbatim(parser, token): + """ + Stop the template engine from rendering the contents of this block tag. + + Usage:: + + {% verbatim %} + {% don't process this %} + {% endverbatim %} + + You can also designate a specific closing tag block (allowing the + unrendered use of ``{% endverbatim %}``):: + + {% verbatim myblock %} + ... + {% endverbatim myblock %} + """ + nodelist = parser.parse(("endverbatim",)) + parser.delete_first_token() + return VerbatimNode(nodelist.render(Context())) + + +@register.tag +def widthratio(parser, token): + """ + For creating bar charts and such. Calculate the ratio of a given value to a + maximum value, and then apply that ratio to a constant. + + For example:: + + <img src="bar.png" alt="Bar" + height="10" width="{% widthratio this_value max_value max_width %}"> + + If ``this_value`` is 175, ``max_value`` is 200, and ``max_width`` is 100, + the image in the above example will be 88 pixels wide + (because 175/200 = .875; .875 * 100 = 87.5 which is rounded up to 88). + + In some cases you might want to capture the result of widthratio in a + variable. It can be useful for instance in a blocktranslate like this:: + + {% widthratio this_value max_value max_width as width %} + {% blocktranslate %}The width is: {{ width }}{% endblocktranslate %} + """ + bits = token.split_contents() + if len(bits) == 4: + tag, this_value_expr, max_value_expr, max_width = bits + asvar = None + elif len(bits) == 6: + tag, this_value_expr, max_value_expr, max_width, as_, asvar = bits + if as_ != "as": + raise TemplateSyntaxError( + "Invalid syntax in widthratio tag. Expecting 'as' keyword" + ) + else: + raise TemplateSyntaxError("widthratio takes at least three arguments") + + return WidthRatioNode( + parser.compile_filter(this_value_expr), + parser.compile_filter(max_value_expr), + parser.compile_filter(max_width), + asvar=asvar, + ) + + +@register.tag("with") +def do_with(parser, token): + """ + Add one or more values to the context (inside of this block) for caching + and easy access. + + For example:: + + {% with total=person.some_sql_method %} + {{ total }} object{{ total|pluralize }} + {% endwith %} + + Multiple values can be added to the context:: + + {% with foo=1 bar=2 %} + ... + {% endwith %} + + The legacy format of ``{% with person.some_sql_method as total %}`` is + still accepted. + """ + bits = token.split_contents() + remaining_bits = bits[1:] + extra_context = token_kwargs(remaining_bits, parser, support_legacy=True) + if not extra_context: + raise TemplateSyntaxError( + "%r expected at least one variable assignment" % bits[0] + ) + if remaining_bits: + raise TemplateSyntaxError( + "%r received an invalid token: %r" % (bits[0], remaining_bits[0]) + ) + nodelist = parser.parse(("endwith",)) + parser.delete_first_token() + return WithNode(None, None, nodelist, extra_context=extra_context) diff --git a/virt/lib/python3.9/site-packages/django/test/client 3.py b/virt/lib/python3.9/site-packages/django/test/client 3.py new file mode 100644 index 00000000..508b24ca --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/test/client 3.py @@ -0,0 +1,1269 @@ +import json +import mimetypes +import os +import sys +from copy import copy +from functools import partial +from http import HTTPStatus +from importlib import import_module +from io import BytesIO, IOBase +from urllib.parse import unquote_to_bytes, urljoin, urlparse, urlsplit + +from asgiref.sync import sync_to_async + +from django.conf import settings +from django.core.handlers.asgi import ASGIRequest +from django.core.handlers.base import BaseHandler +from django.core.handlers.wsgi import LimitedStream, WSGIRequest +from django.core.serializers.json import DjangoJSONEncoder +from django.core.signals import got_request_exception, request_finished, request_started +from django.db import close_old_connections +from django.http import HttpHeaders, HttpRequest, QueryDict, SimpleCookie +from django.test import signals +from django.test.utils import ContextList +from django.urls import resolve +from django.utils.encoding import force_bytes +from django.utils.functional import SimpleLazyObject +from django.utils.http import urlencode +from django.utils.itercompat import is_iterable +from django.utils.regex_helper import _lazy_re_compile + +__all__ = ( + "AsyncClient", + "AsyncRequestFactory", + "Client", + "RedirectCycleError", + "RequestFactory", + "encode_file", + "encode_multipart", +) + + +BOUNDARY = "BoUnDaRyStRiNg" +MULTIPART_CONTENT = "multipart/form-data; boundary=%s" % BOUNDARY +CONTENT_TYPE_RE = _lazy_re_compile(r".*; charset=([\w-]+);?") +# Structured suffix spec: https://tools.ietf.org/html/rfc6838#section-4.2.8 +JSON_CONTENT_TYPE_RE = _lazy_re_compile(r"^application\/(.+\+)?json") + + +class RedirectCycleError(Exception): + """The test client has been asked to follow a redirect loop.""" + + def __init__(self, message, last_response): + super().__init__(message) + self.last_response = last_response + self.redirect_chain = last_response.redirect_chain + + +class FakePayload(IOBase): + """ + A wrapper around BytesIO that restricts what can be read since data from + the network can't be sought and cannot be read outside of its content + length. This makes sure that views can't do anything under the test client + that wouldn't work in real life. + """ + + def __init__(self, initial_bytes=None): + self.__content = BytesIO() + self.__len = 0 + self.read_started = False + if initial_bytes is not None: + self.write(initial_bytes) + + def __len__(self): + return self.__len + + def read(self, size=-1, /): + if not self.read_started: + self.__content.seek(0) + self.read_started = True + if size == -1 or size is None: + size = self.__len + assert ( + self.__len >= size + ), "Cannot read more than the available bytes from the HTTP incoming data." + content = self.__content.read(size) + self.__len -= len(content) + return content + + def readline(self, size=-1, /): + if not self.read_started: + self.__content.seek(0) + self.read_started = True + if size == -1 or size is None: + size = self.__len + assert ( + self.__len >= size + ), "Cannot read more than the available bytes from the HTTP incoming data." + content = self.__content.readline(size) + self.__len -= len(content) + return content + + def write(self, b, /): + if self.read_started: + raise ValueError("Unable to write a payload after it's been read") + content = force_bytes(b) + self.__content.write(content) + self.__len += len(content) + + +def closing_iterator_wrapper(iterable, close): + try: + yield from iterable + finally: + request_finished.disconnect(close_old_connections) + close() # will fire request_finished + request_finished.connect(close_old_connections) + + +async def aclosing_iterator_wrapper(iterable, close): + try: + async for chunk in iterable: + yield chunk + finally: + request_finished.disconnect(close_old_connections) + close() # will fire request_finished + request_finished.connect(close_old_connections) + + +def conditional_content_removal(request, response): + """ + Simulate the behavior of most web servers by removing the content of + responses for HEAD requests, 1xx, 204, and 304 responses. Ensure + compliance with RFC 9112 Section 6.3. + """ + if 100 <= response.status_code < 200 or response.status_code in (204, 304): + if response.streaming: + response.streaming_content = [] + else: + response.content = b"" + if request.method == "HEAD": + if response.streaming: + response.streaming_content = [] + else: + response.content = b"" + return response + + +class ClientHandler(BaseHandler): + """ + An HTTP Handler that can be used for testing purposes. Use the WSGI + interface to compose requests, but return the raw HttpResponse object with + the originating WSGIRequest attached to its ``wsgi_request`` attribute. + """ + + def __init__(self, enforce_csrf_checks=True, *args, **kwargs): + self.enforce_csrf_checks = enforce_csrf_checks + super().__init__(*args, **kwargs) + + def __call__(self, environ): + # Set up middleware if needed. We couldn't do this earlier, because + # settings weren't available. + if self._middleware_chain is None: + self.load_middleware() + + request_started.disconnect(close_old_connections) + request_started.send(sender=self.__class__, environ=environ) + request_started.connect(close_old_connections) + request = WSGIRequest(environ) + # sneaky little hack so that we can easily get round + # CsrfViewMiddleware. This makes life easier, and is probably + # required for backwards compatibility with external tests against + # admin views. + request._dont_enforce_csrf_checks = not self.enforce_csrf_checks + + # Request goes through middleware. + response = self.get_response(request) + + # Simulate behaviors of most web servers. + conditional_content_removal(request, response) + + # Attach the originating request to the response so that it could be + # later retrieved. + response.wsgi_request = request + + # Emulate a WSGI server by calling the close method on completion. + if response.streaming: + if response.is_async: + response.streaming_content = aclosing_iterator_wrapper( + response.streaming_content, response.close + ) + else: + response.streaming_content = closing_iterator_wrapper( + response.streaming_content, response.close + ) + else: + request_finished.disconnect(close_old_connections) + response.close() # will fire request_finished + request_finished.connect(close_old_connections) + + return response + + +class AsyncClientHandler(BaseHandler): + """An async version of ClientHandler.""" + + def __init__(self, enforce_csrf_checks=True, *args, **kwargs): + self.enforce_csrf_checks = enforce_csrf_checks + super().__init__(*args, **kwargs) + + async def __call__(self, scope): + # Set up middleware if needed. We couldn't do this earlier, because + # settings weren't available. + if self._middleware_chain is None: + self.load_middleware(is_async=True) + # Extract body file from the scope, if provided. + if "_body_file" in scope: + body_file = scope.pop("_body_file") + else: + body_file = FakePayload("") + + request_started.disconnect(close_old_connections) + await sync_to_async(request_started.send, thread_sensitive=False)( + sender=self.__class__, scope=scope + ) + request_started.connect(close_old_connections) + # Wrap FakePayload body_file to allow large read() in test environment. + request = ASGIRequest(scope, LimitedStream(body_file, len(body_file))) + # Sneaky little hack so that we can easily get round + # CsrfViewMiddleware. This makes life easier, and is probably required + # for backwards compatibility with external tests against admin views. + request._dont_enforce_csrf_checks = not self.enforce_csrf_checks + # Request goes through middleware. + response = await self.get_response_async(request) + # Simulate behaviors of most web servers. + conditional_content_removal(request, response) + # Attach the originating ASGI request to the response so that it could + # be later retrieved. + response.asgi_request = request + # Emulate a server by calling the close method on completion. + if response.streaming: + if response.is_async: + response.streaming_content = aclosing_iterator_wrapper( + response.streaming_content, response.close + ) + else: + response.streaming_content = closing_iterator_wrapper( + response.streaming_content, response.close + ) + else: + request_finished.disconnect(close_old_connections) + # Will fire request_finished. + await sync_to_async(response.close, thread_sensitive=False)() + request_finished.connect(close_old_connections) + return response + + +def store_rendered_templates(store, signal, sender, template, context, **kwargs): + """ + Store templates and contexts that are rendered. + + The context is copied so that it is an accurate representation at the time + of rendering. + """ + store.setdefault("templates", []).append(template) + if "context" not in store: + store["context"] = ContextList() + store["context"].append(copy(context)) + + +def encode_multipart(boundary, data): + """ + Encode multipart POST data from a dictionary of form values. + + The key will be used as the form data name; the value will be transmitted + as content. If the value is a file, the contents of the file will be sent + as an application/octet-stream; otherwise, str(value) will be sent. + """ + lines = [] + + def to_bytes(s): + return force_bytes(s, settings.DEFAULT_CHARSET) + + # Not by any means perfect, but good enough for our purposes. + def is_file(thing): + return hasattr(thing, "read") and callable(thing.read) + + # Each bit of the multipart form data could be either a form value or a + # file, or a *list* of form values and/or files. Remember that HTTP field + # names can be duplicated! + for key, value in data.items(): + if value is None: + raise TypeError( + "Cannot encode None for key '%s' as POST data. Did you mean " + "to pass an empty string or omit the value?" % key + ) + elif is_file(value): + lines.extend(encode_file(boundary, key, value)) + elif not isinstance(value, str) and is_iterable(value): + for item in value: + if is_file(item): + lines.extend(encode_file(boundary, key, item)) + else: + lines.extend( + to_bytes(val) + for val in [ + "--%s" % boundary, + 'Content-Disposition: form-data; name="%s"' % key, + "", + item, + ] + ) + else: + lines.extend( + to_bytes(val) + for val in [ + "--%s" % boundary, + 'Content-Disposition: form-data; name="%s"' % key, + "", + value, + ] + ) + + lines.extend( + [ + to_bytes("--%s--" % boundary), + b"", + ] + ) + return b"\r\n".join(lines) + + +def encode_file(boundary, key, file): + def to_bytes(s): + return force_bytes(s, settings.DEFAULT_CHARSET) + + # file.name might not be a string. For example, it's an int for + # tempfile.TemporaryFile(). + file_has_string_name = hasattr(file, "name") and isinstance(file.name, str) + filename = os.path.basename(file.name) if file_has_string_name else "" + + if hasattr(file, "content_type"): + content_type = file.content_type + elif filename: + content_type = mimetypes.guess_type(filename)[0] + else: + content_type = None + + if content_type is None: + content_type = "application/octet-stream" + filename = filename or key + return [ + to_bytes("--%s" % boundary), + to_bytes( + 'Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename) + ), + to_bytes("Content-Type: %s" % content_type), + b"", + to_bytes(file.read()), + ] + + +class RequestFactory: + """ + Class that lets you create mock Request objects for use in testing. + + Usage: + + rf = RequestFactory() + get_request = rf.get('/hello/') + post_request = rf.post('/submit/', {'foo': 'bar'}) + + Once you have a request object you can pass it to any view function, + just as if that view had been hooked up using a URLconf. + """ + + def __init__(self, *, json_encoder=DjangoJSONEncoder, headers=None, **defaults): + self.json_encoder = json_encoder + self.defaults = defaults + self.cookies = SimpleCookie() + self.errors = BytesIO() + if headers: + self.defaults.update(HttpHeaders.to_wsgi_names(headers)) + + def _base_environ(self, **request): + """ + The base environment for a request. + """ + # This is a minimal valid WSGI environ dictionary, plus: + # - HTTP_COOKIE: for cookie support, + # - REMOTE_ADDR: often useful, see #8551. + # See https://www.python.org/dev/peps/pep-3333/#environ-variables + return { + "HTTP_COOKIE": "; ".join( + sorted( + "%s=%s" % (morsel.key, morsel.coded_value) + for morsel in self.cookies.values() + ) + ), + "PATH_INFO": "/", + "REMOTE_ADDR": "127.0.0.1", + "REQUEST_METHOD": "GET", + "SCRIPT_NAME": "", + "SERVER_NAME": "testserver", + "SERVER_PORT": "80", + "SERVER_PROTOCOL": "HTTP/1.1", + "wsgi.version": (1, 0), + "wsgi.url_scheme": "http", + "wsgi.input": FakePayload(b""), + "wsgi.errors": self.errors, + "wsgi.multiprocess": True, + "wsgi.multithread": False, + "wsgi.run_once": False, + **self.defaults, + **request, + } + + def request(self, **request): + "Construct a generic request object." + return WSGIRequest(self._base_environ(**request)) + + def _encode_data(self, data, content_type): + if content_type is MULTIPART_CONTENT: + return encode_multipart(BOUNDARY, data) + else: + # Encode the content so that the byte representation is correct. + match = CONTENT_TYPE_RE.match(content_type) + if match: + charset = match[1] + else: + charset = settings.DEFAULT_CHARSET + return force_bytes(data, encoding=charset) + + def _encode_json(self, data, content_type): + """ + Return encoded JSON if data is a dict, list, or tuple and content_type + is application/json. + """ + should_encode = JSON_CONTENT_TYPE_RE.match(content_type) and isinstance( + data, (dict, list, tuple) + ) + return json.dumps(data, cls=self.json_encoder) if should_encode else data + + def _get_path(self, parsed): + path = parsed.path + # If there are parameters, add them + if parsed.params: + path += ";" + parsed.params + path = unquote_to_bytes(path) + # Replace the behavior where non-ASCII values in the WSGI environ are + # arbitrarily decoded with ISO-8859-1. + # Refs comment in `get_bytes_from_wsgi()`. + return path.decode("iso-8859-1") + + def get(self, path, data=None, secure=False, *, headers=None, **extra): + """Construct a GET request.""" + data = {} if data is None else data + return self.generic( + "GET", + path, + secure=secure, + headers=headers, + **{ + "QUERY_STRING": urlencode(data, doseq=True), + **extra, + }, + ) + + def post( + self, + path, + data=None, + content_type=MULTIPART_CONTENT, + secure=False, + *, + headers=None, + **extra, + ): + """Construct a POST request.""" + data = self._encode_json({} if data is None else data, content_type) + post_data = self._encode_data(data, content_type) + + return self.generic( + "POST", + path, + post_data, + content_type, + secure=secure, + headers=headers, + **extra, + ) + + def head(self, path, data=None, secure=False, *, headers=None, **extra): + """Construct a HEAD request.""" + data = {} if data is None else data + return self.generic( + "HEAD", + path, + secure=secure, + headers=headers, + **{ + "QUERY_STRING": urlencode(data, doseq=True), + **extra, + }, + ) + + def trace(self, path, secure=False, *, headers=None, **extra): + """Construct a TRACE request.""" + return self.generic("TRACE", path, secure=secure, headers=headers, **extra) + + def options( + self, + path, + data="", + content_type="application/octet-stream", + secure=False, + *, + headers=None, + **extra, + ): + "Construct an OPTIONS request." + return self.generic( + "OPTIONS", path, data, content_type, secure=secure, headers=headers, **extra + ) + + def put( + self, + path, + data="", + content_type="application/octet-stream", + secure=False, + *, + headers=None, + **extra, + ): + """Construct a PUT request.""" + data = self._encode_json(data, content_type) + return self.generic( + "PUT", path, data, content_type, secure=secure, headers=headers, **extra + ) + + def patch( + self, + path, + data="", + content_type="application/octet-stream", + secure=False, + *, + headers=None, + **extra, + ): + """Construct a PATCH request.""" + data = self._encode_json(data, content_type) + return self.generic( + "PATCH", path, data, content_type, secure=secure, headers=headers, **extra + ) + + def delete( + self, + path, + data="", + content_type="application/octet-stream", + secure=False, + *, + headers=None, + **extra, + ): + """Construct a DELETE request.""" + data = self._encode_json(data, content_type) + return self.generic( + "DELETE", path, data, content_type, secure=secure, headers=headers, **extra + ) + + def generic( + self, + method, + path, + data="", + content_type="application/octet-stream", + secure=False, + *, + headers=None, + **extra, + ): + """Construct an arbitrary HTTP request.""" + parsed = urlparse(str(path)) # path can be lazy + data = force_bytes(data, settings.DEFAULT_CHARSET) + r = { + "PATH_INFO": self._get_path(parsed), + "REQUEST_METHOD": method, + "SERVER_PORT": "443" if secure else "80", + "wsgi.url_scheme": "https" if secure else "http", + } + if data: + r.update( + { + "CONTENT_LENGTH": str(len(data)), + "CONTENT_TYPE": content_type, + "wsgi.input": FakePayload(data), + } + ) + if headers: + extra.update(HttpHeaders.to_wsgi_names(headers)) + r.update(extra) + # If QUERY_STRING is absent or empty, we want to extract it from the URL. + if not r.get("QUERY_STRING"): + # WSGI requires latin-1 encoded strings. See get_path_info(). + query_string = parsed[4].encode().decode("iso-8859-1") + r["QUERY_STRING"] = query_string + return self.request(**r) + + +class AsyncRequestFactory(RequestFactory): + """ + Class that lets you create mock ASGI-like Request objects for use in + testing. Usage: + + rf = AsyncRequestFactory() + get_request = await rf.get('/hello/') + post_request = await rf.post('/submit/', {'foo': 'bar'}) + + Once you have a request object you can pass it to any view function, + including synchronous ones. The reason we have a separate class here is: + a) this makes ASGIRequest subclasses, and + b) AsyncTestClient can subclass it. + """ + + def _base_scope(self, **request): + """The base scope for a request.""" + # This is a minimal valid ASGI scope, plus: + # - headers['cookie'] for cookie support, + # - 'client' often useful, see #8551. + scope = { + "asgi": {"version": "3.0"}, + "type": "http", + "http_version": "1.1", + "client": ["127.0.0.1", 0], + "server": ("testserver", "80"), + "scheme": "http", + "method": "GET", + "headers": [], + **self.defaults, + **request, + } + scope["headers"].append( + ( + b"cookie", + b"; ".join( + sorted( + ("%s=%s" % (morsel.key, morsel.coded_value)).encode("ascii") + for morsel in self.cookies.values() + ) + ), + ) + ) + return scope + + def request(self, **request): + """Construct a generic request object.""" + # This is synchronous, which means all methods on this class are. + # AsyncClient, however, has an async request function, which makes all + # its methods async. + if "_body_file" in request: + body_file = request.pop("_body_file") + else: + body_file = FakePayload("") + # Wrap FakePayload body_file to allow large read() in test environment. + return ASGIRequest( + self._base_scope(**request), LimitedStream(body_file, len(body_file)) + ) + + def generic( + self, + method, + path, + data="", + content_type="application/octet-stream", + secure=False, + *, + headers=None, + **extra, + ): + """Construct an arbitrary HTTP request.""" + parsed = urlparse(str(path)) # path can be lazy. + data = force_bytes(data, settings.DEFAULT_CHARSET) + s = { + "method": method, + "path": self._get_path(parsed), + "server": ("127.0.0.1", "443" if secure else "80"), + "scheme": "https" if secure else "http", + "headers": [(b"host", b"testserver")], + } + if data: + s["headers"].extend( + [ + (b"content-length", str(len(data)).encode("ascii")), + (b"content-type", content_type.encode("ascii")), + ] + ) + s["_body_file"] = FakePayload(data) + follow = extra.pop("follow", None) + if follow is not None: + s["follow"] = follow + if query_string := extra.pop("QUERY_STRING", None): + s["query_string"] = query_string + if headers: + extra.update(HttpHeaders.to_asgi_names(headers)) + s["headers"] += [ + (key.lower().encode("ascii"), value.encode("latin1")) + for key, value in extra.items() + ] + # If QUERY_STRING is absent or empty, we want to extract it from the + # URL. + if not s.get("query_string"): + s["query_string"] = parsed[4] + return self.request(**s) + + +class ClientMixin: + """ + Mixin with common methods between Client and AsyncClient. + """ + + def store_exc_info(self, **kwargs): + """Store exceptions when they are generated by a view.""" + self.exc_info = sys.exc_info() + + def check_exception(self, response): + """ + Look for a signaled exception, clear the current context exception + data, re-raise the signaled exception, and clear the signaled exception + from the local cache. + """ + response.exc_info = self.exc_info + if self.exc_info: + _, exc_value, _ = self.exc_info + self.exc_info = None + if self.raise_request_exception: + raise exc_value + + @property + def session(self): + """Return the current session variables.""" + engine = import_module(settings.SESSION_ENGINE) + cookie = self.cookies.get(settings.SESSION_COOKIE_NAME) + if cookie: + return engine.SessionStore(cookie.value) + session = engine.SessionStore() + session.save() + self.cookies[settings.SESSION_COOKIE_NAME] = session.session_key + return session + + def login(self, **credentials): + """ + Set the Factory to appear as if it has successfully logged into a site. + + Return True if login is possible or False if the provided credentials + are incorrect. + """ + from django.contrib.auth import authenticate + + user = authenticate(**credentials) + if user: + self._login(user) + return True + return False + + def force_login(self, user, backend=None): + def get_backend(): + from django.contrib.auth import load_backend + + for backend_path in settings.AUTHENTICATION_BACKENDS: + backend = load_backend(backend_path) + if hasattr(backend, "get_user"): + return backend_path + + if backend is None: + backend = get_backend() + user.backend = backend + self._login(user, backend) + + def _login(self, user, backend=None): + from django.contrib.auth import login + + # Create a fake request to store login details. + request = HttpRequest() + if self.session: + request.session = self.session + else: + engine = import_module(settings.SESSION_ENGINE) + request.session = engine.SessionStore() + login(request, user, backend) + # Save the session values. + request.session.save() + # Set the cookie to represent the session. + session_cookie = settings.SESSION_COOKIE_NAME + self.cookies[session_cookie] = request.session.session_key + cookie_data = { + "max-age": None, + "path": "/", + "domain": settings.SESSION_COOKIE_DOMAIN, + "secure": settings.SESSION_COOKIE_SECURE or None, + "expires": None, + } + self.cookies[session_cookie].update(cookie_data) + + def logout(self): + """Log out the user by removing the cookies and session object.""" + from django.contrib.auth import get_user, logout + + request = HttpRequest() + if self.session: + request.session = self.session + request.user = get_user(request) + else: + engine = import_module(settings.SESSION_ENGINE) + request.session = engine.SessionStore() + logout(request) + self.cookies = SimpleCookie() + + def _parse_json(self, response, **extra): + if not hasattr(response, "_json"): + if not JSON_CONTENT_TYPE_RE.match(response.get("Content-Type")): + raise ValueError( + 'Content-Type header is "%s", not "application/json"' + % response.get("Content-Type") + ) + response._json = json.loads( + response.content.decode(response.charset), **extra + ) + return response._json + + +class Client(ClientMixin, RequestFactory): + """ + A class that can act as a client for testing purposes. + + It allows the user to compose GET and POST requests, and + obtain the response that the server gave to those requests. + The server Response objects are annotated with the details + of the contexts and templates that were rendered during the + process of serving the request. + + Client objects are stateful - they will retain cookie (and + thus session) details for the lifetime of the Client instance. + + This is not intended as a replacement for Twill/Selenium or + the like - it is here to allow testing against the + contexts and templates produced by a view, rather than the + HTML rendered to the end-user. + """ + + def __init__( + self, + enforce_csrf_checks=False, + raise_request_exception=True, + *, + headers=None, + **defaults, + ): + super().__init__(headers=headers, **defaults) + self.handler = ClientHandler(enforce_csrf_checks) + self.raise_request_exception = raise_request_exception + self.exc_info = None + self.extra = None + self.headers = None + + def request(self, **request): + """ + Make a generic request. Compose the environment dictionary and pass + to the handler, return the result of the handler. Assume defaults for + the query environment, which can be overridden using the arguments to + the request. + """ + environ = self._base_environ(**request) + + # Curry a data dictionary into an instance of the template renderer + # callback function. + data = {} + on_template_render = partial(store_rendered_templates, data) + signal_uid = "template-render-%s" % id(request) + signals.template_rendered.connect(on_template_render, dispatch_uid=signal_uid) + # Capture exceptions created by the handler. + exception_uid = "request-exception-%s" % id(request) + got_request_exception.connect(self.store_exc_info, dispatch_uid=exception_uid) + try: + response = self.handler(environ) + finally: + signals.template_rendered.disconnect(dispatch_uid=signal_uid) + got_request_exception.disconnect(dispatch_uid=exception_uid) + # Check for signaled exceptions. + self.check_exception(response) + # Save the client and request that stimulated the response. + response.client = self + response.request = request + # Add any rendered template detail to the response. + response.templates = data.get("templates", []) + response.context = data.get("context") + response.json = partial(self._parse_json, response) + # Attach the ResolverMatch instance to the response. + urlconf = getattr(response.wsgi_request, "urlconf", None) + response.resolver_match = SimpleLazyObject( + lambda: resolve(request["PATH_INFO"], urlconf=urlconf), + ) + # Flatten a single context. Not really necessary anymore thanks to the + # __getattr__ flattening in ContextList, but has some edge case + # backwards compatibility implications. + if response.context and len(response.context) == 1: + response.context = response.context[0] + # Update persistent cookie data. + if response.cookies: + self.cookies.update(response.cookies) + return response + + def get( + self, + path, + data=None, + follow=False, + secure=False, + *, + headers=None, + **extra, + ): + """Request a response from the server using GET.""" + self.extra = extra + self.headers = headers + response = super().get(path, data=data, secure=secure, headers=headers, **extra) + if follow: + response = self._handle_redirects( + response, data=data, headers=headers, **extra + ) + return response + + def post( + self, + path, + data=None, + content_type=MULTIPART_CONTENT, + follow=False, + secure=False, + *, + headers=None, + **extra, + ): + """Request a response from the server using POST.""" + self.extra = extra + self.headers = headers + response = super().post( + path, + data=data, + content_type=content_type, + secure=secure, + headers=headers, + **extra, + ) + if follow: + response = self._handle_redirects( + response, data=data, content_type=content_type, headers=headers, **extra + ) + return response + + def head( + self, + path, + data=None, + follow=False, + secure=False, + *, + headers=None, + **extra, + ): + """Request a response from the server using HEAD.""" + self.extra = extra + self.headers = headers + response = super().head( + path, data=data, secure=secure, headers=headers, **extra + ) + if follow: + response = self._handle_redirects( + response, data=data, headers=headers, **extra + ) + return response + + def options( + self, + path, + data="", + content_type="application/octet-stream", + follow=False, + secure=False, + *, + headers=None, + **extra, + ): + """Request a response from the server using OPTIONS.""" + self.extra = extra + self.headers = headers + response = super().options( + path, + data=data, + content_type=content_type, + secure=secure, + headers=headers, + **extra, + ) + if follow: + response = self._handle_redirects( + response, data=data, content_type=content_type, headers=headers, **extra + ) + return response + + def put( + self, + path, + data="", + content_type="application/octet-stream", + follow=False, + secure=False, + *, + headers=None, + **extra, + ): + """Send a resource to the server using PUT.""" + self.extra = extra + self.headers = headers + response = super().put( + path, + data=data, + content_type=content_type, + secure=secure, + headers=headers, + **extra, + ) + if follow: + response = self._handle_redirects( + response, data=data, content_type=content_type, headers=headers, **extra + ) + return response + + def patch( + self, + path, + data="", + content_type="application/octet-stream", + follow=False, + secure=False, + *, + headers=None, + **extra, + ): + """Send a resource to the server using PATCH.""" + self.extra = extra + self.headers = headers + response = super().patch( + path, + data=data, + content_type=content_type, + secure=secure, + headers=headers, + **extra, + ) + if follow: + response = self._handle_redirects( + response, data=data, content_type=content_type, headers=headers, **extra + ) + return response + + def delete( + self, + path, + data="", + content_type="application/octet-stream", + follow=False, + secure=False, + *, + headers=None, + **extra, + ): + """Send a DELETE request to the server.""" + self.extra = extra + self.headers = headers + response = super().delete( + path, + data=data, + content_type=content_type, + secure=secure, + headers=headers, + **extra, + ) + if follow: + response = self._handle_redirects( + response, data=data, content_type=content_type, headers=headers, **extra + ) + return response + + def trace( + self, + path, + data="", + follow=False, + secure=False, + *, + headers=None, + **extra, + ): + """Send a TRACE request to the server.""" + self.extra = extra + self.headers = headers + response = super().trace( + path, data=data, secure=secure, headers=headers, **extra + ) + if follow: + response = self._handle_redirects( + response, data=data, headers=headers, **extra + ) + return response + + def _handle_redirects( + self, + response, + data="", + content_type="", + headers=None, + **extra, + ): + """ + Follow any redirects by requesting responses from the server using GET. + """ + response.redirect_chain = [] + redirect_status_codes = ( + HTTPStatus.MOVED_PERMANENTLY, + HTTPStatus.FOUND, + HTTPStatus.SEE_OTHER, + HTTPStatus.TEMPORARY_REDIRECT, + HTTPStatus.PERMANENT_REDIRECT, + ) + while response.status_code in redirect_status_codes: + response_url = response.url + redirect_chain = response.redirect_chain + redirect_chain.append((response_url, response.status_code)) + + url = urlsplit(response_url) + if url.scheme: + extra["wsgi.url_scheme"] = url.scheme + if url.hostname: + extra["SERVER_NAME"] = url.hostname + if url.port: + extra["SERVER_PORT"] = str(url.port) + + path = url.path + # RFC 3986 Section 6.2.3: Empty path should be normalized to "/". + if not path and url.netloc: + path = "/" + # Prepend the request path to handle relative path redirects + if not path.startswith("/"): + path = urljoin(response.request["PATH_INFO"], path) + + if response.status_code in ( + HTTPStatus.TEMPORARY_REDIRECT, + HTTPStatus.PERMANENT_REDIRECT, + ): + # Preserve request method and query string (if needed) + # post-redirect for 307/308 responses. + request_method = response.request["REQUEST_METHOD"].lower() + if request_method not in ("get", "head"): + extra["QUERY_STRING"] = url.query + request_method = getattr(self, request_method) + else: + request_method = self.get + data = QueryDict(url.query) + content_type = None + + response = request_method( + path, + data=data, + content_type=content_type, + follow=False, + headers=headers, + **extra, + ) + response.redirect_chain = redirect_chain + + if redirect_chain[-1] in redirect_chain[:-1]: + # Check that we're not redirecting to somewhere we've already + # been to, to prevent loops. + raise RedirectCycleError( + "Redirect loop detected.", last_response=response + ) + if len(redirect_chain) > 20: + # Such a lengthy chain likely also means a loop, but one with + # a growing path, changing view, or changing query argument; + # 20 is the value of "network.http.redirection-limit" from Firefox. + raise RedirectCycleError("Too many redirects.", last_response=response) + + return response + + +class AsyncClient(ClientMixin, AsyncRequestFactory): + """ + An async version of Client that creates ASGIRequests and calls through an + async request path. + + Does not currently support "follow" on its methods. + """ + + def __init__( + self, + enforce_csrf_checks=False, + raise_request_exception=True, + *, + headers=None, + **defaults, + ): + super().__init__(headers=headers, **defaults) + self.handler = AsyncClientHandler(enforce_csrf_checks) + self.raise_request_exception = raise_request_exception + self.exc_info = None + self.extra = None + self.headers = None + + async def request(self, **request): + """ + Make a generic request. Compose the scope dictionary and pass to the + handler, return the result of the handler. Assume defaults for the + query environment, which can be overridden using the arguments to the + request. + """ + if "follow" in request: + raise NotImplementedError( + "AsyncClient request methods do not accept the follow parameter." + ) + scope = self._base_scope(**request) + # Curry a data dictionary into an instance of the template renderer + # callback function. + data = {} + on_template_render = partial(store_rendered_templates, data) + signal_uid = "template-render-%s" % id(request) + signals.template_rendered.connect(on_template_render, dispatch_uid=signal_uid) + # Capture exceptions created by the handler. + exception_uid = "request-exception-%s" % id(request) + got_request_exception.connect(self.store_exc_info, dispatch_uid=exception_uid) + try: + response = await self.handler(scope) + finally: + signals.template_rendered.disconnect(dispatch_uid=signal_uid) + got_request_exception.disconnect(dispatch_uid=exception_uid) + # Check for signaled exceptions. + self.check_exception(response) + # Save the client and request that stimulated the response. + response.client = self + response.request = request + # Add any rendered template detail to the response. + response.templates = data.get("templates", []) + response.context = data.get("context") + response.json = partial(self._parse_json, response) + # Attach the ResolverMatch instance to the response. + urlconf = getattr(response.asgi_request, "urlconf", None) + response.resolver_match = SimpleLazyObject( + lambda: resolve(request["path"], urlconf=urlconf), + ) + # Flatten a single context. Not really necessary anymore thanks to the + # __getattr__ flattening in ContextList, but has some edge case + # backwards compatibility implications. + if response.context and len(response.context) == 1: + response.context = response.context[0] + # Update persistent cookie data. + if response.cookies: + self.cookies.update(response.cookies) + return response diff --git a/virt/lib/python3.9/site-packages/django/test/utils 3.py b/virt/lib/python3.9/site-packages/django/test/utils 3.py new file mode 100644 index 00000000..0ec736c6 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/test/utils 3.py @@ -0,0 +1,1002 @@ +import collections +import logging +import os +import re +import sys +import time +import warnings +from contextlib import contextmanager +from functools import wraps +from io import StringIO +from itertools import chain +from types import SimpleNamespace +from unittest import TestCase, skipIf, skipUnless +from xml.dom.minidom import Node, parseString + +from asgiref.sync import iscoroutinefunction + +from django.apps import apps +from django.apps.registry import Apps +from django.conf import UserSettingsHolder, settings +from django.core import mail +from django.core.exceptions import ImproperlyConfigured +from django.core.signals import request_started, setting_changed +from django.db import DEFAULT_DB_ALIAS, connections, reset_queries +from django.db.models.options import Options +from django.template import Template +from django.test.signals import template_rendered +from django.urls import get_script_prefix, set_script_prefix +from django.utils.deprecation import RemovedInDjango50Warning +from django.utils.translation import deactivate + +try: + import jinja2 +except ImportError: + jinja2 = None + + +__all__ = ( + "Approximate", + "ContextList", + "isolate_lru_cache", + "get_runner", + "CaptureQueriesContext", + "ignore_warnings", + "isolate_apps", + "modify_settings", + "override_settings", + "override_system_checks", + "tag", + "requires_tz_support", + "setup_databases", + "setup_test_environment", + "teardown_test_environment", +) + +TZ_SUPPORT = hasattr(time, "tzset") + + +class Approximate: + def __init__(self, val, places=7): + self.val = val + self.places = places + + def __repr__(self): + return repr(self.val) + + def __eq__(self, other): + return self.val == other or round(abs(self.val - other), self.places) == 0 + + +class ContextList(list): + """ + A wrapper that provides direct key access to context items contained + in a list of context objects. + """ + + def __getitem__(self, key): + if isinstance(key, str): + for subcontext in self: + if key in subcontext: + return subcontext[key] + raise KeyError(key) + else: + return super().__getitem__(key) + + def get(self, key, default=None): + try: + return self.__getitem__(key) + except KeyError: + return default + + def __contains__(self, key): + try: + self[key] + except KeyError: + return False + return True + + def keys(self): + """ + Flattened keys of subcontexts. + """ + return set(chain.from_iterable(d for subcontext in self for d in subcontext)) + + +def instrumented_test_render(self, context): + """ + An instrumented Template render method, providing a signal that can be + intercepted by the test Client. + """ + template_rendered.send(sender=self, template=self, context=context) + return self.nodelist.render(context) + + +class _TestState: + pass + + +def setup_test_environment(debug=None): + """ + Perform global pre-test setup, such as installing the instrumented template + renderer and setting the email backend to the locmem email backend. + """ + if hasattr(_TestState, "saved_data"): + # Executing this function twice would overwrite the saved values. + raise RuntimeError( + "setup_test_environment() was already called and can't be called " + "again without first calling teardown_test_environment()." + ) + + if debug is None: + debug = settings.DEBUG + + saved_data = SimpleNamespace() + _TestState.saved_data = saved_data + + saved_data.allowed_hosts = settings.ALLOWED_HOSTS + # Add the default host of the test client. + settings.ALLOWED_HOSTS = [*settings.ALLOWED_HOSTS, "testserver"] + + saved_data.debug = settings.DEBUG + settings.DEBUG = debug + + saved_data.email_backend = settings.EMAIL_BACKEND + settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend" + + saved_data.template_render = Template._render + Template._render = instrumented_test_render + + mail.outbox = [] + + deactivate() + + +def teardown_test_environment(): + """ + Perform any global post-test teardown, such as restoring the original + template renderer and restoring the email sending functions. + """ + saved_data = _TestState.saved_data + + settings.ALLOWED_HOSTS = saved_data.allowed_hosts + settings.DEBUG = saved_data.debug + settings.EMAIL_BACKEND = saved_data.email_backend + Template._render = saved_data.template_render + + del _TestState.saved_data + del mail.outbox + + +def setup_databases( + verbosity, + interactive, + *, + time_keeper=None, + keepdb=False, + debug_sql=False, + parallel=0, + aliases=None, + serialized_aliases=None, + **kwargs, +): + """Create the test databases.""" + if time_keeper is None: + time_keeper = NullTimeKeeper() + + test_databases, mirrored_aliases = get_unique_databases_and_mirrors(aliases) + + old_names = [] + + for db_name, aliases in test_databases.values(): + first_alias = None + for alias in aliases: + connection = connections[alias] + old_names.append((connection, db_name, first_alias is None)) + + # Actually create the database for the first connection + if first_alias is None: + first_alias = alias + with time_keeper.timed(" Creating '%s'" % alias): + # RemovedInDjango50Warning: when the deprecation ends, + # replace with: + # serialize_alias = ( + # serialized_aliases is None + # or alias in serialized_aliases + # ) + try: + serialize_alias = connection.settings_dict["TEST"]["SERIALIZE"] + except KeyError: + serialize_alias = ( + serialized_aliases is None or alias in serialized_aliases + ) + else: + warnings.warn( + "The SERIALIZE test database setting is " + "deprecated as it can be inferred from the " + "TestCase/TransactionTestCase.databases that " + "enable the serialized_rollback feature.", + category=RemovedInDjango50Warning, + ) + connection.creation.create_test_db( + verbosity=verbosity, + autoclobber=not interactive, + keepdb=keepdb, + serialize=serialize_alias, + ) + if parallel > 1: + for index in range(parallel): + with time_keeper.timed(" Cloning '%s'" % alias): + connection.creation.clone_test_db( + suffix=str(index + 1), + verbosity=verbosity, + keepdb=keepdb, + ) + # Configure all other connections as mirrors of the first one + else: + connections[alias].creation.set_as_test_mirror( + connections[first_alias].settings_dict + ) + + # Configure the test mirrors. + for alias, mirror_alias in mirrored_aliases.items(): + connections[alias].creation.set_as_test_mirror( + connections[mirror_alias].settings_dict + ) + + if debug_sql: + for alias in connections: + connections[alias].force_debug_cursor = True + + return old_names + + +def iter_test_cases(tests): + """ + Return an iterator over a test suite's unittest.TestCase objects. + + The tests argument can also be an iterable of TestCase objects. + """ + for test in tests: + if isinstance(test, str): + # Prevent an unfriendly RecursionError that can happen with + # strings. + raise TypeError( + f"Test {test!r} must be a test case or test suite not string " + f"(was found in {tests!r})." + ) + if isinstance(test, TestCase): + yield test + else: + # Otherwise, assume it is a test suite. + yield from iter_test_cases(test) + + +def dependency_ordered(test_databases, dependencies): + """ + Reorder test_databases into an order that honors the dependencies + described in TEST[DEPENDENCIES]. + """ + ordered_test_databases = [] + resolved_databases = set() + + # Maps db signature to dependencies of all its aliases + dependencies_map = {} + + # Check that no database depends on its own alias + for sig, (_, aliases) in test_databases: + all_deps = set() + for alias in aliases: + all_deps.update(dependencies.get(alias, [])) + if not all_deps.isdisjoint(aliases): + raise ImproperlyConfigured( + "Circular dependency: databases %r depend on each other, " + "but are aliases." % aliases + ) + dependencies_map[sig] = all_deps + + while test_databases: + changed = False + deferred = [] + + # Try to find a DB that has all its dependencies met + for signature, (db_name, aliases) in test_databases: + if dependencies_map[signature].issubset(resolved_databases): + resolved_databases.update(aliases) + ordered_test_databases.append((signature, (db_name, aliases))) + changed = True + else: + deferred.append((signature, (db_name, aliases))) + + if not changed: + raise ImproperlyConfigured("Circular dependency in TEST[DEPENDENCIES]") + test_databases = deferred + return ordered_test_databases + + +def get_unique_databases_and_mirrors(aliases=None): + """ + Figure out which databases actually need to be created. + + Deduplicate entries in DATABASES that correspond the same database or are + configured as test mirrors. + + Return two values: + - test_databases: ordered mapping of signatures to (name, list of aliases) + where all aliases share the same underlying database. + - mirrored_aliases: mapping of mirror aliases to original aliases. + """ + if aliases is None: + aliases = connections + mirrored_aliases = {} + test_databases = {} + dependencies = {} + default_sig = connections[DEFAULT_DB_ALIAS].creation.test_db_signature() + + for alias in connections: + connection = connections[alias] + test_settings = connection.settings_dict["TEST"] + + if test_settings["MIRROR"]: + # If the database is marked as a test mirror, save the alias. + mirrored_aliases[alias] = test_settings["MIRROR"] + elif alias in aliases: + # Store a tuple with DB parameters that uniquely identify it. + # If we have two aliases with the same values for that tuple, + # we only need to create the test database once. + item = test_databases.setdefault( + connection.creation.test_db_signature(), + (connection.settings_dict["NAME"], []), + ) + # The default database must be the first because data migrations + # use the default alias by default. + if alias == DEFAULT_DB_ALIAS: + item[1].insert(0, alias) + else: + item[1].append(alias) + + if "DEPENDENCIES" in test_settings: + dependencies[alias] = test_settings["DEPENDENCIES"] + else: + if ( + alias != DEFAULT_DB_ALIAS + and connection.creation.test_db_signature() != default_sig + ): + dependencies[alias] = test_settings.get( + "DEPENDENCIES", [DEFAULT_DB_ALIAS] + ) + + test_databases = dict(dependency_ordered(test_databases.items(), dependencies)) + return test_databases, mirrored_aliases + + +def teardown_databases(old_config, verbosity, parallel=0, keepdb=False): + """Destroy all the non-mirror databases.""" + for connection, old_name, destroy in old_config: + if destroy: + if parallel > 1: + for index in range(parallel): + connection.creation.destroy_test_db( + suffix=str(index + 1), + verbosity=verbosity, + keepdb=keepdb, + ) + connection.creation.destroy_test_db(old_name, verbosity, keepdb) + + +def get_runner(settings, test_runner_class=None): + test_runner_class = test_runner_class or settings.TEST_RUNNER + test_path = test_runner_class.split(".") + # Allow for relative paths + if len(test_path) > 1: + test_module_name = ".".join(test_path[:-1]) + else: + test_module_name = "." + test_module = __import__(test_module_name, {}, {}, test_path[-1]) + return getattr(test_module, test_path[-1]) + + +class TestContextDecorator: + """ + A base class that can either be used as a context manager during tests + or as a test function or unittest.TestCase subclass decorator to perform + temporary alterations. + + `attr_name`: attribute assigned the return value of enable() if used as + a class decorator. + + `kwarg_name`: keyword argument passing the return value of enable() if + used as a function decorator. + """ + + def __init__(self, attr_name=None, kwarg_name=None): + self.attr_name = attr_name + self.kwarg_name = kwarg_name + + def enable(self): + raise NotImplementedError + + def disable(self): + raise NotImplementedError + + def __enter__(self): + return self.enable() + + def __exit__(self, exc_type, exc_value, traceback): + self.disable() + + def decorate_class(self, cls): + if issubclass(cls, TestCase): + decorated_setUp = cls.setUp + + def setUp(inner_self): + context = self.enable() + inner_self.addCleanup(self.disable) + if self.attr_name: + setattr(inner_self, self.attr_name, context) + decorated_setUp(inner_self) + + cls.setUp = setUp + return cls + raise TypeError("Can only decorate subclasses of unittest.TestCase") + + def decorate_callable(self, func): + if iscoroutinefunction(func): + # If the inner function is an async function, we must execute async + # as well so that the `with` statement executes at the right time. + @wraps(func) + async def inner(*args, **kwargs): + with self as context: + if self.kwarg_name: + kwargs[self.kwarg_name] = context + return await func(*args, **kwargs) + + else: + + @wraps(func) + def inner(*args, **kwargs): + with self as context: + if self.kwarg_name: + kwargs[self.kwarg_name] = context + return func(*args, **kwargs) + + return inner + + def __call__(self, decorated): + if isinstance(decorated, type): + return self.decorate_class(decorated) + elif callable(decorated): + return self.decorate_callable(decorated) + raise TypeError("Cannot decorate object of type %s" % type(decorated)) + + +class override_settings(TestContextDecorator): + """ + Act as either a decorator or a context manager. If it's a decorator, take a + function and return a wrapped function. If it's a contextmanager, use it + with the ``with`` statement. In either event, entering/exiting are called + before and after, respectively, the function/block is executed. + """ + + enable_exception = None + + def __init__(self, **kwargs): + self.options = kwargs + super().__init__() + + def enable(self): + # Keep this code at the beginning to leave the settings unchanged + # in case it raises an exception because INSTALLED_APPS is invalid. + if "INSTALLED_APPS" in self.options: + try: + apps.set_installed_apps(self.options["INSTALLED_APPS"]) + except Exception: + apps.unset_installed_apps() + raise + override = UserSettingsHolder(settings._wrapped) + for key, new_value in self.options.items(): + setattr(override, key, new_value) + self.wrapped = settings._wrapped + settings._wrapped = override + for key, new_value in self.options.items(): + try: + setting_changed.send( + sender=settings._wrapped.__class__, + setting=key, + value=new_value, + enter=True, + ) + except Exception as exc: + self.enable_exception = exc + self.disable() + + def disable(self): + if "INSTALLED_APPS" in self.options: + apps.unset_installed_apps() + settings._wrapped = self.wrapped + del self.wrapped + responses = [] + for key in self.options: + new_value = getattr(settings, key, None) + responses_for_setting = setting_changed.send_robust( + sender=settings._wrapped.__class__, + setting=key, + value=new_value, + enter=False, + ) + responses.extend(responses_for_setting) + if self.enable_exception is not None: + exc = self.enable_exception + self.enable_exception = None + raise exc + for _, response in responses: + if isinstance(response, Exception): + raise response + + def save_options(self, test_func): + if test_func._overridden_settings is None: + test_func._overridden_settings = self.options + else: + # Duplicate dict to prevent subclasses from altering their parent. + test_func._overridden_settings = { + **test_func._overridden_settings, + **self.options, + } + + def decorate_class(self, cls): + from django.test import SimpleTestCase + + if not issubclass(cls, SimpleTestCase): + raise ValueError( + "Only subclasses of Django SimpleTestCase can be decorated " + "with override_settings" + ) + self.save_options(cls) + return cls + + +class modify_settings(override_settings): + """ + Like override_settings, but makes it possible to append, prepend, or remove + items instead of redefining the entire list. + """ + + def __init__(self, *args, **kwargs): + if args: + # Hack used when instantiating from SimpleTestCase.setUpClass. + assert not kwargs + self.operations = args[0] + else: + assert not args + self.operations = list(kwargs.items()) + super(override_settings, self).__init__() + + def save_options(self, test_func): + if test_func._modified_settings is None: + test_func._modified_settings = self.operations + else: + # Duplicate list to prevent subclasses from altering their parent. + test_func._modified_settings = ( + list(test_func._modified_settings) + self.operations + ) + + def enable(self): + self.options = {} + for name, operations in self.operations: + try: + # When called from SimpleTestCase.setUpClass, values may be + # overridden several times; cumulate changes. + value = self.options[name] + except KeyError: + value = list(getattr(settings, name, [])) + for action, items in operations.items(): + # items my be a single value or an iterable. + if isinstance(items, str): + items = [items] + if action == "append": + value += [item for item in items if item not in value] + elif action == "prepend": + value = [item for item in items if item not in value] + value + elif action == "remove": + value = [item for item in value if item not in items] + else: + raise ValueError("Unsupported action: %s" % action) + self.options[name] = value + super().enable() + + +class override_system_checks(TestContextDecorator): + """ + Act as a decorator. Override list of registered system checks. + Useful when you override `INSTALLED_APPS`, e.g. if you exclude `auth` app, + you also need to exclude its system checks. + """ + + def __init__(self, new_checks, deployment_checks=None): + from django.core.checks.registry import registry + + self.registry = registry + self.new_checks = new_checks + self.deployment_checks = deployment_checks + super().__init__() + + def enable(self): + self.old_checks = self.registry.registered_checks + self.registry.registered_checks = set() + for check in self.new_checks: + self.registry.register(check, *getattr(check, "tags", ())) + self.old_deployment_checks = self.registry.deployment_checks + if self.deployment_checks is not None: + self.registry.deployment_checks = set() + for check in self.deployment_checks: + self.registry.register(check, *getattr(check, "tags", ()), deploy=True) + + def disable(self): + self.registry.registered_checks = self.old_checks + self.registry.deployment_checks = self.old_deployment_checks + + +def compare_xml(want, got): + """ + Try to do a 'xml-comparison' of want and got. Plain string comparison + doesn't always work because, for example, attribute ordering should not be + important. Ignore comment nodes, processing instructions, document type + node, and leading and trailing whitespaces. + + Based on https://github.com/lxml/lxml/blob/master/src/lxml/doctestcompare.py + """ + _norm_whitespace_re = re.compile(r"[ \t\n][ \t\n]+") + + def norm_whitespace(v): + return _norm_whitespace_re.sub(" ", v) + + def child_text(element): + return "".join( + c.data for c in element.childNodes if c.nodeType == Node.TEXT_NODE + ) + + def children(element): + return [c for c in element.childNodes if c.nodeType == Node.ELEMENT_NODE] + + def norm_child_text(element): + return norm_whitespace(child_text(element)) + + def attrs_dict(element): + return dict(element.attributes.items()) + + def check_element(want_element, got_element): + if want_element.tagName != got_element.tagName: + return False + if norm_child_text(want_element) != norm_child_text(got_element): + return False + if attrs_dict(want_element) != attrs_dict(got_element): + return False + want_children = children(want_element) + got_children = children(got_element) + if len(want_children) != len(got_children): + return False + return all( + check_element(want, got) for want, got in zip(want_children, got_children) + ) + + def first_node(document): + for node in document.childNodes: + if node.nodeType not in ( + Node.COMMENT_NODE, + Node.DOCUMENT_TYPE_NODE, + Node.PROCESSING_INSTRUCTION_NODE, + ): + return node + + want = want.strip().replace("\\n", "\n") + got = got.strip().replace("\\n", "\n") + + # If the string is not a complete xml document, we may need to add a + # root element. This allow us to compare fragments, like "<foo/><bar/>" + if not want.startswith("<?xml"): + wrapper = "<root>%s</root>" + want = wrapper % want + got = wrapper % got + + # Parse the want and got strings, and compare the parsings. + want_root = first_node(parseString(want)) + got_root = first_node(parseString(got)) + + return check_element(want_root, got_root) + + +class CaptureQueriesContext: + """ + Context manager that captures queries executed by the specified connection. + """ + + def __init__(self, connection): + self.connection = connection + + def __iter__(self): + return iter(self.captured_queries) + + def __getitem__(self, index): + return self.captured_queries[index] + + def __len__(self): + return len(self.captured_queries) + + @property + def captured_queries(self): + return self.connection.queries[self.initial_queries : self.final_queries] + + def __enter__(self): + self.force_debug_cursor = self.connection.force_debug_cursor + self.connection.force_debug_cursor = True + # Run any initialization queries if needed so that they won't be + # included as part of the count. + self.connection.ensure_connection() + self.initial_queries = len(self.connection.queries_log) + self.final_queries = None + request_started.disconnect(reset_queries) + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.connection.force_debug_cursor = self.force_debug_cursor + request_started.connect(reset_queries) + if exc_type is not None: + return + self.final_queries = len(self.connection.queries_log) + + +class ignore_warnings(TestContextDecorator): + def __init__(self, **kwargs): + self.ignore_kwargs = kwargs + if "message" in self.ignore_kwargs or "module" in self.ignore_kwargs: + self.filter_func = warnings.filterwarnings + else: + self.filter_func = warnings.simplefilter + super().__init__() + + def enable(self): + self.catch_warnings = warnings.catch_warnings() + self.catch_warnings.__enter__() + self.filter_func("ignore", **self.ignore_kwargs) + + def disable(self): + self.catch_warnings.__exit__(*sys.exc_info()) + + +# On OSes that don't provide tzset (Windows), we can't set the timezone +# in which the program runs. As a consequence, we must skip tests that +# don't enforce a specific timezone (with timezone.override or equivalent), +# or attempt to interpret naive datetimes in the default timezone. + +requires_tz_support = skipUnless( + TZ_SUPPORT, + "This test relies on the ability to run a program in an arbitrary " + "time zone, but your operating system isn't able to do that.", +) + + +@contextmanager +def extend_sys_path(*paths): + """Context manager to temporarily add paths to sys.path.""" + _orig_sys_path = sys.path[:] + sys.path.extend(paths) + try: + yield + finally: + sys.path = _orig_sys_path + + +@contextmanager +def isolate_lru_cache(lru_cache_object): + """Clear the cache of an LRU cache object on entering and exiting.""" + lru_cache_object.cache_clear() + try: + yield + finally: + lru_cache_object.cache_clear() + + +@contextmanager +def captured_output(stream_name): + """Return a context manager used by captured_stdout/stdin/stderr + that temporarily replaces the sys stream *stream_name* with a StringIO. + + Note: This function and the following ``captured_std*`` are copied + from CPython's ``test.support`` module.""" + orig_stdout = getattr(sys, stream_name) + setattr(sys, stream_name, StringIO()) + try: + yield getattr(sys, stream_name) + finally: + setattr(sys, stream_name, orig_stdout) + + +def captured_stdout(): + """Capture the output of sys.stdout: + + with captured_stdout() as stdout: + print("hello") + self.assertEqual(stdout.getvalue(), "hello\n") + """ + return captured_output("stdout") + + +def captured_stderr(): + """Capture the output of sys.stderr: + + with captured_stderr() as stderr: + print("hello", file=sys.stderr) + self.assertEqual(stderr.getvalue(), "hello\n") + """ + return captured_output("stderr") + + +def captured_stdin(): + """Capture the input to sys.stdin: + + with captured_stdin() as stdin: + stdin.write('hello\n') + stdin.seek(0) + # call test code that consumes from sys.stdin + captured = input() + self.assertEqual(captured, "hello") + """ + return captured_output("stdin") + + +@contextmanager +def freeze_time(t): + """ + Context manager to temporarily freeze time.time(). This temporarily + modifies the time function of the time module. Modules which import the + time function directly (e.g. `from time import time`) won't be affected + This isn't meant as a public API, but helps reduce some repetitive code in + Django's test suite. + """ + _real_time = time.time + time.time = lambda: t + try: + yield + finally: + time.time = _real_time + + +def require_jinja2(test_func): + """ + Decorator to enable a Jinja2 template engine in addition to the regular + Django template engine for a test or skip it if Jinja2 isn't available. + """ + test_func = skipIf(jinja2 is None, "this test requires jinja2")(test_func) + return override_settings( + TEMPLATES=[ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "APP_DIRS": True, + }, + { + "BACKEND": "django.template.backends.jinja2.Jinja2", + "APP_DIRS": True, + "OPTIONS": {"keep_trailing_newline": True}, + }, + ] + )(test_func) + + +class override_script_prefix(TestContextDecorator): + """Decorator or context manager to temporary override the script prefix.""" + + def __init__(self, prefix): + self.prefix = prefix + super().__init__() + + def enable(self): + self.old_prefix = get_script_prefix() + set_script_prefix(self.prefix) + + def disable(self): + set_script_prefix(self.old_prefix) + + +class LoggingCaptureMixin: + """ + Capture the output from the 'django' logger and store it on the class's + logger_output attribute. + """ + + def setUp(self): + self.logger = logging.getLogger("django") + self.old_stream = self.logger.handlers[0].stream + self.logger_output = StringIO() + self.logger.handlers[0].stream = self.logger_output + + def tearDown(self): + self.logger.handlers[0].stream = self.old_stream + + +class isolate_apps(TestContextDecorator): + """ + Act as either a decorator or a context manager to register models defined + in its wrapped context to an isolated registry. + + The list of installed apps the isolated registry should contain must be + passed as arguments. + + Two optional keyword arguments can be specified: + + `attr_name`: attribute assigned the isolated registry if used as a class + decorator. + + `kwarg_name`: keyword argument passing the isolated registry if used as a + function decorator. + """ + + def __init__(self, *installed_apps, **kwargs): + self.installed_apps = installed_apps + super().__init__(**kwargs) + + def enable(self): + self.old_apps = Options.default_apps + apps = Apps(self.installed_apps) + setattr(Options, "default_apps", apps) + return apps + + def disable(self): + setattr(Options, "default_apps", self.old_apps) + + +class TimeKeeper: + def __init__(self): + self.records = collections.defaultdict(list) + + @contextmanager + def timed(self, name): + self.records[name] + start_time = time.perf_counter() + try: + yield + finally: + end_time = time.perf_counter() - start_time + self.records[name].append(end_time) + + def print_results(self): + for name, end_times in self.records.items(): + for record_time in end_times: + record = "%s took %.3fs" % (name, record_time) + sys.stderr.write(record + os.linesep) + + +class NullTimeKeeper: + @contextmanager + def timed(self, name): + yield + + def print_results(self): + pass + + +def tag(*tags): + """Decorator to add tags to a test class or method.""" + + def decorator(obj): + if hasattr(obj, "tags"): + obj.tags = obj.tags.union(tags) + else: + setattr(obj, "tags", set(tags)) + return obj + + return decorator + + +@contextmanager +def register_lookup(field, *lookups, lookup_name=None): + """ + Context manager to temporarily register lookups on a model field using + lookup_name (or the lookup's lookup_name if not provided). + """ + try: + for lookup in lookups: + field.register_lookup(lookup, lookup_name) + yield + finally: + for lookup in lookups: + field._unregister_lookup(lookup, lookup_name) diff --git a/virt/lib/python3.9/site-packages/django/urls/resolvers 3.py b/virt/lib/python3.9/site-packages/django/urls/resolvers 3.py new file mode 100644 index 00000000..e4a33a36 --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/urls/resolvers 3.py @@ -0,0 +1,828 @@ +""" +This module converts requested URLs to callback view functions. + +URLResolver is the main class here. Its resolve() method takes a URL (as +a string) and returns a ResolverMatch object which provides access to all +attributes of the resolved URL match. +""" +import functools +import inspect +import re +import string +from importlib import import_module +from pickle import PicklingError +from urllib.parse import quote + +from asgiref.local import Local + +from django.conf import settings +from django.core.checks import Error, Warning +from django.core.checks.urls import check_resolver +from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist +from django.utils.datastructures import MultiValueDict +from django.utils.functional import cached_property +from django.utils.http import RFC3986_SUBDELIMS, escape_leading_slashes +from django.utils.regex_helper import _lazy_re_compile, normalize +from django.utils.translation import get_language + +from .converters import get_converter +from .exceptions import NoReverseMatch, Resolver404 +from .utils import get_callable + + +class ResolverMatch: + def __init__( + self, + func, + args, + kwargs, + url_name=None, + app_names=None, + namespaces=None, + route=None, + tried=None, + captured_kwargs=None, + extra_kwargs=None, + ): + self.func = func + self.args = args + self.kwargs = kwargs + self.url_name = url_name + self.route = route + self.tried = tried + self.captured_kwargs = captured_kwargs + self.extra_kwargs = extra_kwargs + + # If a URLRegexResolver doesn't have a namespace or app_name, it passes + # in an empty value. + self.app_names = [x for x in app_names if x] if app_names else [] + self.app_name = ":".join(self.app_names) + self.namespaces = [x for x in namespaces if x] if namespaces else [] + self.namespace = ":".join(self.namespaces) + + if hasattr(func, "view_class"): + func = func.view_class + if not hasattr(func, "__name__"): + # A class-based view + self._func_path = func.__class__.__module__ + "." + func.__class__.__name__ + else: + # A function-based view + self._func_path = func.__module__ + "." + func.__name__ + + view_path = url_name or self._func_path + self.view_name = ":".join(self.namespaces + [view_path]) + + def __getitem__(self, index): + return (self.func, self.args, self.kwargs)[index] + + def __repr__(self): + if isinstance(self.func, functools.partial): + func = repr(self.func) + else: + func = self._func_path + return ( + "ResolverMatch(func=%s, args=%r, kwargs=%r, url_name=%r, " + "app_names=%r, namespaces=%r, route=%r%s%s)" + % ( + func, + self.args, + self.kwargs, + self.url_name, + self.app_names, + self.namespaces, + self.route, + f", captured_kwargs={self.captured_kwargs!r}" + if self.captured_kwargs + else "", + f", extra_kwargs={self.extra_kwargs!r}" if self.extra_kwargs else "", + ) + ) + + def __reduce_ex__(self, protocol): + raise PicklingError(f"Cannot pickle {self.__class__.__qualname__}.") + + +def get_resolver(urlconf=None): + if urlconf is None: + urlconf = settings.ROOT_URLCONF + return _get_cached_resolver(urlconf) + + +@functools.lru_cache(maxsize=None) +def _get_cached_resolver(urlconf=None): + return URLResolver(RegexPattern(r"^/"), urlconf) + + +@functools.lru_cache(maxsize=None) +def get_ns_resolver(ns_pattern, resolver, converters): + # Build a namespaced resolver for the given parent URLconf pattern. + # This makes it possible to have captured parameters in the parent + # URLconf pattern. + pattern = RegexPattern(ns_pattern) + pattern.converters = dict(converters) + ns_resolver = URLResolver(pattern, resolver.url_patterns) + return URLResolver(RegexPattern(r"^/"), [ns_resolver]) + + +class LocaleRegexDescriptor: + def __init__(self, attr): + self.attr = attr + + def __get__(self, instance, cls=None): + """ + Return a compiled regular expression based on the active language. + """ + if instance is None: + return self + # As a performance optimization, if the given regex string is a regular + # string (not a lazily-translated string proxy), compile it once and + # avoid per-language compilation. + pattern = getattr(instance, self.attr) + if isinstance(pattern, str): + instance.__dict__["regex"] = instance._compile(pattern) + return instance.__dict__["regex"] + language_code = get_language() + if language_code not in instance._regex_dict: + instance._regex_dict[language_code] = instance._compile(str(pattern)) + return instance._regex_dict[language_code] + + +class CheckURLMixin: + def describe(self): + """ + Format the URL pattern for display in warning messages. + """ + description = "'{}'".format(self) + if self.name: + description += " [name='{}']".format(self.name) + return description + + def _check_pattern_startswith_slash(self): + """ + Check that the pattern does not begin with a forward slash. + """ + regex_pattern = self.regex.pattern + if not settings.APPEND_SLASH: + # Skip check as it can be useful to start a URL pattern with a slash + # when APPEND_SLASH=False. + return [] + if regex_pattern.startswith(("/", "^/", "^\\/")) and not regex_pattern.endswith( + "/" + ): + warning = Warning( + "Your URL pattern {} has a route beginning with a '/'. Remove this " + "slash as it is unnecessary. If this pattern is targeted in an " + "include(), ensure the include() pattern has a trailing '/'.".format( + self.describe() + ), + id="urls.W002", + ) + return [warning] + else: + return [] + + +class RegexPattern(CheckURLMixin): + regex = LocaleRegexDescriptor("_regex") + + def __init__(self, regex, name=None, is_endpoint=False): + self._regex = regex + self._regex_dict = {} + self._is_endpoint = is_endpoint + self.name = name + self.converters = {} + + def match(self, path): + match = ( + self.regex.fullmatch(path) + if self._is_endpoint and self.regex.pattern.endswith("$") + else self.regex.search(path) + ) + if match: + # If there are any named groups, use those as kwargs, ignoring + # non-named groups. Otherwise, pass all non-named arguments as + # positional arguments. + kwargs = match.groupdict() + args = () if kwargs else match.groups() + kwargs = {k: v for k, v in kwargs.items() if v is not None} + return path[match.end() :], args, kwargs + return None + + def check(self): + warnings = [] + warnings.extend(self._check_pattern_startswith_slash()) + if not self._is_endpoint: + warnings.extend(self._check_include_trailing_dollar()) + return warnings + + def _check_include_trailing_dollar(self): + regex_pattern = self.regex.pattern + if regex_pattern.endswith("$") and not regex_pattern.endswith(r"\$"): + return [ + Warning( + "Your URL pattern {} uses include with a route ending with a '$'. " + "Remove the dollar from the route to avoid problems including " + "URLs.".format(self.describe()), + id="urls.W001", + ) + ] + else: + return [] + + def _compile(self, regex): + """Compile and return the given regular expression.""" + try: + return re.compile(regex) + except re.error as e: + raise ImproperlyConfigured( + '"%s" is not a valid regular expression: %s' % (regex, e) + ) from e + + def __str__(self): + return str(self._regex) + + +_PATH_PARAMETER_COMPONENT_RE = _lazy_re_compile( + r"<(?:(?P<converter>[^>:]+):)?(?P<parameter>[^>]+)>" +) + + +def _route_to_regex(route, is_endpoint=False): + """ + Convert a path pattern into a regular expression. Return the regular + expression and a dictionary mapping the capture names to the converters. + For example, 'foo/<int:pk>' returns '^foo\\/(?P<pk>[0-9]+)' + and {'pk': <django.urls.converters.IntConverter>}. + """ + original_route = route + parts = ["^"] + converters = {} + while True: + match = _PATH_PARAMETER_COMPONENT_RE.search(route) + if not match: + parts.append(re.escape(route)) + break + elif not set(match.group()).isdisjoint(string.whitespace): + raise ImproperlyConfigured( + "URL route '%s' cannot contain whitespace in angle brackets " + "<…>." % original_route + ) + parts.append(re.escape(route[: match.start()])) + route = route[match.end() :] + parameter = match["parameter"] + if not parameter.isidentifier(): + raise ImproperlyConfigured( + "URL route '%s' uses parameter name %r which isn't a valid " + "Python identifier." % (original_route, parameter) + ) + raw_converter = match["converter"] + if raw_converter is None: + # If a converter isn't specified, the default is `str`. + raw_converter = "str" + try: + converter = get_converter(raw_converter) + except KeyError as e: + raise ImproperlyConfigured( + "URL route %r uses invalid converter %r." + % (original_route, raw_converter) + ) from e + converters[parameter] = converter + parts.append("(?P<" + parameter + ">" + converter.regex + ")") + if is_endpoint: + parts.append(r"\Z") + return "".join(parts), converters + + +class RoutePattern(CheckURLMixin): + regex = LocaleRegexDescriptor("_route") + + def __init__(self, route, name=None, is_endpoint=False): + self._route = route + self._regex_dict = {} + self._is_endpoint = is_endpoint + self.name = name + self.converters = _route_to_regex(str(route), is_endpoint)[1] + + def match(self, path): + match = self.regex.search(path) + if match: + # RoutePattern doesn't allow non-named groups so args are ignored. + kwargs = match.groupdict() + for key, value in kwargs.items(): + converter = self.converters[key] + try: + kwargs[key] = converter.to_python(value) + except ValueError: + return None + return path[match.end() :], (), kwargs + return None + + def check(self): + warnings = self._check_pattern_startswith_slash() + route = self._route + if "(?P<" in route or route.startswith("^") or route.endswith("$"): + warnings.append( + Warning( + "Your URL pattern {} has a route that contains '(?P<', begins " + "with a '^', or ends with a '$'. This was likely an oversight " + "when migrating to django.urls.path().".format(self.describe()), + id="2_0.W001", + ) + ) + return warnings + + def _compile(self, route): + return re.compile(_route_to_regex(route, self._is_endpoint)[0]) + + def __str__(self): + return str(self._route) + + +class LocalePrefixPattern: + def __init__(self, prefix_default_language=True): + self.prefix_default_language = prefix_default_language + self.converters = {} + + @property + def regex(self): + # This is only used by reverse() and cached in _reverse_dict. + return re.compile(re.escape(self.language_prefix)) + + @property + def language_prefix(self): + language_code = get_language() or settings.LANGUAGE_CODE + if language_code == settings.LANGUAGE_CODE and not self.prefix_default_language: + return "" + else: + return "%s/" % language_code + + def match(self, path): + language_prefix = self.language_prefix + if path.startswith(language_prefix): + return path[len(language_prefix) :], (), {} + return None + + def check(self): + return [] + + def describe(self): + return "'{}'".format(self) + + def __str__(self): + return self.language_prefix + + +class URLPattern: + def __init__(self, pattern, callback, default_args=None, name=None): + self.pattern = pattern + self.callback = callback # the view + self.default_args = default_args or {} + self.name = name + + def __repr__(self): + return "<%s %s>" % (self.__class__.__name__, self.pattern.describe()) + + def check(self): + warnings = self._check_pattern_name() + warnings.extend(self.pattern.check()) + warnings.extend(self._check_callback()) + return warnings + + def _check_pattern_name(self): + """ + Check that the pattern name does not contain a colon. + """ + if self.pattern.name is not None and ":" in self.pattern.name: + warning = Warning( + "Your URL pattern {} has a name including a ':'. Remove the colon, to " + "avoid ambiguous namespace references.".format(self.pattern.describe()), + id="urls.W003", + ) + return [warning] + else: + return [] + + def _check_callback(self): + from django.views import View + + view = self.callback + if inspect.isclass(view) and issubclass(view, View): + return [ + Error( + "Your URL pattern %s has an invalid view, pass %s.as_view() " + "instead of %s." + % ( + self.pattern.describe(), + view.__name__, + view.__name__, + ), + id="urls.E009", + ) + ] + return [] + + def resolve(self, path): + match = self.pattern.match(path) + if match: + new_path, args, captured_kwargs = match + # Pass any default args as **kwargs. + kwargs = {**captured_kwargs, **self.default_args} + return ResolverMatch( + self.callback, + args, + kwargs, + self.pattern.name, + route=str(self.pattern), + captured_kwargs=captured_kwargs, + extra_kwargs=self.default_args, + ) + + @cached_property + def lookup_str(self): + """ + A string that identifies the view (e.g. 'path.to.view_function' or + 'path.to.ClassBasedView'). + """ + callback = self.callback + if isinstance(callback, functools.partial): + callback = callback.func + if hasattr(callback, "view_class"): + callback = callback.view_class + elif not hasattr(callback, "__name__"): + return callback.__module__ + "." + callback.__class__.__name__ + return callback.__module__ + "." + callback.__qualname__ + + +class URLResolver: + def __init__( + self, pattern, urlconf_name, default_kwargs=None, app_name=None, namespace=None + ): + self.pattern = pattern + # urlconf_name is the dotted Python path to the module defining + # urlpatterns. It may also be an object with an urlpatterns attribute + # or urlpatterns itself. + self.urlconf_name = urlconf_name + self.callback = None + self.default_kwargs = default_kwargs or {} + self.namespace = namespace + self.app_name = app_name + self._reverse_dict = {} + self._namespace_dict = {} + self._app_dict = {} + # set of dotted paths to all functions and classes that are used in + # urlpatterns + self._callback_strs = set() + self._populated = False + self._local = Local() + + def __repr__(self): + if isinstance(self.urlconf_name, list) and self.urlconf_name: + # Don't bother to output the whole list, it can be huge + urlconf_repr = "<%s list>" % self.urlconf_name[0].__class__.__name__ + else: + urlconf_repr = repr(self.urlconf_name) + return "<%s %s (%s:%s) %s>" % ( + self.__class__.__name__, + urlconf_repr, + self.app_name, + self.namespace, + self.pattern.describe(), + ) + + def check(self): + messages = [] + for pattern in self.url_patterns: + messages.extend(check_resolver(pattern)) + messages.extend(self._check_custom_error_handlers()) + return messages or self.pattern.check() + + def _check_custom_error_handlers(self): + messages = [] + # All handlers take (request, exception) arguments except handler500 + # which takes (request). + for status_code, num_parameters in [(400, 2), (403, 2), (404, 2), (500, 1)]: + try: + handler = self.resolve_error_handler(status_code) + except (ImportError, ViewDoesNotExist) as e: + path = getattr(self.urlconf_module, "handler%s" % status_code) + msg = ( + "The custom handler{status_code} view '{path}' could not be " + "imported." + ).format(status_code=status_code, path=path) + messages.append(Error(msg, hint=str(e), id="urls.E008")) + continue + signature = inspect.signature(handler) + args = [None] * num_parameters + try: + signature.bind(*args) + except TypeError: + msg = ( + "The custom handler{status_code} view '{path}' does not " + "take the correct number of arguments ({args})." + ).format( + status_code=status_code, + path=handler.__module__ + "." + handler.__qualname__, + args="request, exception" if num_parameters == 2 else "request", + ) + messages.append(Error(msg, id="urls.E007")) + return messages + + def _populate(self): + # Short-circuit if called recursively in this thread to prevent + # infinite recursion. Concurrent threads may call this at the same + # time and will need to continue, so set 'populating' on a + # thread-local variable. + if getattr(self._local, "populating", False): + return + try: + self._local.populating = True + lookups = MultiValueDict() + namespaces = {} + apps = {} + language_code = get_language() + for url_pattern in reversed(self.url_patterns): + p_pattern = url_pattern.pattern.regex.pattern + if p_pattern.startswith("^"): + p_pattern = p_pattern[1:] + if isinstance(url_pattern, URLPattern): + self._callback_strs.add(url_pattern.lookup_str) + bits = normalize(url_pattern.pattern.regex.pattern) + lookups.appendlist( + url_pattern.callback, + ( + bits, + p_pattern, + url_pattern.default_args, + url_pattern.pattern.converters, + ), + ) + if url_pattern.name is not None: + lookups.appendlist( + url_pattern.name, + ( + bits, + p_pattern, + url_pattern.default_args, + url_pattern.pattern.converters, + ), + ) + else: # url_pattern is a URLResolver. + url_pattern._populate() + if url_pattern.app_name: + apps.setdefault(url_pattern.app_name, []).append( + url_pattern.namespace + ) + namespaces[url_pattern.namespace] = (p_pattern, url_pattern) + else: + for name in url_pattern.reverse_dict: + for ( + matches, + pat, + defaults, + converters, + ) in url_pattern.reverse_dict.getlist(name): + new_matches = normalize(p_pattern + pat) + lookups.appendlist( + name, + ( + new_matches, + p_pattern + pat, + {**defaults, **url_pattern.default_kwargs}, + { + **self.pattern.converters, + **url_pattern.pattern.converters, + **converters, + }, + ), + ) + for namespace, ( + prefix, + sub_pattern, + ) in url_pattern.namespace_dict.items(): + current_converters = url_pattern.pattern.converters + sub_pattern.pattern.converters.update(current_converters) + namespaces[namespace] = (p_pattern + prefix, sub_pattern) + for app_name, namespace_list in url_pattern.app_dict.items(): + apps.setdefault(app_name, []).extend(namespace_list) + self._callback_strs.update(url_pattern._callback_strs) + self._namespace_dict[language_code] = namespaces + self._app_dict[language_code] = apps + self._reverse_dict[language_code] = lookups + self._populated = True + finally: + self._local.populating = False + + @property + def reverse_dict(self): + language_code = get_language() + if language_code not in self._reverse_dict: + self._populate() + return self._reverse_dict[language_code] + + @property + def namespace_dict(self): + language_code = get_language() + if language_code not in self._namespace_dict: + self._populate() + return self._namespace_dict[language_code] + + @property + def app_dict(self): + language_code = get_language() + if language_code not in self._app_dict: + self._populate() + return self._app_dict[language_code] + + @staticmethod + def _extend_tried(tried, pattern, sub_tried=None): + if sub_tried is None: + tried.append([pattern]) + else: + tried.extend([pattern, *t] for t in sub_tried) + + @staticmethod + def _join_route(route1, route2): + """Join two routes, without the starting ^ in the second route.""" + if not route1: + return route2 + if route2.startswith("^"): + route2 = route2[1:] + return route1 + route2 + + def _is_callback(self, name): + if not self._populated: + self._populate() + return name in self._callback_strs + + def resolve(self, path): + path = str(path) # path may be a reverse_lazy object + tried = [] + match = self.pattern.match(path) + if match: + new_path, args, kwargs = match + for pattern in self.url_patterns: + try: + sub_match = pattern.resolve(new_path) + except Resolver404 as e: + self._extend_tried(tried, pattern, e.args[0].get("tried")) + else: + if sub_match: + # Merge captured arguments in match with submatch + sub_match_dict = {**kwargs, **self.default_kwargs} + # Update the sub_match_dict with the kwargs from the sub_match. + sub_match_dict.update(sub_match.kwargs) + # If there are *any* named groups, ignore all non-named groups. + # Otherwise, pass all non-named arguments as positional + # arguments. + sub_match_args = sub_match.args + if not sub_match_dict: + sub_match_args = args + sub_match.args + current_route = ( + "" + if isinstance(pattern, URLPattern) + else str(pattern.pattern) + ) + self._extend_tried(tried, pattern, sub_match.tried) + return ResolverMatch( + sub_match.func, + sub_match_args, + sub_match_dict, + sub_match.url_name, + [self.app_name] + sub_match.app_names, + [self.namespace] + sub_match.namespaces, + self._join_route(current_route, sub_match.route), + tried, + captured_kwargs=sub_match.captured_kwargs, + extra_kwargs={ + **self.default_kwargs, + **sub_match.extra_kwargs, + }, + ) + tried.append([pattern]) + raise Resolver404({"tried": tried, "path": new_path}) + raise Resolver404({"path": path}) + + @cached_property + def urlconf_module(self): + if isinstance(self.urlconf_name, str): + return import_module(self.urlconf_name) + else: + return self.urlconf_name + + @cached_property + def url_patterns(self): + # urlconf_module might be a valid set of patterns, so we default to it + patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module) + try: + iter(patterns) + except TypeError as e: + msg = ( + "The included URLconf '{name}' does not appear to have " + "any patterns in it. If you see the 'urlpatterns' variable " + "with valid patterns in the file then the issue is probably " + "caused by a circular import." + ) + raise ImproperlyConfigured(msg.format(name=self.urlconf_name)) from e + return patterns + + def resolve_error_handler(self, view_type): + callback = getattr(self.urlconf_module, "handler%s" % view_type, None) + if not callback: + # No handler specified in file; use lazy import, since + # django.conf.urls imports this file. + from django.conf import urls + + callback = getattr(urls, "handler%s" % view_type) + return get_callable(callback) + + def reverse(self, lookup_view, *args, **kwargs): + return self._reverse_with_prefix(lookup_view, "", *args, **kwargs) + + def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs): + if args and kwargs: + raise ValueError("Don't mix *args and **kwargs in call to reverse()!") + + if not self._populated: + self._populate() + + possibilities = self.reverse_dict.getlist(lookup_view) + + for possibility, pattern, defaults, converters in possibilities: + for result, params in possibility: + if args: + if len(args) != len(params): + continue + candidate_subs = dict(zip(params, args)) + else: + if set(kwargs).symmetric_difference(params).difference(defaults): + continue + matches = True + for k, v in defaults.items(): + if k in params: + continue + if kwargs.get(k, v) != v: + matches = False + break + if not matches: + continue + candidate_subs = kwargs + # Convert the candidate subs to text using Converter.to_url(). + text_candidate_subs = {} + match = True + for k, v in candidate_subs.items(): + if k in converters: + try: + text_candidate_subs[k] = converters[k].to_url(v) + except ValueError: + match = False + break + else: + text_candidate_subs[k] = str(v) + if not match: + continue + # WSGI provides decoded URLs, without %xx escapes, and the URL + # resolver operates on such URLs. First substitute arguments + # without quoting to build a decoded URL and look for a match. + # Then, if we have a match, redo the substitution with quoted + # arguments in order to return a properly encoded URL. + candidate_pat = _prefix.replace("%", "%%") + result + if re.search( + "^%s%s" % (re.escape(_prefix), pattern), + candidate_pat % text_candidate_subs, + ): + # safe characters from `pchar` definition of RFC 3986 + url = quote( + candidate_pat % text_candidate_subs, + safe=RFC3986_SUBDELIMS + "/~:@", + ) + # Don't allow construction of scheme relative urls. + return escape_leading_slashes(url) + # lookup_view can be URL name or callable, but callables are not + # friendly in error messages. + m = getattr(lookup_view, "__module__", None) + n = getattr(lookup_view, "__name__", None) + if m is not None and n is not None: + lookup_view_s = "%s.%s" % (m, n) + else: + lookup_view_s = lookup_view + + patterns = [pattern for (_, pattern, _, _) in possibilities] + if patterns: + if args: + arg_msg = "arguments '%s'" % (args,) + elif kwargs: + arg_msg = "keyword arguments '%s'" % kwargs + else: + arg_msg = "no arguments" + msg = "Reverse for '%s' with %s not found. %d pattern(s) tried: %s" % ( + lookup_view_s, + arg_msg, + len(patterns), + patterns, + ) + else: + msg = ( + "Reverse for '%(view)s' not found. '%(view)s' is not " + "a valid view function or pattern name." % {"view": lookup_view_s} + ) + raise NoReverseMatch(msg) diff --git a/virt/lib/python3.9/site-packages/django/utils/translation/trans_real 3.py b/virt/lib/python3.9/site-packages/django/utils/translation/trans_real 3.py new file mode 100644 index 00000000..f4ba784f --- /dev/null +++ b/virt/lib/python3.9/site-packages/django/utils/translation/trans_real 3.py @@ -0,0 +1,639 @@ +"""Translation helper functions.""" +import functools +import gettext as gettext_module +import os +import re +import sys +import warnings + +from asgiref.local import Local + +from django.apps import apps +from django.conf import settings +from django.conf.locale import LANG_INFO +from django.core.exceptions import AppRegistryNotReady +from django.core.signals import setting_changed +from django.dispatch import receiver +from django.utils.regex_helper import _lazy_re_compile +from django.utils.safestring import SafeData, mark_safe + +from . import to_language, to_locale + +# Translations are cached in a dictionary for every language. +# The active translations are stored by threadid to make them thread local. +_translations = {} +_active = Local() + +# The default translation is based on the settings file. +_default = None + +# magic gettext number to separate context from message +CONTEXT_SEPARATOR = "\x04" + +# Maximum number of characters that will be parsed from the Accept-Language +# header to prevent possible denial of service or memory exhaustion attacks. +# About 10x longer than the longest value shown on MDN’s Accept-Language page. +ACCEPT_LANGUAGE_HEADER_MAX_LENGTH = 500 + +# Format of Accept-Language header values. From RFC 9110 Sections 12.4.2 and +# 12.5.4, and RFC 5646 Section 2.1. +accept_language_re = _lazy_re_compile( + r""" + # "en", "en-au", "x-y-z", "es-419", "*" + ([A-Za-z]{1,8}(?:-[A-Za-z0-9]{1,8})*|\*) + # Optional "q=1.00", "q=0.8" + (?:\s*;\s*q=(0(?:\.[0-9]{,3})?|1(?:\.0{,3})?))? + # Multiple accepts per header. + (?:\s*,\s*|$) + """, + re.VERBOSE, +) + +language_code_re = _lazy_re_compile( + r"^[a-z]{1,8}(?:-[a-z0-9]{1,8})*(?:@[a-z0-9]{1,20})?$", re.IGNORECASE +) + +language_code_prefix_re = _lazy_re_compile(r"^/(\w+([@-]\w+){0,2})(/|$)") + + +@receiver(setting_changed) +def reset_cache(*, setting, **kwargs): + """ + Reset global state when LANGUAGES setting has been changed, as some + languages should no longer be accepted. + """ + if setting in ("LANGUAGES", "LANGUAGE_CODE"): + check_for_language.cache_clear() + get_languages.cache_clear() + get_supported_language_variant.cache_clear() + + +class TranslationCatalog: + """ + Simulate a dict for DjangoTranslation._catalog so as multiple catalogs + with different plural equations are kept separate. + """ + + def __init__(self, trans=None): + self._catalogs = [trans._catalog.copy()] if trans else [{}] + self._plurals = [trans.plural] if trans else [lambda n: int(n != 1)] + + def __getitem__(self, key): + for cat in self._catalogs: + try: + return cat[key] + except KeyError: + pass + raise KeyError(key) + + def __setitem__(self, key, value): + self._catalogs[0][key] = value + + def __contains__(self, key): + return any(key in cat for cat in self._catalogs) + + def items(self): + for cat in self._catalogs: + yield from cat.items() + + def keys(self): + for cat in self._catalogs: + yield from cat.keys() + + def update(self, trans): + # Merge if plural function is the same, else prepend. + for cat, plural in zip(self._catalogs, self._plurals): + if trans.plural.__code__ == plural.__code__: + cat.update(trans._catalog) + break + else: + self._catalogs.insert(0, trans._catalog.copy()) + self._plurals.insert(0, trans.plural) + + def get(self, key, default=None): + missing = object() + for cat in self._catalogs: + result = cat.get(key, missing) + if result is not missing: + return result + return default + + def plural(self, msgid, num): + for cat, plural in zip(self._catalogs, self._plurals): + tmsg = cat.get((msgid, plural(num))) + if tmsg is not None: + return tmsg + raise KeyError + + +class DjangoTranslation(gettext_module.GNUTranslations): + """ + Set up the GNUTranslations context with regard to output charset. + + This translation object will be constructed out of multiple GNUTranslations + objects by merging their catalogs. It will construct an object for the + requested language and add a fallback to the default language, if it's + different from the requested language. + """ + + domain = "django" + + def __init__(self, language, domain=None, localedirs=None): + """Create a GNUTranslations() using many locale directories""" + gettext_module.GNUTranslations.__init__(self) + if domain is not None: + self.domain = domain + + self.__language = language + self.__to_language = to_language(language) + self.__locale = to_locale(language) + self._catalog = None + # If a language doesn't have a catalog, use the Germanic default for + # pluralization: anything except one is pluralized. + self.plural = lambda n: int(n != 1) + + if self.domain == "django": + if localedirs is not None: + # A module-level cache is used for caching 'django' translations + warnings.warn( + "localedirs is ignored when domain is 'django'.", RuntimeWarning + ) + localedirs = None + self._init_translation_catalog() + + if localedirs: + for localedir in localedirs: + translation = self._new_gnu_trans(localedir) + self.merge(translation) + else: + self._add_installed_apps_translations() + + self._add_local_translations() + if ( + self.__language == settings.LANGUAGE_CODE + and self.domain == "django" + and self._catalog is None + ): + # default lang should have at least one translation file available. + raise OSError( + "No translation files found for default language %s." + % settings.LANGUAGE_CODE + ) + self._add_fallback(localedirs) + if self._catalog is None: + # No catalogs found for this language, set an empty catalog. + self._catalog = TranslationCatalog() + + def __repr__(self): + return "<DjangoTranslation lang:%s>" % self.__language + + def _new_gnu_trans(self, localedir, use_null_fallback=True): + """ + Return a mergeable gettext.GNUTranslations instance. + + A convenience wrapper. By default gettext uses 'fallback=False'. + Using param `use_null_fallback` to avoid confusion with any other + references to 'fallback'. + """ + return gettext_module.translation( + domain=self.domain, + localedir=localedir, + languages=[self.__locale], + fallback=use_null_fallback, + ) + + def _init_translation_catalog(self): + """Create a base catalog using global django translations.""" + settingsfile = sys.modules[settings.__module__].__file__ + localedir = os.path.join(os.path.dirname(settingsfile), "locale") + translation = self._new_gnu_trans(localedir) + self.merge(translation) + + def _add_installed_apps_translations(self): + """Merge translations from each installed app.""" + try: + app_configs = reversed(apps.get_app_configs()) + except AppRegistryNotReady: + raise AppRegistryNotReady( + "The translation infrastructure cannot be initialized before the " + "apps registry is ready. Check that you don't make non-lazy " + "gettext calls at import time." + ) + for app_config in app_configs: + localedir = os.path.join(app_config.path, "locale") + if os.path.exists(localedir): + translation = self._new_gnu_trans(localedir) + self.merge(translation) + + def _add_local_translations(self): + """Merge translations defined in LOCALE_PATHS.""" + for localedir in reversed(settings.LOCALE_PATHS): + translation = self._new_gnu_trans(localedir) + self.merge(translation) + + def _add_fallback(self, localedirs=None): + """Set the GNUTranslations() fallback with the default language.""" + # Don't set a fallback for the default language or any English variant + # (as it's empty, so it'll ALWAYS fall back to the default language) + if self.__language == settings.LANGUAGE_CODE or self.__language.startswith( + "en" + ): + return + if self.domain == "django": + # Get from cache + default_translation = translation(settings.LANGUAGE_CODE) + else: + default_translation = DjangoTranslation( + settings.LANGUAGE_CODE, domain=self.domain, localedirs=localedirs + ) + self.add_fallback(default_translation) + + def merge(self, other): + """Merge another translation into this catalog.""" + if not getattr(other, "_catalog", None): + return # NullTranslations() has no _catalog + if self._catalog is None: + # Take plural and _info from first catalog found (generally Django's). + self.plural = other.plural + self._info = other._info.copy() + self._catalog = TranslationCatalog(other) + else: + self._catalog.update(other) + if other._fallback: + self.add_fallback(other._fallback) + + def language(self): + """Return the translation language.""" + return self.__language + + def to_language(self): + """Return the translation language name.""" + return self.__to_language + + def ngettext(self, msgid1, msgid2, n): + try: + tmsg = self._catalog.plural(msgid1, n) + except KeyError: + if self._fallback: + return self._fallback.ngettext(msgid1, msgid2, n) + if n == 1: + tmsg = msgid1 + else: + tmsg = msgid2 + return tmsg + + +def translation(language): + """ + Return a translation object in the default 'django' domain. + """ + global _translations + if language not in _translations: + _translations[language] = DjangoTranslation(language) + return _translations[language] + + +def activate(language): + """ + Fetch the translation object for a given language and install it as the + current translation object for the current thread. + """ + if not language: + return + _active.value = translation(language) + + +def deactivate(): + """ + Uninstall the active translation object so that further _() calls resolve + to the default translation object. + """ + if hasattr(_active, "value"): + del _active.value + + +def deactivate_all(): + """ + Make the active translation object a NullTranslations() instance. This is + useful when we want delayed translations to appear as the original string + for some reason. + """ + _active.value = gettext_module.NullTranslations() + _active.value.to_language = lambda *args: None + + +def get_language(): + """Return the currently selected language.""" + t = getattr(_active, "value", None) + if t is not None: + try: + return t.to_language() + except AttributeError: + pass + # If we don't have a real translation object, assume it's the default language. + return settings.LANGUAGE_CODE + + +def get_language_bidi(): + """ + Return selected language's BiDi layout. + + * False = left-to-right layout + * True = right-to-left layout + """ + lang = get_language() + if lang is None: + return False + else: + base_lang = get_language().split("-")[0] + return base_lang in settings.LANGUAGES_BIDI + + +def catalog(): + """ + Return the current active catalog for further processing. + This can be used if you need to modify the catalog or want to access the + whole message catalog instead of just translating one string. + """ + global _default + + t = getattr(_active, "value", None) + if t is not None: + return t + if _default is None: + _default = translation(settings.LANGUAGE_CODE) + return _default + + +def gettext(message): + """ + Translate the 'message' string. It uses the current thread to find the + translation object to use. If no current translation is activated, the + message will be run through the default translation object. + """ + global _default + + eol_message = message.replace("\r\n", "\n").replace("\r", "\n") + + if eol_message: + _default = _default or translation(settings.LANGUAGE_CODE) + translation_object = getattr(_active, "value", _default) + + result = translation_object.gettext(eol_message) + else: + # Return an empty value of the corresponding type if an empty message + # is given, instead of metadata, which is the default gettext behavior. + result = type(message)("") + + if isinstance(message, SafeData): + return mark_safe(result) + + return result + + +def pgettext(context, message): + msg_with_ctxt = "%s%s%s" % (context, CONTEXT_SEPARATOR, message) + result = gettext(msg_with_ctxt) + if CONTEXT_SEPARATOR in result: + # Translation not found + result = message + elif isinstance(message, SafeData): + result = mark_safe(result) + return result + + +def gettext_noop(message): + """ + Mark strings for translation but don't translate them now. This can be + used to store strings in global variables that should stay in the base + language (because they might be used externally) and will be translated + later. + """ + return message + + +def do_ntranslate(singular, plural, number, translation_function): + global _default + + t = getattr(_active, "value", None) + if t is not None: + return getattr(t, translation_function)(singular, plural, number) + if _default is None: + _default = translation(settings.LANGUAGE_CODE) + return getattr(_default, translation_function)(singular, plural, number) + + +def ngettext(singular, plural, number): + """ + Return a string of the translation of either the singular or plural, + based on the number. + """ + return do_ntranslate(singular, plural, number, "ngettext") + + +def npgettext(context, singular, plural, number): + msgs_with_ctxt = ( + "%s%s%s" % (context, CONTEXT_SEPARATOR, singular), + "%s%s%s" % (context, CONTEXT_SEPARATOR, plural), + number, + ) + result = ngettext(*msgs_with_ctxt) + if CONTEXT_SEPARATOR in result: + # Translation not found + result = ngettext(singular, plural, number) + return result + + +def all_locale_paths(): + """ + Return a list of paths to user-provides languages files. + """ + globalpath = os.path.join( + os.path.dirname(sys.modules[settings.__module__].__file__), "locale" + ) + app_paths = [] + for app_config in apps.get_app_configs(): + locale_path = os.path.join(app_config.path, "locale") + if os.path.exists(locale_path): + app_paths.append(locale_path) + return [globalpath, *settings.LOCALE_PATHS, *app_paths] + + +@functools.lru_cache(maxsize=1000) +def check_for_language(lang_code): + """ + Check whether there is a global language file for the given language + code. This is used to decide whether a user-provided language is + available. + + lru_cache should have a maxsize to prevent from memory exhaustion attacks, + as the provided language codes are taken from the HTTP request. See also + <https://www.djangoproject.com/weblog/2007/oct/26/security-fix/>. + """ + # First, a quick check to make sure lang_code is well-formed (#21458) + if lang_code is None or not language_code_re.search(lang_code): + return False + return any( + gettext_module.find("django", path, [to_locale(lang_code)]) is not None + for path in all_locale_paths() + ) + + +@functools.lru_cache +def get_languages(): + """ + Cache of settings.LANGUAGES in a dictionary for easy lookups by key. + Convert keys to lowercase as they should be treated as case-insensitive. + """ + return {key.lower(): value for key, value in dict(settings.LANGUAGES).items()} + + +@functools.lru_cache(maxsize=1000) +def get_supported_language_variant(lang_code, strict=False): + """ + Return the language code that's listed in supported languages, possibly + selecting a more generic variant. Raise LookupError if nothing is found. + + If `strict` is False (the default), look for a country-specific variant + when neither the language code nor its generic variant is found. + + lru_cache should have a maxsize to prevent from memory exhaustion attacks, + as the provided language codes are taken from the HTTP request. See also + <https://www.djangoproject.com/weblog/2007/oct/26/security-fix/>. + """ + if lang_code: + # If 'zh-hant-tw' is not supported, try special fallback or subsequent + # language codes i.e. 'zh-hant' and 'zh'. + possible_lang_codes = [lang_code] + try: + possible_lang_codes.extend(LANG_INFO[lang_code]["fallback"]) + except KeyError: + pass + i = None + while (i := lang_code.rfind("-", 0, i)) > -1: + possible_lang_codes.append(lang_code[:i]) + generic_lang_code = possible_lang_codes[-1] + supported_lang_codes = get_languages() + + for code in possible_lang_codes: + if code.lower() in supported_lang_codes and check_for_language(code): + return code + if not strict: + # if fr-fr is not supported, try fr-ca. + for supported_code in supported_lang_codes: + if supported_code.startswith(generic_lang_code + "-"): + return supported_code + raise LookupError(lang_code) + + +def get_language_from_path(path, strict=False): + """ + Return the language code if there's a valid language code found in `path`. + + If `strict` is False (the default), look for a country-specific variant + when neither the language code nor its generic variant is found. + """ + regex_match = language_code_prefix_re.match(path) + if not regex_match: + return None + lang_code = regex_match[1] + try: + return get_supported_language_variant(lang_code, strict=strict) + except LookupError: + return None + + +def get_language_from_request(request, check_path=False): + """ + Analyze the request to find what language the user wants the system to + show. Only languages listed in settings.LANGUAGES are taken into account. + If the user requests a sublanguage where we have a main language, we send + out the main language. + + If check_path is True, the URL path prefix will be checked for a language + code, otherwise this is skipped for backwards compatibility. + """ + if check_path: + lang_code = get_language_from_path(request.path_info) + if lang_code is not None: + return lang_code + + lang_code = request.COOKIES.get(settings.LANGUAGE_COOKIE_NAME) + if ( + lang_code is not None + and lang_code in get_languages() + and check_for_language(lang_code) + ): + return lang_code + + try: + return get_supported_language_variant(lang_code) + except LookupError: + pass + + accept = request.META.get("HTTP_ACCEPT_LANGUAGE", "") + for accept_lang, unused in parse_accept_lang_header(accept): + if accept_lang == "*": + break + + if not language_code_re.search(accept_lang): + continue + + try: + return get_supported_language_variant(accept_lang) + except LookupError: + continue + + try: + return get_supported_language_variant(settings.LANGUAGE_CODE) + except LookupError: + return settings.LANGUAGE_CODE + + +@functools.lru_cache(maxsize=1000) +def _parse_accept_lang_header(lang_string): + """ + Parse the lang_string, which is the body of an HTTP Accept-Language + header, and return a tuple of (lang, q-value), ordered by 'q' values. + + Return an empty tuple if there are any format errors in lang_string. + """ + result = [] + pieces = accept_language_re.split(lang_string.lower()) + if pieces[-1]: + return () + for i in range(0, len(pieces) - 1, 3): + first, lang, priority = pieces[i : i + 3] + if first: + return () + if priority: + priority = float(priority) + else: + priority = 1.0 + result.append((lang, priority)) + result.sort(key=lambda k: k[1], reverse=True) + return tuple(result) + + +def parse_accept_lang_header(lang_string): + """ + Parse the value of the Accept-Language header up to a maximum length. + + The value of the header is truncated to a maximum length to avoid potential + denial of service and memory exhaustion attacks. Excessive memory could be + used if the raw value is very large as it would be cached due to the use of + functools.lru_cache() to avoid repetitive parsing of common header values. + """ + # If the header value doesn't exceed the maximum allowed length, parse it. + if len(lang_string) <= ACCEPT_LANGUAGE_HEADER_MAX_LENGTH: + return _parse_accept_lang_header(lang_string) + + # If there is at least one comma in the value, parse up to the last comma + # before the max length, skipping any truncated parts at the end of the + # header value. + if (index := lang_string.rfind(",", 0, ACCEPT_LANGUAGE_HEADER_MAX_LENGTH)) > 0: + return _parse_accept_lang_header(lang_string[:index]) + + # Don't attempt to parse if there is only one language-range value which is + # longer than the maximum allowed length and so truncated. + return () diff --git a/virt/lib/python3.9/site-packages/google/protobuf/descriptor_pb2 3.py b/virt/lib/python3.9/site-packages/google/protobuf/descriptor_pb2 3.py new file mode 100644 index 00000000..128fa311 --- /dev/null +++ b/virt/lib/python3.9/site-packages/google/protobuf/descriptor_pb2 3.py @@ -0,0 +1,1925 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/descriptor.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR = _descriptor.FileDescriptor( + name='google/protobuf/descriptor.proto', + package='google.protobuf', + syntax='proto2', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n google/protobuf/descriptor.proto\x12\x0fgoogle.protobuf\"M\n\x11\x46ileDescriptorSet\x12\x38\n\x04\x66ile\x18\x01 \x03(\x0b\x32$.google.protobuf.FileDescriptorProtoR\x04\x66ile\"\xe4\x04\n\x13\x46ileDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07package\x18\x02 \x01(\tR\x07package\x12\x1e\n\ndependency\x18\x03 \x03(\tR\ndependency\x12+\n\x11public_dependency\x18\n \x03(\x05R\x10publicDependency\x12\'\n\x0fweak_dependency\x18\x0b \x03(\x05R\x0eweakDependency\x12\x43\n\x0cmessage_type\x18\x04 \x03(\x0b\x32 .google.protobuf.DescriptorProtoR\x0bmessageType\x12\x41\n\tenum_type\x18\x05 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProtoR\x08\x65numType\x12\x41\n\x07service\x18\x06 \x03(\x0b\x32\'.google.protobuf.ServiceDescriptorProtoR\x07service\x12\x43\n\textension\x18\x07 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProtoR\textension\x12\x36\n\x07options\x18\x08 \x01(\x0b\x32\x1c.google.protobuf.FileOptionsR\x07options\x12I\n\x10source_code_info\x18\t \x01(\x0b\x32\x1f.google.protobuf.SourceCodeInfoR\x0esourceCodeInfo\x12\x16\n\x06syntax\x18\x0c \x01(\tR\x06syntax\"\xb9\x06\n\x0f\x44\x65scriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12;\n\x05\x66ield\x18\x02 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProtoR\x05\x66ield\x12\x43\n\textension\x18\x06 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProtoR\textension\x12\x41\n\x0bnested_type\x18\x03 \x03(\x0b\x32 .google.protobuf.DescriptorProtoR\nnestedType\x12\x41\n\tenum_type\x18\x04 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProtoR\x08\x65numType\x12X\n\x0f\x65xtension_range\x18\x05 \x03(\x0b\x32/.google.protobuf.DescriptorProto.ExtensionRangeR\x0e\x65xtensionRange\x12\x44\n\noneof_decl\x18\x08 \x03(\x0b\x32%.google.protobuf.OneofDescriptorProtoR\toneofDecl\x12\x39\n\x07options\x18\x07 \x01(\x0b\x32\x1f.google.protobuf.MessageOptionsR\x07options\x12U\n\x0ereserved_range\x18\t \x03(\x0b\x32..google.protobuf.DescriptorProto.ReservedRangeR\rreservedRange\x12#\n\rreserved_name\x18\n \x03(\tR\x0creservedName\x1az\n\x0e\x45xtensionRange\x12\x14\n\x05start\x18\x01 \x01(\x05R\x05start\x12\x10\n\x03\x65nd\x18\x02 \x01(\x05R\x03\x65nd\x12@\n\x07options\x18\x03 \x01(\x0b\x32&.google.protobuf.ExtensionRangeOptionsR\x07options\x1a\x37\n\rReservedRange\x12\x14\n\x05start\x18\x01 \x01(\x05R\x05start\x12\x10\n\x03\x65nd\x18\x02 \x01(\x05R\x03\x65nd\"|\n\x15\x45xtensionRangeOptions\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xc1\x06\n\x14\x46ieldDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n\x06number\x18\x03 \x01(\x05R\x06number\x12\x41\n\x05label\x18\x04 \x01(\x0e\x32+.google.protobuf.FieldDescriptorProto.LabelR\x05label\x12>\n\x04type\x18\x05 \x01(\x0e\x32*.google.protobuf.FieldDescriptorProto.TypeR\x04type\x12\x1b\n\ttype_name\x18\x06 \x01(\tR\x08typeName\x12\x1a\n\x08\x65xtendee\x18\x02 \x01(\tR\x08\x65xtendee\x12#\n\rdefault_value\x18\x07 \x01(\tR\x0c\x64\x65\x66\x61ultValue\x12\x1f\n\x0boneof_index\x18\t \x01(\x05R\noneofIndex\x12\x1b\n\tjson_name\x18\n \x01(\tR\x08jsonName\x12\x37\n\x07options\x18\x08 \x01(\x0b\x32\x1d.google.protobuf.FieldOptionsR\x07options\x12\'\n\x0fproto3_optional\x18\x11 \x01(\x08R\x0eproto3Optional\"\xb6\x02\n\x04Type\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"C\n\x05Label\x12\x12\n\x0eLABEL_OPTIONAL\x10\x01\x12\x12\n\x0eLABEL_REQUIRED\x10\x02\x12\x12\n\x0eLABEL_REPEATED\x10\x03\"c\n\x14OneofDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x37\n\x07options\x18\x02 \x01(\x0b\x32\x1d.google.protobuf.OneofOptionsR\x07options\"\xe3\x02\n\x13\x45numDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12?\n\x05value\x18\x02 \x03(\x0b\x32).google.protobuf.EnumValueDescriptorProtoR\x05value\x12\x36\n\x07options\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.EnumOptionsR\x07options\x12]\n\x0ereserved_range\x18\x04 \x03(\x0b\x32\x36.google.protobuf.EnumDescriptorProto.EnumReservedRangeR\rreservedRange\x12#\n\rreserved_name\x18\x05 \x03(\tR\x0creservedName\x1a;\n\x11\x45numReservedRange\x12\x14\n\x05start\x18\x01 \x01(\x05R\x05start\x12\x10\n\x03\x65nd\x18\x02 \x01(\x05R\x03\x65nd\"\x83\x01\n\x18\x45numValueDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n\x06number\x18\x02 \x01(\x05R\x06number\x12;\n\x07options\x18\x03 \x01(\x0b\x32!.google.protobuf.EnumValueOptionsR\x07options\"\xa7\x01\n\x16ServiceDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12>\n\x06method\x18\x02 \x03(\x0b\x32&.google.protobuf.MethodDescriptorProtoR\x06method\x12\x39\n\x07options\x18\x03 \x01(\x0b\x32\x1f.google.protobuf.ServiceOptionsR\x07options\"\x89\x02\n\x15MethodDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1d\n\ninput_type\x18\x02 \x01(\tR\tinputType\x12\x1f\n\x0boutput_type\x18\x03 \x01(\tR\noutputType\x12\x38\n\x07options\x18\x04 \x01(\x0b\x32\x1e.google.protobuf.MethodOptionsR\x07options\x12\x30\n\x10\x63lient_streaming\x18\x05 \x01(\x08:\x05\x66\x61lseR\x0f\x63lientStreaming\x12\x30\n\x10server_streaming\x18\x06 \x01(\x08:\x05\x66\x61lseR\x0fserverStreaming\"\x91\t\n\x0b\x46ileOptions\x12!\n\x0cjava_package\x18\x01 \x01(\tR\x0bjavaPackage\x12\x30\n\x14java_outer_classname\x18\x08 \x01(\tR\x12javaOuterClassname\x12\x35\n\x13java_multiple_files\x18\n \x01(\x08:\x05\x66\x61lseR\x11javaMultipleFiles\x12\x44\n\x1djava_generate_equals_and_hash\x18\x14 \x01(\x08\x42\x02\x18\x01R\x19javaGenerateEqualsAndHash\x12:\n\x16java_string_check_utf8\x18\x1b \x01(\x08:\x05\x66\x61lseR\x13javaStringCheckUtf8\x12S\n\x0coptimize_for\x18\t \x01(\x0e\x32).google.protobuf.FileOptions.OptimizeMode:\x05SPEEDR\x0boptimizeFor\x12\x1d\n\ngo_package\x18\x0b \x01(\tR\tgoPackage\x12\x35\n\x13\x63\x63_generic_services\x18\x10 \x01(\x08:\x05\x66\x61lseR\x11\x63\x63GenericServices\x12\x39\n\x15java_generic_services\x18\x11 \x01(\x08:\x05\x66\x61lseR\x13javaGenericServices\x12\x35\n\x13py_generic_services\x18\x12 \x01(\x08:\x05\x66\x61lseR\x11pyGenericServices\x12\x37\n\x14php_generic_services\x18* \x01(\x08:\x05\x66\x61lseR\x12phpGenericServices\x12%\n\ndeprecated\x18\x17 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12.\n\x10\x63\x63_enable_arenas\x18\x1f \x01(\x08:\x04trueR\x0e\x63\x63\x45nableArenas\x12*\n\x11objc_class_prefix\x18$ \x01(\tR\x0fobjcClassPrefix\x12)\n\x10\x63sharp_namespace\x18% \x01(\tR\x0f\x63sharpNamespace\x12!\n\x0cswift_prefix\x18\' \x01(\tR\x0bswiftPrefix\x12(\n\x10php_class_prefix\x18( \x01(\tR\x0ephpClassPrefix\x12#\n\rphp_namespace\x18) \x01(\tR\x0cphpNamespace\x12\x34\n\x16php_metadata_namespace\x18, \x01(\tR\x14phpMetadataNamespace\x12!\n\x0cruby_package\x18- \x01(\tR\x0brubyPackage\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption\":\n\x0cOptimizeMode\x12\t\n\x05SPEED\x10\x01\x12\r\n\tCODE_SIZE\x10\x02\x12\x10\n\x0cLITE_RUNTIME\x10\x03*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08&\x10\'\"\xe3\x02\n\x0eMessageOptions\x12<\n\x17message_set_wire_format\x18\x01 \x01(\x08:\x05\x66\x61lseR\x14messageSetWireFormat\x12L\n\x1fno_standard_descriptor_accessor\x18\x02 \x01(\x08:\x05\x66\x61lseR\x1cnoStandardDescriptorAccessor\x12%\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12\x1b\n\tmap_entry\x18\x07 \x01(\x08R\x08mapEntry\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x04\x10\x05J\x04\x08\x05\x10\x06J\x04\x08\x06\x10\x07J\x04\x08\x08\x10\tJ\x04\x08\t\x10\n\"\x92\x04\n\x0c\x46ieldOptions\x12\x41\n\x05\x63type\x18\x01 \x01(\x0e\x32#.google.protobuf.FieldOptions.CType:\x06STRINGR\x05\x63type\x12\x16\n\x06packed\x18\x02 \x01(\x08R\x06packed\x12G\n\x06jstype\x18\x06 \x01(\x0e\x32$.google.protobuf.FieldOptions.JSType:\tJS_NORMALR\x06jstype\x12\x19\n\x04lazy\x18\x05 \x01(\x08:\x05\x66\x61lseR\x04lazy\x12.\n\x0funverified_lazy\x18\x0f \x01(\x08:\x05\x66\x61lseR\x0eunverifiedLazy\x12%\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12\x19\n\x04weak\x18\n \x01(\x08:\x05\x66\x61lseR\x04weak\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption\"/\n\x05\x43Type\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x43ORD\x10\x01\x12\x10\n\x0cSTRING_PIECE\x10\x02\"5\n\x06JSType\x12\r\n\tJS_NORMAL\x10\x00\x12\r\n\tJS_STRING\x10\x01\x12\r\n\tJS_NUMBER\x10\x02*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x04\x10\x05\"s\n\x0cOneofOptions\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xc0\x01\n\x0b\x45numOptions\x12\x1f\n\x0b\x61llow_alias\x18\x02 \x01(\x08R\nallowAlias\x12%\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x05\x10\x06\"\x9e\x01\n\x10\x45numValueOptions\x12%\n\ndeprecated\x18\x01 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x9c\x01\n\x0eServiceOptions\x12%\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xe0\x02\n\rMethodOptions\x12%\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12q\n\x11idempotency_level\x18\" \x01(\x0e\x32/.google.protobuf.MethodOptions.IdempotencyLevel:\x13IDEMPOTENCY_UNKNOWNR\x10idempotencyLevel\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption\"P\n\x10IdempotencyLevel\x12\x17\n\x13IDEMPOTENCY_UNKNOWN\x10\x00\x12\x13\n\x0fNO_SIDE_EFFECTS\x10\x01\x12\x0e\n\nIDEMPOTENT\x10\x02*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x9a\x03\n\x13UninterpretedOption\x12\x41\n\x04name\x18\x02 \x03(\x0b\x32-.google.protobuf.UninterpretedOption.NamePartR\x04name\x12)\n\x10identifier_value\x18\x03 \x01(\tR\x0fidentifierValue\x12,\n\x12positive_int_value\x18\x04 \x01(\x04R\x10positiveIntValue\x12,\n\x12negative_int_value\x18\x05 \x01(\x03R\x10negativeIntValue\x12!\n\x0c\x64ouble_value\x18\x06 \x01(\x01R\x0b\x64oubleValue\x12!\n\x0cstring_value\x18\x07 \x01(\x0cR\x0bstringValue\x12\'\n\x0f\x61ggregate_value\x18\x08 \x01(\tR\x0e\x61ggregateValue\x1aJ\n\x08NamePart\x12\x1b\n\tname_part\x18\x01 \x02(\tR\x08namePart\x12!\n\x0cis_extension\x18\x02 \x02(\x08R\x0bisExtension\"\xa7\x02\n\x0eSourceCodeInfo\x12\x44\n\x08location\x18\x01 \x03(\x0b\x32(.google.protobuf.SourceCodeInfo.LocationR\x08location\x1a\xce\x01\n\x08Location\x12\x16\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01R\x04path\x12\x16\n\x04span\x18\x02 \x03(\x05\x42\x02\x10\x01R\x04span\x12)\n\x10leading_comments\x18\x03 \x01(\tR\x0fleadingComments\x12+\n\x11trailing_comments\x18\x04 \x01(\tR\x10trailingComments\x12:\n\x19leading_detached_comments\x18\x06 \x03(\tR\x17leadingDetachedComments\"\xd1\x01\n\x11GeneratedCodeInfo\x12M\n\nannotation\x18\x01 \x03(\x0b\x32-.google.protobuf.GeneratedCodeInfo.AnnotationR\nannotation\x1am\n\nAnnotation\x12\x16\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01R\x04path\x12\x1f\n\x0bsource_file\x18\x02 \x01(\tR\nsourceFile\x12\x14\n\x05\x62\x65gin\x18\x03 \x01(\x05R\x05\x62\x65gin\x12\x10\n\x03\x65nd\x18\x04 \x01(\x05R\x03\x65ndB~\n\x13\x63om.google.protobufB\x10\x44\x65scriptorProtosH\x01Z-google.golang.org/protobuf/types/descriptorpb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1aGoogle.Protobuf.Reflection' + ) +else: + DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n google/protobuf/descriptor.proto\x12\x0fgoogle.protobuf\"M\n\x11\x46ileDescriptorSet\x12\x38\n\x04\x66ile\x18\x01 \x03(\x0b\x32$.google.protobuf.FileDescriptorProtoR\x04\x66ile\"\xe4\x04\n\x13\x46ileDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07package\x18\x02 \x01(\tR\x07package\x12\x1e\n\ndependency\x18\x03 \x03(\tR\ndependency\x12+\n\x11public_dependency\x18\n \x03(\x05R\x10publicDependency\x12\'\n\x0fweak_dependency\x18\x0b \x03(\x05R\x0eweakDependency\x12\x43\n\x0cmessage_type\x18\x04 \x03(\x0b\x32 .google.protobuf.DescriptorProtoR\x0bmessageType\x12\x41\n\tenum_type\x18\x05 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProtoR\x08\x65numType\x12\x41\n\x07service\x18\x06 \x03(\x0b\x32\'.google.protobuf.ServiceDescriptorProtoR\x07service\x12\x43\n\textension\x18\x07 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProtoR\textension\x12\x36\n\x07options\x18\x08 \x01(\x0b\x32\x1c.google.protobuf.FileOptionsR\x07options\x12I\n\x10source_code_info\x18\t \x01(\x0b\x32\x1f.google.protobuf.SourceCodeInfoR\x0esourceCodeInfo\x12\x16\n\x06syntax\x18\x0c \x01(\tR\x06syntax\"\xb9\x06\n\x0f\x44\x65scriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12;\n\x05\x66ield\x18\x02 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProtoR\x05\x66ield\x12\x43\n\textension\x18\x06 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProtoR\textension\x12\x41\n\x0bnested_type\x18\x03 \x03(\x0b\x32 .google.protobuf.DescriptorProtoR\nnestedType\x12\x41\n\tenum_type\x18\x04 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProtoR\x08\x65numType\x12X\n\x0f\x65xtension_range\x18\x05 \x03(\x0b\x32/.google.protobuf.DescriptorProto.ExtensionRangeR\x0e\x65xtensionRange\x12\x44\n\noneof_decl\x18\x08 \x03(\x0b\x32%.google.protobuf.OneofDescriptorProtoR\toneofDecl\x12\x39\n\x07options\x18\x07 \x01(\x0b\x32\x1f.google.protobuf.MessageOptionsR\x07options\x12U\n\x0ereserved_range\x18\t \x03(\x0b\x32..google.protobuf.DescriptorProto.ReservedRangeR\rreservedRange\x12#\n\rreserved_name\x18\n \x03(\tR\x0creservedName\x1az\n\x0e\x45xtensionRange\x12\x14\n\x05start\x18\x01 \x01(\x05R\x05start\x12\x10\n\x03\x65nd\x18\x02 \x01(\x05R\x03\x65nd\x12@\n\x07options\x18\x03 \x01(\x0b\x32&.google.protobuf.ExtensionRangeOptionsR\x07options\x1a\x37\n\rReservedRange\x12\x14\n\x05start\x18\x01 \x01(\x05R\x05start\x12\x10\n\x03\x65nd\x18\x02 \x01(\x05R\x03\x65nd\"|\n\x15\x45xtensionRangeOptions\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xc1\x06\n\x14\x46ieldDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n\x06number\x18\x03 \x01(\x05R\x06number\x12\x41\n\x05label\x18\x04 \x01(\x0e\x32+.google.protobuf.FieldDescriptorProto.LabelR\x05label\x12>\n\x04type\x18\x05 \x01(\x0e\x32*.google.protobuf.FieldDescriptorProto.TypeR\x04type\x12\x1b\n\ttype_name\x18\x06 \x01(\tR\x08typeName\x12\x1a\n\x08\x65xtendee\x18\x02 \x01(\tR\x08\x65xtendee\x12#\n\rdefault_value\x18\x07 \x01(\tR\x0c\x64\x65\x66\x61ultValue\x12\x1f\n\x0boneof_index\x18\t \x01(\x05R\noneofIndex\x12\x1b\n\tjson_name\x18\n \x01(\tR\x08jsonName\x12\x37\n\x07options\x18\x08 \x01(\x0b\x32\x1d.google.protobuf.FieldOptionsR\x07options\x12\'\n\x0fproto3_optional\x18\x11 \x01(\x08R\x0eproto3Optional\"\xb6\x02\n\x04Type\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"C\n\x05Label\x12\x12\n\x0eLABEL_OPTIONAL\x10\x01\x12\x12\n\x0eLABEL_REQUIRED\x10\x02\x12\x12\n\x0eLABEL_REPEATED\x10\x03\"c\n\x14OneofDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x37\n\x07options\x18\x02 \x01(\x0b\x32\x1d.google.protobuf.OneofOptionsR\x07options\"\xe3\x02\n\x13\x45numDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12?\n\x05value\x18\x02 \x03(\x0b\x32).google.protobuf.EnumValueDescriptorProtoR\x05value\x12\x36\n\x07options\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.EnumOptionsR\x07options\x12]\n\x0ereserved_range\x18\x04 \x03(\x0b\x32\x36.google.protobuf.EnumDescriptorProto.EnumReservedRangeR\rreservedRange\x12#\n\rreserved_name\x18\x05 \x03(\tR\x0creservedName\x1a;\n\x11\x45numReservedRange\x12\x14\n\x05start\x18\x01 \x01(\x05R\x05start\x12\x10\n\x03\x65nd\x18\x02 \x01(\x05R\x03\x65nd\"\x83\x01\n\x18\x45numValueDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n\x06number\x18\x02 \x01(\x05R\x06number\x12;\n\x07options\x18\x03 \x01(\x0b\x32!.google.protobuf.EnumValueOptionsR\x07options\"\xa7\x01\n\x16ServiceDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12>\n\x06method\x18\x02 \x03(\x0b\x32&.google.protobuf.MethodDescriptorProtoR\x06method\x12\x39\n\x07options\x18\x03 \x01(\x0b\x32\x1f.google.protobuf.ServiceOptionsR\x07options\"\x89\x02\n\x15MethodDescriptorProto\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1d\n\ninput_type\x18\x02 \x01(\tR\tinputType\x12\x1f\n\x0boutput_type\x18\x03 \x01(\tR\noutputType\x12\x38\n\x07options\x18\x04 \x01(\x0b\x32\x1e.google.protobuf.MethodOptionsR\x07options\x12\x30\n\x10\x63lient_streaming\x18\x05 \x01(\x08:\x05\x66\x61lseR\x0f\x63lientStreaming\x12\x30\n\x10server_streaming\x18\x06 \x01(\x08:\x05\x66\x61lseR\x0fserverStreaming\"\x91\t\n\x0b\x46ileOptions\x12!\n\x0cjava_package\x18\x01 \x01(\tR\x0bjavaPackage\x12\x30\n\x14java_outer_classname\x18\x08 \x01(\tR\x12javaOuterClassname\x12\x35\n\x13java_multiple_files\x18\n \x01(\x08:\x05\x66\x61lseR\x11javaMultipleFiles\x12\x44\n\x1djava_generate_equals_and_hash\x18\x14 \x01(\x08\x42\x02\x18\x01R\x19javaGenerateEqualsAndHash\x12:\n\x16java_string_check_utf8\x18\x1b \x01(\x08:\x05\x66\x61lseR\x13javaStringCheckUtf8\x12S\n\x0coptimize_for\x18\t \x01(\x0e\x32).google.protobuf.FileOptions.OptimizeMode:\x05SPEEDR\x0boptimizeFor\x12\x1d\n\ngo_package\x18\x0b \x01(\tR\tgoPackage\x12\x35\n\x13\x63\x63_generic_services\x18\x10 \x01(\x08:\x05\x66\x61lseR\x11\x63\x63GenericServices\x12\x39\n\x15java_generic_services\x18\x11 \x01(\x08:\x05\x66\x61lseR\x13javaGenericServices\x12\x35\n\x13py_generic_services\x18\x12 \x01(\x08:\x05\x66\x61lseR\x11pyGenericServices\x12\x37\n\x14php_generic_services\x18* \x01(\x08:\x05\x66\x61lseR\x12phpGenericServices\x12%\n\ndeprecated\x18\x17 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12.\n\x10\x63\x63_enable_arenas\x18\x1f \x01(\x08:\x04trueR\x0e\x63\x63\x45nableArenas\x12*\n\x11objc_class_prefix\x18$ \x01(\tR\x0fobjcClassPrefix\x12)\n\x10\x63sharp_namespace\x18% \x01(\tR\x0f\x63sharpNamespace\x12!\n\x0cswift_prefix\x18\' \x01(\tR\x0bswiftPrefix\x12(\n\x10php_class_prefix\x18( \x01(\tR\x0ephpClassPrefix\x12#\n\rphp_namespace\x18) \x01(\tR\x0cphpNamespace\x12\x34\n\x16php_metadata_namespace\x18, \x01(\tR\x14phpMetadataNamespace\x12!\n\x0cruby_package\x18- \x01(\tR\x0brubyPackage\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption\":\n\x0cOptimizeMode\x12\t\n\x05SPEED\x10\x01\x12\r\n\tCODE_SIZE\x10\x02\x12\x10\n\x0cLITE_RUNTIME\x10\x03*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08&\x10\'\"\xe3\x02\n\x0eMessageOptions\x12<\n\x17message_set_wire_format\x18\x01 \x01(\x08:\x05\x66\x61lseR\x14messageSetWireFormat\x12L\n\x1fno_standard_descriptor_accessor\x18\x02 \x01(\x08:\x05\x66\x61lseR\x1cnoStandardDescriptorAccessor\x12%\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12\x1b\n\tmap_entry\x18\x07 \x01(\x08R\x08mapEntry\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x04\x10\x05J\x04\x08\x05\x10\x06J\x04\x08\x06\x10\x07J\x04\x08\x08\x10\tJ\x04\x08\t\x10\n\"\x92\x04\n\x0c\x46ieldOptions\x12\x41\n\x05\x63type\x18\x01 \x01(\x0e\x32#.google.protobuf.FieldOptions.CType:\x06STRINGR\x05\x63type\x12\x16\n\x06packed\x18\x02 \x01(\x08R\x06packed\x12G\n\x06jstype\x18\x06 \x01(\x0e\x32$.google.protobuf.FieldOptions.JSType:\tJS_NORMALR\x06jstype\x12\x19\n\x04lazy\x18\x05 \x01(\x08:\x05\x66\x61lseR\x04lazy\x12.\n\x0funverified_lazy\x18\x0f \x01(\x08:\x05\x66\x61lseR\x0eunverifiedLazy\x12%\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12\x19\n\x04weak\x18\n \x01(\x08:\x05\x66\x61lseR\x04weak\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption\"/\n\x05\x43Type\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x43ORD\x10\x01\x12\x10\n\x0cSTRING_PIECE\x10\x02\"5\n\x06JSType\x12\r\n\tJS_NORMAL\x10\x00\x12\r\n\tJS_STRING\x10\x01\x12\r\n\tJS_NUMBER\x10\x02*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x04\x10\x05\"s\n\x0cOneofOptions\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xc0\x01\n\x0b\x45numOptions\x12\x1f\n\x0b\x61llow_alias\x18\x02 \x01(\x08R\nallowAlias\x12%\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x05\x10\x06\"\x9e\x01\n\x10\x45numValueOptions\x12%\n\ndeprecated\x18\x01 \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x9c\x01\n\x0eServiceOptions\x12%\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xe0\x02\n\rMethodOptions\x12%\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lseR\ndeprecated\x12q\n\x11idempotency_level\x18\" \x01(\x0e\x32/.google.protobuf.MethodOptions.IdempotencyLevel:\x13IDEMPOTENCY_UNKNOWNR\x10idempotencyLevel\x12X\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOptionR\x13uninterpretedOption\"P\n\x10IdempotencyLevel\x12\x17\n\x13IDEMPOTENCY_UNKNOWN\x10\x00\x12\x13\n\x0fNO_SIDE_EFFECTS\x10\x01\x12\x0e\n\nIDEMPOTENT\x10\x02*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x9a\x03\n\x13UninterpretedOption\x12\x41\n\x04name\x18\x02 \x03(\x0b\x32-.google.protobuf.UninterpretedOption.NamePartR\x04name\x12)\n\x10identifier_value\x18\x03 \x01(\tR\x0fidentifierValue\x12,\n\x12positive_int_value\x18\x04 \x01(\x04R\x10positiveIntValue\x12,\n\x12negative_int_value\x18\x05 \x01(\x03R\x10negativeIntValue\x12!\n\x0c\x64ouble_value\x18\x06 \x01(\x01R\x0b\x64oubleValue\x12!\n\x0cstring_value\x18\x07 \x01(\x0cR\x0bstringValue\x12\'\n\x0f\x61ggregate_value\x18\x08 \x01(\tR\x0e\x61ggregateValue\x1aJ\n\x08NamePart\x12\x1b\n\tname_part\x18\x01 \x02(\tR\x08namePart\x12!\n\x0cis_extension\x18\x02 \x02(\x08R\x0bisExtension\"\xa7\x02\n\x0eSourceCodeInfo\x12\x44\n\x08location\x18\x01 \x03(\x0b\x32(.google.protobuf.SourceCodeInfo.LocationR\x08location\x1a\xce\x01\n\x08Location\x12\x16\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01R\x04path\x12\x16\n\x04span\x18\x02 \x03(\x05\x42\x02\x10\x01R\x04span\x12)\n\x10leading_comments\x18\x03 \x01(\tR\x0fleadingComments\x12+\n\x11trailing_comments\x18\x04 \x01(\tR\x10trailingComments\x12:\n\x19leading_detached_comments\x18\x06 \x03(\tR\x17leadingDetachedComments\"\xd1\x01\n\x11GeneratedCodeInfo\x12M\n\nannotation\x18\x01 \x03(\x0b\x32-.google.protobuf.GeneratedCodeInfo.AnnotationR\nannotation\x1am\n\nAnnotation\x12\x16\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01R\x04path\x12\x1f\n\x0bsource_file\x18\x02 \x01(\tR\nsourceFile\x12\x14\n\x05\x62\x65gin\x18\x03 \x01(\x05R\x05\x62\x65gin\x12\x10\n\x03\x65nd\x18\x04 \x01(\x05R\x03\x65ndB~\n\x13\x63om.google.protobufB\x10\x44\x65scriptorProtosH\x01Z-google.golang.org/protobuf/types/descriptorpb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1aGoogle.Protobuf.Reflection') + +if _descriptor._USE_C_DESCRIPTORS == False: + _FIELDDESCRIPTORPROTO_TYPE = _descriptor.EnumDescriptor( + name='Type', + full_name='google.protobuf.FieldDescriptorProto.Type', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='TYPE_DOUBLE', index=0, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_FLOAT', index=1, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_INT64', index=2, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_UINT64', index=3, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_INT32', index=4, number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_FIXED64', index=5, number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_FIXED32', index=6, number=7, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_BOOL', index=7, number=8, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_STRING', index=8, number=9, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_GROUP', index=9, number=10, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_MESSAGE', index=10, number=11, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_BYTES', index=11, number=12, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_UINT32', index=12, number=13, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_ENUM', index=13, number=14, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_SFIXED32', index=14, number=15, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_SFIXED64', index=15, number=16, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_SINT32', index=16, number=17, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_SINT64', index=17, number=18, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FIELDDESCRIPTORPROTO_TYPE) + + _FIELDDESCRIPTORPROTO_LABEL = _descriptor.EnumDescriptor( + name='Label', + full_name='google.protobuf.FieldDescriptorProto.Label', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='LABEL_OPTIONAL', index=0, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='LABEL_REQUIRED', index=1, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='LABEL_REPEATED', index=2, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FIELDDESCRIPTORPROTO_LABEL) + + _FILEOPTIONS_OPTIMIZEMODE = _descriptor.EnumDescriptor( + name='OptimizeMode', + full_name='google.protobuf.FileOptions.OptimizeMode', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SPEED', index=0, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CODE_SIZE', index=1, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='LITE_RUNTIME', index=2, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FILEOPTIONS_OPTIMIZEMODE) + + _FIELDOPTIONS_CTYPE = _descriptor.EnumDescriptor( + name='CType', + full_name='google.protobuf.FieldOptions.CType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='STRING', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CORD', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='STRING_PIECE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FIELDOPTIONS_CTYPE) + + _FIELDOPTIONS_JSTYPE = _descriptor.EnumDescriptor( + name='JSType', + full_name='google.protobuf.FieldOptions.JSType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='JS_NORMAL', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='JS_STRING', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='JS_NUMBER', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FIELDOPTIONS_JSTYPE) + + _METHODOPTIONS_IDEMPOTENCYLEVEL = _descriptor.EnumDescriptor( + name='IdempotencyLevel', + full_name='google.protobuf.MethodOptions.IdempotencyLevel', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='IDEMPOTENCY_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='NO_SIDE_EFFECTS', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='IDEMPOTENT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_METHODOPTIONS_IDEMPOTENCYLEVEL) + + + _FILEDESCRIPTORSET = _descriptor.Descriptor( + name='FileDescriptorSet', + full_name='google.protobuf.FileDescriptorSet', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='file', full_name='google.protobuf.FileDescriptorSet.file', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='file', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _FILEDESCRIPTORPROTO = _descriptor.Descriptor( + name='FileDescriptorProto', + full_name='google.protobuf.FileDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.FileDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='package', full_name='google.protobuf.FileDescriptorProto.package', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='package', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='dependency', full_name='google.protobuf.FileDescriptorProto.dependency', index=2, + number=3, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='dependency', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='public_dependency', full_name='google.protobuf.FileDescriptorProto.public_dependency', index=3, + number=10, type=5, cpp_type=1, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='publicDependency', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='weak_dependency', full_name='google.protobuf.FileDescriptorProto.weak_dependency', index=4, + number=11, type=5, cpp_type=1, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='weakDependency', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='message_type', full_name='google.protobuf.FileDescriptorProto.message_type', index=5, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='messageType', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='enum_type', full_name='google.protobuf.FileDescriptorProto.enum_type', index=6, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='enumType', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='service', full_name='google.protobuf.FileDescriptorProto.service', index=7, + number=6, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='service', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extension', full_name='google.protobuf.FileDescriptorProto.extension', index=8, + number=7, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='extension', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.FileDescriptorProto.options', index=9, + number=8, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='source_code_info', full_name='google.protobuf.FileDescriptorProto.source_code_info', index=10, + number=9, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='sourceCodeInfo', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='syntax', full_name='google.protobuf.FileDescriptorProto.syntax', index=11, + number=12, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='syntax', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _DESCRIPTORPROTO_EXTENSIONRANGE = _descriptor.Descriptor( + name='ExtensionRange', + full_name='google.protobuf.DescriptorProto.ExtensionRange', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='start', full_name='google.protobuf.DescriptorProto.ExtensionRange.start', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='start', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='end', full_name='google.protobuf.DescriptorProto.ExtensionRange.end', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='end', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.DescriptorProto.ExtensionRange.options', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + _DESCRIPTORPROTO_RESERVEDRANGE = _descriptor.Descriptor( + name='ReservedRange', + full_name='google.protobuf.DescriptorProto.ReservedRange', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='start', full_name='google.protobuf.DescriptorProto.ReservedRange.start', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='start', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='end', full_name='google.protobuf.DescriptorProto.ReservedRange.end', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='end', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + _DESCRIPTORPROTO = _descriptor.Descriptor( + name='DescriptorProto', + full_name='google.protobuf.DescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.DescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='field', full_name='google.protobuf.DescriptorProto.field', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='field', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extension', full_name='google.protobuf.DescriptorProto.extension', index=2, + number=6, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='extension', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='nested_type', full_name='google.protobuf.DescriptorProto.nested_type', index=3, + number=3, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='nestedType', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='enum_type', full_name='google.protobuf.DescriptorProto.enum_type', index=4, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='enumType', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extension_range', full_name='google.protobuf.DescriptorProto.extension_range', index=5, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='extensionRange', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='oneof_decl', full_name='google.protobuf.DescriptorProto.oneof_decl', index=6, + number=8, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='oneofDecl', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.DescriptorProto.options', index=7, + number=7, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='reserved_range', full_name='google.protobuf.DescriptorProto.reserved_range', index=8, + number=9, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='reservedRange', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='reserved_name', full_name='google.protobuf.DescriptorProto.reserved_name', index=9, + number=10, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='reservedName', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_DESCRIPTORPROTO_EXTENSIONRANGE, _DESCRIPTORPROTO_RESERVEDRANGE, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _EXTENSIONRANGEOPTIONS = _descriptor.Descriptor( + name='ExtensionRangeOptions', + full_name='google.protobuf.ExtensionRangeOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.ExtensionRangeOptions.uninterpreted_option', index=0, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _FIELDDESCRIPTORPROTO = _descriptor.Descriptor( + name='FieldDescriptorProto', + full_name='google.protobuf.FieldDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.FieldDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='number', full_name='google.protobuf.FieldDescriptorProto.number', index=1, + number=3, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='number', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='label', full_name='google.protobuf.FieldDescriptorProto.label', index=2, + number=4, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=1, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='label', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='type', full_name='google.protobuf.FieldDescriptorProto.type', index=3, + number=5, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=1, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='type', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='type_name', full_name='google.protobuf.FieldDescriptorProto.type_name', index=4, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='typeName', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extendee', full_name='google.protobuf.FieldDescriptorProto.extendee', index=5, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='extendee', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='default_value', full_name='google.protobuf.FieldDescriptorProto.default_value', index=6, + number=7, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='defaultValue', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='oneof_index', full_name='google.protobuf.FieldDescriptorProto.oneof_index', index=7, + number=9, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='oneofIndex', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='json_name', full_name='google.protobuf.FieldDescriptorProto.json_name', index=8, + number=10, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='jsonName', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.FieldDescriptorProto.options', index=9, + number=8, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='proto3_optional', full_name='google.protobuf.FieldDescriptorProto.proto3_optional', index=10, + number=17, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='proto3Optional', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _FIELDDESCRIPTORPROTO_TYPE, + _FIELDDESCRIPTORPROTO_LABEL, + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _ONEOFDESCRIPTORPROTO = _descriptor.Descriptor( + name='OneofDescriptorProto', + full_name='google.protobuf.OneofDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.OneofDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.OneofDescriptorProto.options', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE = _descriptor.Descriptor( + name='EnumReservedRange', + full_name='google.protobuf.EnumDescriptorProto.EnumReservedRange', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='start', full_name='google.protobuf.EnumDescriptorProto.EnumReservedRange.start', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='start', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='end', full_name='google.protobuf.EnumDescriptorProto.EnumReservedRange.end', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='end', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + _ENUMDESCRIPTORPROTO = _descriptor.Descriptor( + name='EnumDescriptorProto', + full_name='google.protobuf.EnumDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.EnumDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', full_name='google.protobuf.EnumDescriptorProto.value', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='value', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.EnumDescriptorProto.options', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='reserved_range', full_name='google.protobuf.EnumDescriptorProto.reserved_range', index=3, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='reservedRange', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='reserved_name', full_name='google.protobuf.EnumDescriptorProto.reserved_name', index=4, + number=5, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='reservedName', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _ENUMVALUEDESCRIPTORPROTO = _descriptor.Descriptor( + name='EnumValueDescriptorProto', + full_name='google.protobuf.EnumValueDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.EnumValueDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='number', full_name='google.protobuf.EnumValueDescriptorProto.number', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='number', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.EnumValueDescriptorProto.options', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _SERVICEDESCRIPTORPROTO = _descriptor.Descriptor( + name='ServiceDescriptorProto', + full_name='google.protobuf.ServiceDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.ServiceDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='method', full_name='google.protobuf.ServiceDescriptorProto.method', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='method', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.ServiceDescriptorProto.options', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _METHODDESCRIPTORPROTO = _descriptor.Descriptor( + name='MethodDescriptorProto', + full_name='google.protobuf.MethodDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.MethodDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='input_type', full_name='google.protobuf.MethodDescriptorProto.input_type', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='inputType', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='output_type', full_name='google.protobuf.MethodDescriptorProto.output_type', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='outputType', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.MethodDescriptorProto.options', index=3, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='options', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='client_streaming', full_name='google.protobuf.MethodDescriptorProto.client_streaming', index=4, + number=5, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='clientStreaming', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='server_streaming', full_name='google.protobuf.MethodDescriptorProto.server_streaming', index=5, + number=6, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='serverStreaming', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _FILEOPTIONS = _descriptor.Descriptor( + name='FileOptions', + full_name='google.protobuf.FileOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='java_package', full_name='google.protobuf.FileOptions.java_package', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='javaPackage', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='java_outer_classname', full_name='google.protobuf.FileOptions.java_outer_classname', index=1, + number=8, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='javaOuterClassname', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='java_multiple_files', full_name='google.protobuf.FileOptions.java_multiple_files', index=2, + number=10, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='javaMultipleFiles', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='java_generate_equals_and_hash', full_name='google.protobuf.FileOptions.java_generate_equals_and_hash', index=3, + number=20, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='javaGenerateEqualsAndHash', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='java_string_check_utf8', full_name='google.protobuf.FileOptions.java_string_check_utf8', index=4, + number=27, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='javaStringCheckUtf8', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='optimize_for', full_name='google.protobuf.FileOptions.optimize_for', index=5, + number=9, type=14, cpp_type=8, label=1, + has_default_value=True, default_value=1, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='optimizeFor', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='go_package', full_name='google.protobuf.FileOptions.go_package', index=6, + number=11, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='goPackage', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cc_generic_services', full_name='google.protobuf.FileOptions.cc_generic_services', index=7, + number=16, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='ccGenericServices', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='java_generic_services', full_name='google.protobuf.FileOptions.java_generic_services', index=8, + number=17, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='javaGenericServices', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='py_generic_services', full_name='google.protobuf.FileOptions.py_generic_services', index=9, + number=18, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='pyGenericServices', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='php_generic_services', full_name='google.protobuf.FileOptions.php_generic_services', index=10, + number=42, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='phpGenericServices', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.FileOptions.deprecated', index=11, + number=23, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='deprecated', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cc_enable_arenas', full_name='google.protobuf.FileOptions.cc_enable_arenas', index=12, + number=31, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=True, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='ccEnableArenas', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='objc_class_prefix', full_name='google.protobuf.FileOptions.objc_class_prefix', index=13, + number=36, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='objcClassPrefix', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='csharp_namespace', full_name='google.protobuf.FileOptions.csharp_namespace', index=14, + number=37, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='csharpNamespace', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='swift_prefix', full_name='google.protobuf.FileOptions.swift_prefix', index=15, + number=39, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='swiftPrefix', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='php_class_prefix', full_name='google.protobuf.FileOptions.php_class_prefix', index=16, + number=40, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='phpClassPrefix', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='php_namespace', full_name='google.protobuf.FileOptions.php_namespace', index=17, + number=41, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='phpNamespace', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='php_metadata_namespace', full_name='google.protobuf.FileOptions.php_metadata_namespace', index=18, + number=44, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='phpMetadataNamespace', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ruby_package', full_name='google.protobuf.FileOptions.ruby_package', index=19, + number=45, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='rubyPackage', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.FileOptions.uninterpreted_option', index=20, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _FILEOPTIONS_OPTIMIZEMODE, + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _MESSAGEOPTIONS = _descriptor.Descriptor( + name='MessageOptions', + full_name='google.protobuf.MessageOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='message_set_wire_format', full_name='google.protobuf.MessageOptions.message_set_wire_format', index=0, + number=1, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='messageSetWireFormat', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='no_standard_descriptor_accessor', full_name='google.protobuf.MessageOptions.no_standard_descriptor_accessor', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='noStandardDescriptorAccessor', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.MessageOptions.deprecated', index=2, + number=3, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='deprecated', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='map_entry', full_name='google.protobuf.MessageOptions.map_entry', index=3, + number=7, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='mapEntry', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.MessageOptions.uninterpreted_option', index=4, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _FIELDOPTIONS = _descriptor.Descriptor( + name='FieldOptions', + full_name='google.protobuf.FieldOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='ctype', full_name='google.protobuf.FieldOptions.ctype', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=True, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='ctype', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='packed', full_name='google.protobuf.FieldOptions.packed', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='packed', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='jstype', full_name='google.protobuf.FieldOptions.jstype', index=2, + number=6, type=14, cpp_type=8, label=1, + has_default_value=True, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='jstype', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='lazy', full_name='google.protobuf.FieldOptions.lazy', index=3, + number=5, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='lazy', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='unverified_lazy', full_name='google.protobuf.FieldOptions.unverified_lazy', index=4, + number=15, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='unverifiedLazy', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.FieldOptions.deprecated', index=5, + number=3, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='deprecated', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='weak', full_name='google.protobuf.FieldOptions.weak', index=6, + number=10, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='weak', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.FieldOptions.uninterpreted_option', index=7, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _FIELDOPTIONS_CTYPE, + _FIELDOPTIONS_JSTYPE, + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _ONEOFOPTIONS = _descriptor.Descriptor( + name='OneofOptions', + full_name='google.protobuf.OneofOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.OneofOptions.uninterpreted_option', index=0, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _ENUMOPTIONS = _descriptor.Descriptor( + name='EnumOptions', + full_name='google.protobuf.EnumOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='allow_alias', full_name='google.protobuf.EnumOptions.allow_alias', index=0, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='allowAlias', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.EnumOptions.deprecated', index=1, + number=3, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='deprecated', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.EnumOptions.uninterpreted_option', index=2, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _ENUMVALUEOPTIONS = _descriptor.Descriptor( + name='EnumValueOptions', + full_name='google.protobuf.EnumValueOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.EnumValueOptions.deprecated', index=0, + number=1, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='deprecated', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.EnumValueOptions.uninterpreted_option', index=1, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _SERVICEOPTIONS = _descriptor.Descriptor( + name='ServiceOptions', + full_name='google.protobuf.ServiceOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.ServiceOptions.deprecated', index=0, + number=33, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='deprecated', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.ServiceOptions.uninterpreted_option', index=1, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _METHODOPTIONS = _descriptor.Descriptor( + name='MethodOptions', + full_name='google.protobuf.MethodOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.MethodOptions.deprecated', index=0, + number=33, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='deprecated', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='idempotency_level', full_name='google.protobuf.MethodOptions.idempotency_level', index=1, + number=34, type=14, cpp_type=8, label=1, + has_default_value=True, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='idempotencyLevel', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.MethodOptions.uninterpreted_option', index=2, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='uninterpretedOption', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _METHODOPTIONS_IDEMPOTENCYLEVEL, + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _UNINTERPRETEDOPTION_NAMEPART = _descriptor.Descriptor( + name='NamePart', + full_name='google.protobuf.UninterpretedOption.NamePart', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name_part', full_name='google.protobuf.UninterpretedOption.NamePart.name_part', index=0, + number=1, type=9, cpp_type=9, label=2, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='namePart', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_extension', full_name='google.protobuf.UninterpretedOption.NamePart.is_extension', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='isExtension', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + _UNINTERPRETEDOPTION = _descriptor.Descriptor( + name='UninterpretedOption', + full_name='google.protobuf.UninterpretedOption', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.UninterpretedOption.name', index=0, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='name', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='identifier_value', full_name='google.protobuf.UninterpretedOption.identifier_value', index=1, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='identifierValue', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='positive_int_value', full_name='google.protobuf.UninterpretedOption.positive_int_value', index=2, + number=4, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='positiveIntValue', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='negative_int_value', full_name='google.protobuf.UninterpretedOption.negative_int_value', index=3, + number=5, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='negativeIntValue', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='double_value', full_name='google.protobuf.UninterpretedOption.double_value', index=4, + number=6, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='doubleValue', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='string_value', full_name='google.protobuf.UninterpretedOption.string_value', index=5, + number=7, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='stringValue', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='aggregate_value', full_name='google.protobuf.UninterpretedOption.aggregate_value', index=6, + number=8, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='aggregateValue', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_UNINTERPRETEDOPTION_NAMEPART, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _SOURCECODEINFO_LOCATION = _descriptor.Descriptor( + name='Location', + full_name='google.protobuf.SourceCodeInfo.Location', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='path', full_name='google.protobuf.SourceCodeInfo.Location.path', index=0, + number=1, type=5, cpp_type=1, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='path', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='span', full_name='google.protobuf.SourceCodeInfo.Location.span', index=1, + number=2, type=5, cpp_type=1, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='span', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='leading_comments', full_name='google.protobuf.SourceCodeInfo.Location.leading_comments', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='leadingComments', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='trailing_comments', full_name='google.protobuf.SourceCodeInfo.Location.trailing_comments', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='trailingComments', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='leading_detached_comments', full_name='google.protobuf.SourceCodeInfo.Location.leading_detached_comments', index=4, + number=6, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='leadingDetachedComments', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + _SOURCECODEINFO = _descriptor.Descriptor( + name='SourceCodeInfo', + full_name='google.protobuf.SourceCodeInfo', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='location', full_name='google.protobuf.SourceCodeInfo.location', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='location', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_SOURCECODEINFO_LOCATION, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _GENERATEDCODEINFO_ANNOTATION = _descriptor.Descriptor( + name='Annotation', + full_name='google.protobuf.GeneratedCodeInfo.Annotation', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='path', full_name='google.protobuf.GeneratedCodeInfo.Annotation.path', index=0, + number=1, type=5, cpp_type=1, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='path', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='source_file', full_name='google.protobuf.GeneratedCodeInfo.Annotation.source_file', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='sourceFile', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='begin', full_name='google.protobuf.GeneratedCodeInfo.Annotation.begin', index=2, + number=3, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='begin', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='end', full_name='google.protobuf.GeneratedCodeInfo.Annotation.end', index=3, + number=4, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='end', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + _GENERATEDCODEINFO = _descriptor.Descriptor( + name='GeneratedCodeInfo', + full_name='google.protobuf.GeneratedCodeInfo', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='annotation', full_name='google.protobuf.GeneratedCodeInfo.annotation', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, json_name='annotation', file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_GENERATEDCODEINFO_ANNOTATION, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + _FILEDESCRIPTORSET.fields_by_name['file'].message_type = _FILEDESCRIPTORPROTO + _FILEDESCRIPTORPROTO.fields_by_name['message_type'].message_type = _DESCRIPTORPROTO + _FILEDESCRIPTORPROTO.fields_by_name['enum_type'].message_type = _ENUMDESCRIPTORPROTO + _FILEDESCRIPTORPROTO.fields_by_name['service'].message_type = _SERVICEDESCRIPTORPROTO + _FILEDESCRIPTORPROTO.fields_by_name['extension'].message_type = _FIELDDESCRIPTORPROTO + _FILEDESCRIPTORPROTO.fields_by_name['options'].message_type = _FILEOPTIONS + _FILEDESCRIPTORPROTO.fields_by_name['source_code_info'].message_type = _SOURCECODEINFO + _DESCRIPTORPROTO_EXTENSIONRANGE.fields_by_name['options'].message_type = _EXTENSIONRANGEOPTIONS + _DESCRIPTORPROTO_EXTENSIONRANGE.containing_type = _DESCRIPTORPROTO + _DESCRIPTORPROTO_RESERVEDRANGE.containing_type = _DESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['field'].message_type = _FIELDDESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['extension'].message_type = _FIELDDESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['nested_type'].message_type = _DESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['enum_type'].message_type = _ENUMDESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['extension_range'].message_type = _DESCRIPTORPROTO_EXTENSIONRANGE + _DESCRIPTORPROTO.fields_by_name['oneof_decl'].message_type = _ONEOFDESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['options'].message_type = _MESSAGEOPTIONS + _DESCRIPTORPROTO.fields_by_name['reserved_range'].message_type = _DESCRIPTORPROTO_RESERVEDRANGE + _EXTENSIONRANGEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _FIELDDESCRIPTORPROTO.fields_by_name['label'].enum_type = _FIELDDESCRIPTORPROTO_LABEL + _FIELDDESCRIPTORPROTO.fields_by_name['type'].enum_type = _FIELDDESCRIPTORPROTO_TYPE + _FIELDDESCRIPTORPROTO.fields_by_name['options'].message_type = _FIELDOPTIONS + _FIELDDESCRIPTORPROTO_TYPE.containing_type = _FIELDDESCRIPTORPROTO + _FIELDDESCRIPTORPROTO_LABEL.containing_type = _FIELDDESCRIPTORPROTO + _ONEOFDESCRIPTORPROTO.fields_by_name['options'].message_type = _ONEOFOPTIONS + _ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE.containing_type = _ENUMDESCRIPTORPROTO + _ENUMDESCRIPTORPROTO.fields_by_name['value'].message_type = _ENUMVALUEDESCRIPTORPROTO + _ENUMDESCRIPTORPROTO.fields_by_name['options'].message_type = _ENUMOPTIONS + _ENUMDESCRIPTORPROTO.fields_by_name['reserved_range'].message_type = _ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE + _ENUMVALUEDESCRIPTORPROTO.fields_by_name['options'].message_type = _ENUMVALUEOPTIONS + _SERVICEDESCRIPTORPROTO.fields_by_name['method'].message_type = _METHODDESCRIPTORPROTO + _SERVICEDESCRIPTORPROTO.fields_by_name['options'].message_type = _SERVICEOPTIONS + _METHODDESCRIPTORPROTO.fields_by_name['options'].message_type = _METHODOPTIONS + _FILEOPTIONS.fields_by_name['optimize_for'].enum_type = _FILEOPTIONS_OPTIMIZEMODE + _FILEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _FILEOPTIONS_OPTIMIZEMODE.containing_type = _FILEOPTIONS + _MESSAGEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _FIELDOPTIONS.fields_by_name['ctype'].enum_type = _FIELDOPTIONS_CTYPE + _FIELDOPTIONS.fields_by_name['jstype'].enum_type = _FIELDOPTIONS_JSTYPE + _FIELDOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _FIELDOPTIONS_CTYPE.containing_type = _FIELDOPTIONS + _FIELDOPTIONS_JSTYPE.containing_type = _FIELDOPTIONS + _ONEOFOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _ENUMOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _ENUMVALUEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _SERVICEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _METHODOPTIONS.fields_by_name['idempotency_level'].enum_type = _METHODOPTIONS_IDEMPOTENCYLEVEL + _METHODOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _METHODOPTIONS_IDEMPOTENCYLEVEL.containing_type = _METHODOPTIONS + _UNINTERPRETEDOPTION_NAMEPART.containing_type = _UNINTERPRETEDOPTION + _UNINTERPRETEDOPTION.fields_by_name['name'].message_type = _UNINTERPRETEDOPTION_NAMEPART + _SOURCECODEINFO_LOCATION.containing_type = _SOURCECODEINFO + _SOURCECODEINFO.fields_by_name['location'].message_type = _SOURCECODEINFO_LOCATION + _GENERATEDCODEINFO_ANNOTATION.containing_type = _GENERATEDCODEINFO + _GENERATEDCODEINFO.fields_by_name['annotation'].message_type = _GENERATEDCODEINFO_ANNOTATION + DESCRIPTOR.message_types_by_name['FileDescriptorSet'] = _FILEDESCRIPTORSET + DESCRIPTOR.message_types_by_name['FileDescriptorProto'] = _FILEDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['DescriptorProto'] = _DESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['ExtensionRangeOptions'] = _EXTENSIONRANGEOPTIONS + DESCRIPTOR.message_types_by_name['FieldDescriptorProto'] = _FIELDDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['OneofDescriptorProto'] = _ONEOFDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['EnumDescriptorProto'] = _ENUMDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['EnumValueDescriptorProto'] = _ENUMVALUEDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['ServiceDescriptorProto'] = _SERVICEDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['MethodDescriptorProto'] = _METHODDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['FileOptions'] = _FILEOPTIONS + DESCRIPTOR.message_types_by_name['MessageOptions'] = _MESSAGEOPTIONS + DESCRIPTOR.message_types_by_name['FieldOptions'] = _FIELDOPTIONS + DESCRIPTOR.message_types_by_name['OneofOptions'] = _ONEOFOPTIONS + DESCRIPTOR.message_types_by_name['EnumOptions'] = _ENUMOPTIONS + DESCRIPTOR.message_types_by_name['EnumValueOptions'] = _ENUMVALUEOPTIONS + DESCRIPTOR.message_types_by_name['ServiceOptions'] = _SERVICEOPTIONS + DESCRIPTOR.message_types_by_name['MethodOptions'] = _METHODOPTIONS + DESCRIPTOR.message_types_by_name['UninterpretedOption'] = _UNINTERPRETEDOPTION + DESCRIPTOR.message_types_by_name['SourceCodeInfo'] = _SOURCECODEINFO + DESCRIPTOR.message_types_by_name['GeneratedCodeInfo'] = _GENERATEDCODEINFO + _sym_db.RegisterFileDescriptor(DESCRIPTOR) + +else: + _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.descriptor_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _FILEDESCRIPTORSET._serialized_start=53 + _FILEDESCRIPTORSET._serialized_end=130 + _FILEDESCRIPTORPROTO._serialized_start=133 + _FILEDESCRIPTORPROTO._serialized_end=745 + _DESCRIPTORPROTO._serialized_start=748 + _DESCRIPTORPROTO._serialized_end=1573 + _DESCRIPTORPROTO_EXTENSIONRANGE._serialized_start=1394 + _DESCRIPTORPROTO_EXTENSIONRANGE._serialized_end=1516 + _DESCRIPTORPROTO_RESERVEDRANGE._serialized_start=1518 + _DESCRIPTORPROTO_RESERVEDRANGE._serialized_end=1573 + _EXTENSIONRANGEOPTIONS._serialized_start=1575 + _EXTENSIONRANGEOPTIONS._serialized_end=1699 + _FIELDDESCRIPTORPROTO._serialized_start=1702 + _FIELDDESCRIPTORPROTO._serialized_end=2535 + _FIELDDESCRIPTORPROTO_TYPE._serialized_start=2156 + _FIELDDESCRIPTORPROTO_TYPE._serialized_end=2466 + _FIELDDESCRIPTORPROTO_LABEL._serialized_start=2468 + _FIELDDESCRIPTORPROTO_LABEL._serialized_end=2535 + _ONEOFDESCRIPTORPROTO._serialized_start=2537 + _ONEOFDESCRIPTORPROTO._serialized_end=2636 + _ENUMDESCRIPTORPROTO._serialized_start=2639 + _ENUMDESCRIPTORPROTO._serialized_end=2994 + _ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE._serialized_start=2935 + _ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE._serialized_end=2994 + _ENUMVALUEDESCRIPTORPROTO._serialized_start=2997 + _ENUMVALUEDESCRIPTORPROTO._serialized_end=3128 + _SERVICEDESCRIPTORPROTO._serialized_start=3131 + _SERVICEDESCRIPTORPROTO._serialized_end=3298 + _METHODDESCRIPTORPROTO._serialized_start=3301 + _METHODDESCRIPTORPROTO._serialized_end=3566 + _FILEOPTIONS._serialized_start=3569 + _FILEOPTIONS._serialized_end=4738 + _FILEOPTIONS_OPTIMIZEMODE._serialized_start=4663 + _FILEOPTIONS_OPTIMIZEMODE._serialized_end=4721 + _MESSAGEOPTIONS._serialized_start=4741 + _MESSAGEOPTIONS._serialized_end=5096 + _FIELDOPTIONS._serialized_start=5099 + _FIELDOPTIONS._serialized_end=5629 + _FIELDOPTIONS_CTYPE._serialized_start=5510 + _FIELDOPTIONS_CTYPE._serialized_end=5557 + _FIELDOPTIONS_JSTYPE._serialized_start=5559 + _FIELDOPTIONS_JSTYPE._serialized_end=5612 + _ONEOFOPTIONS._serialized_start=5631 + _ONEOFOPTIONS._serialized_end=5746 + _ENUMOPTIONS._serialized_start=5749 + _ENUMOPTIONS._serialized_end=5941 + _ENUMVALUEOPTIONS._serialized_start=5944 + _ENUMVALUEOPTIONS._serialized_end=6102 + _SERVICEOPTIONS._serialized_start=6105 + _SERVICEOPTIONS._serialized_end=6261 + _METHODOPTIONS._serialized_start=6264 + _METHODOPTIONS._serialized_end=6616 + _METHODOPTIONS_IDEMPOTENCYLEVEL._serialized_start=6525 + _METHODOPTIONS_IDEMPOTENCYLEVEL._serialized_end=6605 + _UNINTERPRETEDOPTION._serialized_start=6619 + _UNINTERPRETEDOPTION._serialized_end=7029 + _UNINTERPRETEDOPTION_NAMEPART._serialized_start=6955 + _UNINTERPRETEDOPTION_NAMEPART._serialized_end=7029 + _SOURCECODEINFO._serialized_start=7032 + _SOURCECODEINFO._serialized_end=7327 + _SOURCECODEINFO_LOCATION._serialized_start=7121 + _SOURCECODEINFO_LOCATION._serialized_end=7327 + _GENERATEDCODEINFO._serialized_start=7330 + _GENERATEDCODEINFO._serialized_end=7539 + _GENERATEDCODEINFO_ANNOTATION._serialized_start=7430 + _GENERATEDCODEINFO_ANNOTATION._serialized_end=7539 +# @@protoc_insertion_point(module_scope) diff --git a/virt/lib/python3.9/site-packages/google/protobuf/internal/descriptor_pool_test 3.py b/virt/lib/python3.9/site-packages/google/protobuf/internal/descriptor_pool_test 3.py new file mode 100644 index 00000000..8619f848 --- /dev/null +++ b/virt/lib/python3.9/site-packages/google/protobuf/internal/descriptor_pool_test 3.py @@ -0,0 +1,1149 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# 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 Google 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 +# OWNER 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. + +"""Tests for google.protobuf.descriptor_pool.""" + +__author__ = 'matthewtoia@google.com (Matt Toia)' + +import copy +import os +import unittest +import warnings + +from google.protobuf import unittest_import_pb2 +from google.protobuf import unittest_import_public_pb2 +from google.protobuf import unittest_pb2 +from google.protobuf import descriptor_pb2 +from google.protobuf.internal import api_implementation +from google.protobuf.internal import descriptor_pool_test1_pb2 +from google.protobuf.internal import descriptor_pool_test2_pb2 +from google.protobuf.internal import factory_test1_pb2 +from google.protobuf.internal import factory_test2_pb2 +from google.protobuf.internal import file_options_test_pb2 +from google.protobuf.internal import more_messages_pb2 +from google.protobuf.internal import no_package_pb2 +from google.protobuf.internal import testing_refleaks +from google.protobuf import descriptor +from google.protobuf import descriptor_database +from google.protobuf import descriptor_pool +from google.protobuf import message_factory +from google.protobuf import symbol_database + + + +warnings.simplefilter('error', DeprecationWarning) + + +class DescriptorPoolTestBase(object): + + def testFindFileByName(self): + name1 = 'google/protobuf/internal/factory_test1.proto' + file_desc1 = self.pool.FindFileByName(name1) + self.assertIsInstance(file_desc1, descriptor.FileDescriptor) + self.assertEqual(name1, file_desc1.name) + self.assertEqual('google.protobuf.python.internal', file_desc1.package) + self.assertIn('Factory1Message', file_desc1.message_types_by_name) + + name2 = 'google/protobuf/internal/factory_test2.proto' + file_desc2 = self.pool.FindFileByName(name2) + self.assertIsInstance(file_desc2, descriptor.FileDescriptor) + self.assertEqual(name2, file_desc2.name) + self.assertEqual('google.protobuf.python.internal', file_desc2.package) + self.assertIn('Factory2Message', file_desc2.message_types_by_name) + + def testFindFileByNameFailure(self): + with self.assertRaises(KeyError): + self.pool.FindFileByName('Does not exist') + + def testFindFileContainingSymbol(self): + file_desc1 = self.pool.FindFileContainingSymbol( + 'google.protobuf.python.internal.Factory1Message') + self.assertIsInstance(file_desc1, descriptor.FileDescriptor) + self.assertEqual('google/protobuf/internal/factory_test1.proto', + file_desc1.name) + self.assertEqual('google.protobuf.python.internal', file_desc1.package) + self.assertIn('Factory1Message', file_desc1.message_types_by_name) + + file_desc2 = self.pool.FindFileContainingSymbol( + 'google.protobuf.python.internal.Factory2Message') + self.assertIsInstance(file_desc2, descriptor.FileDescriptor) + self.assertEqual('google/protobuf/internal/factory_test2.proto', + file_desc2.name) + self.assertEqual('google.protobuf.python.internal', file_desc2.package) + self.assertIn('Factory2Message', file_desc2.message_types_by_name) + + # Tests top level extension. + file_desc3 = self.pool.FindFileContainingSymbol( + 'google.protobuf.python.internal.another_field') + self.assertIsInstance(file_desc3, descriptor.FileDescriptor) + self.assertEqual('google/protobuf/internal/factory_test2.proto', + file_desc3.name) + + # Tests nested extension inside a message. + file_desc4 = self.pool.FindFileContainingSymbol( + 'google.protobuf.python.internal.Factory2Message.one_more_field') + self.assertIsInstance(file_desc4, descriptor.FileDescriptor) + self.assertEqual('google/protobuf/internal/factory_test2.proto', + file_desc4.name) + + file_desc5 = self.pool.FindFileContainingSymbol( + 'protobuf_unittest.TestService') + self.assertIsInstance(file_desc5, descriptor.FileDescriptor) + self.assertEqual('google/protobuf/unittest.proto', + file_desc5.name) + # Tests the generated pool. + assert descriptor_pool.Default().FindFileContainingSymbol( + 'google.protobuf.python.internal.Factory2Message.one_more_field') + assert descriptor_pool.Default().FindFileContainingSymbol( + 'google.protobuf.python.internal.another_field') + assert descriptor_pool.Default().FindFileContainingSymbol( + 'protobuf_unittest.TestService') + + # Can find field. + file_desc6 = self.pool.FindFileContainingSymbol( + 'google.protobuf.python.internal.Factory1Message.list_value') + self.assertIsInstance(file_desc6, descriptor.FileDescriptor) + self.assertEqual('google/protobuf/internal/factory_test1.proto', + file_desc6.name) + + # Can find top level Enum value. + file_desc7 = self.pool.FindFileContainingSymbol( + 'google.protobuf.python.internal.FACTORY_1_VALUE_0') + self.assertIsInstance(file_desc7, descriptor.FileDescriptor) + self.assertEqual('google/protobuf/internal/factory_test1.proto', + file_desc7.name) + + # Can find nested Enum value. + file_desc8 = self.pool.FindFileContainingSymbol( + 'protobuf_unittest.TestAllTypes.FOO') + self.assertIsInstance(file_desc8, descriptor.FileDescriptor) + self.assertEqual('google/protobuf/unittest.proto', + file_desc8.name) + + # TODO(jieluo): Add tests for no package when b/13860351 is fixed. + + self.assertRaises(KeyError, self.pool.FindFileContainingSymbol, + 'google.protobuf.python.internal.Factory1Message.none_field') + + def testFindFileContainingSymbolFailure(self): + with self.assertRaises(KeyError): + self.pool.FindFileContainingSymbol('Does not exist') + + def testFindMessageTypeByName(self): + msg1 = self.pool.FindMessageTypeByName( + 'google.protobuf.python.internal.Factory1Message') + self.assertIsInstance(msg1, descriptor.Descriptor) + self.assertEqual('Factory1Message', msg1.name) + self.assertEqual('google.protobuf.python.internal.Factory1Message', + msg1.full_name) + self.assertEqual(None, msg1.containing_type) + self.assertFalse(msg1.has_options) + + nested_msg1 = msg1.nested_types[0] + self.assertEqual('NestedFactory1Message', nested_msg1.name) + self.assertEqual(msg1, nested_msg1.containing_type) + + nested_enum1 = msg1.enum_types[0] + self.assertEqual('NestedFactory1Enum', nested_enum1.name) + self.assertEqual(msg1, nested_enum1.containing_type) + + self.assertEqual(nested_msg1, msg1.fields_by_name[ + 'nested_factory_1_message'].message_type) + self.assertEqual(nested_enum1, msg1.fields_by_name[ + 'nested_factory_1_enum'].enum_type) + + msg2 = self.pool.FindMessageTypeByName( + 'google.protobuf.python.internal.Factory2Message') + self.assertIsInstance(msg2, descriptor.Descriptor) + self.assertEqual('Factory2Message', msg2.name) + self.assertEqual('google.protobuf.python.internal.Factory2Message', + msg2.full_name) + self.assertIsNone(msg2.containing_type) + + nested_msg2 = msg2.nested_types[0] + self.assertEqual('NestedFactory2Message', nested_msg2.name) + self.assertEqual(msg2, nested_msg2.containing_type) + + nested_enum2 = msg2.enum_types[0] + self.assertEqual('NestedFactory2Enum', nested_enum2.name) + self.assertEqual(msg2, nested_enum2.containing_type) + + self.assertEqual(nested_msg2, msg2.fields_by_name[ + 'nested_factory_2_message'].message_type) + self.assertEqual(nested_enum2, msg2.fields_by_name[ + 'nested_factory_2_enum'].enum_type) + + self.assertTrue(msg2.fields_by_name['int_with_default'].has_default_value) + self.assertEqual( + 1776, msg2.fields_by_name['int_with_default'].default_value) + + self.assertTrue( + msg2.fields_by_name['double_with_default'].has_default_value) + self.assertEqual( + 9.99, msg2.fields_by_name['double_with_default'].default_value) + + self.assertTrue( + msg2.fields_by_name['string_with_default'].has_default_value) + self.assertEqual( + 'hello world', msg2.fields_by_name['string_with_default'].default_value) + + self.assertTrue(msg2.fields_by_name['bool_with_default'].has_default_value) + self.assertFalse(msg2.fields_by_name['bool_with_default'].default_value) + + self.assertTrue(msg2.fields_by_name['enum_with_default'].has_default_value) + self.assertEqual( + 1, msg2.fields_by_name['enum_with_default'].default_value) + + msg3 = self.pool.FindMessageTypeByName( + 'google.protobuf.python.internal.Factory2Message.NestedFactory2Message') + self.assertEqual(nested_msg2, msg3) + + self.assertTrue(msg2.fields_by_name['bytes_with_default'].has_default_value) + self.assertEqual( + b'a\xfb\x00c', + msg2.fields_by_name['bytes_with_default'].default_value) + + self.assertEqual(1, len(msg2.oneofs)) + self.assertEqual(1, len(msg2.oneofs_by_name)) + self.assertEqual(2, len(msg2.oneofs[0].fields)) + for name in ['oneof_int', 'oneof_string']: + self.assertEqual(msg2.oneofs[0], + msg2.fields_by_name[name].containing_oneof) + self.assertIn(msg2.fields_by_name[name], msg2.oneofs[0].fields) + + def testFindTypeErrors(self): + self.assertRaises(TypeError, self.pool.FindExtensionByNumber, '') + self.assertRaises(KeyError, self.pool.FindMethodByName, '') + + # TODO(jieluo): Fix python to raise correct errors. + if api_implementation.Type() == 'python': + error_type = AttributeError + else: + error_type = TypeError + self.assertRaises(error_type, self.pool.FindMessageTypeByName, 0) + self.assertRaises(error_type, self.pool.FindFieldByName, 0) + self.assertRaises(error_type, self.pool.FindExtensionByName, 0) + self.assertRaises(error_type, self.pool.FindEnumTypeByName, 0) + self.assertRaises(error_type, self.pool.FindOneofByName, 0) + self.assertRaises(error_type, self.pool.FindServiceByName, 0) + self.assertRaises(error_type, self.pool.FindMethodByName, 0) + self.assertRaises(error_type, self.pool.FindFileContainingSymbol, 0) + if api_implementation.Type() == 'python': + error_type = KeyError + self.assertRaises(error_type, self.pool.FindFileByName, 0) + + def testFindMessageTypeByNameFailure(self): + with self.assertRaises(KeyError): + self.pool.FindMessageTypeByName('Does not exist') + + def testFindEnumTypeByName(self): + enum1 = self.pool.FindEnumTypeByName( + 'google.protobuf.python.internal.Factory1Enum') + self.assertIsInstance(enum1, descriptor.EnumDescriptor) + self.assertEqual(0, enum1.values_by_name['FACTORY_1_VALUE_0'].number) + self.assertEqual(1, enum1.values_by_name['FACTORY_1_VALUE_1'].number) + self.assertFalse(enum1.has_options) + + nested_enum1 = self.pool.FindEnumTypeByName( + 'google.protobuf.python.internal.Factory1Message.NestedFactory1Enum') + self.assertIsInstance(nested_enum1, descriptor.EnumDescriptor) + self.assertEqual( + 0, nested_enum1.values_by_name['NESTED_FACTORY_1_VALUE_0'].number) + self.assertEqual( + 1, nested_enum1.values_by_name['NESTED_FACTORY_1_VALUE_1'].number) + + enum2 = self.pool.FindEnumTypeByName( + 'google.protobuf.python.internal.Factory2Enum') + self.assertIsInstance(enum2, descriptor.EnumDescriptor) + self.assertEqual(0, enum2.values_by_name['FACTORY_2_VALUE_0'].number) + self.assertEqual(1, enum2.values_by_name['FACTORY_2_VALUE_1'].number) + + nested_enum2 = self.pool.FindEnumTypeByName( + 'google.protobuf.python.internal.Factory2Message.NestedFactory2Enum') + self.assertIsInstance(nested_enum2, descriptor.EnumDescriptor) + self.assertEqual( + 0, nested_enum2.values_by_name['NESTED_FACTORY_2_VALUE_0'].number) + self.assertEqual( + 1, nested_enum2.values_by_name['NESTED_FACTORY_2_VALUE_1'].number) + + def testFindEnumTypeByNameFailure(self): + with self.assertRaises(KeyError): + self.pool.FindEnumTypeByName('Does not exist') + + def testFindFieldByName(self): + field = self.pool.FindFieldByName( + 'google.protobuf.python.internal.Factory1Message.list_value') + self.assertEqual(field.name, 'list_value') + self.assertEqual(field.label, field.LABEL_REPEATED) + self.assertFalse(field.has_options) + + with self.assertRaises(KeyError): + self.pool.FindFieldByName('Does not exist') + + def testFindOneofByName(self): + oneof = self.pool.FindOneofByName( + 'google.protobuf.python.internal.Factory2Message.oneof_field') + self.assertEqual(oneof.name, 'oneof_field') + with self.assertRaises(KeyError): + self.pool.FindOneofByName('Does not exist') + + def testFindExtensionByName(self): + # An extension defined in a message. + extension = self.pool.FindExtensionByName( + 'google.protobuf.python.internal.Factory2Message.one_more_field') + self.assertEqual(extension.name, 'one_more_field') + # An extension defined at file scope. + extension = self.pool.FindExtensionByName( + 'google.protobuf.python.internal.another_field') + self.assertEqual(extension.name, 'another_field') + self.assertEqual(extension.number, 1002) + with self.assertRaises(KeyError): + self.pool.FindFieldByName('Does not exist') + + def testFindAllExtensions(self): + factory1_message = self.pool.FindMessageTypeByName( + 'google.protobuf.python.internal.Factory1Message') + factory2_message = self.pool.FindMessageTypeByName( + 'google.protobuf.python.internal.Factory2Message') + # An extension defined in a message. + one_more_field = factory2_message.extensions_by_name['one_more_field'] + # An extension defined at file scope. + factory_test2 = self.pool.FindFileByName( + 'google/protobuf/internal/factory_test2.proto') + another_field = factory_test2.extensions_by_name['another_field'] + + extensions = self.pool.FindAllExtensions(factory1_message) + expected_extension_numbers = set([one_more_field, another_field]) + self.assertEqual(expected_extension_numbers, set(extensions)) + # Verify that mutating the returned list does not affect the pool. + extensions.append('unexpected_element') + # Get the extensions again, the returned value does not contain the + # 'unexpected_element'. + extensions = self.pool.FindAllExtensions(factory1_message) + self.assertEqual(expected_extension_numbers, set(extensions)) + + def testFindExtensionByNumber(self): + factory1_message = self.pool.FindMessageTypeByName( + 'google.protobuf.python.internal.Factory1Message') + # Build factory_test2.proto which will put extensions to the pool + self.pool.FindFileByName( + 'google/protobuf/internal/factory_test2.proto') + + # An extension defined in a message. + extension = self.pool.FindExtensionByNumber(factory1_message, 1001) + self.assertEqual(extension.name, 'one_more_field') + # An extension defined at file scope. + extension = self.pool.FindExtensionByNumber(factory1_message, 1002) + self.assertEqual(extension.name, 'another_field') + with self.assertRaises(KeyError): + extension = self.pool.FindExtensionByNumber(factory1_message, 1234567) + + def testExtensionsAreNotFields(self): + with self.assertRaises(KeyError): + self.pool.FindFieldByName('google.protobuf.python.internal.another_field') + with self.assertRaises(KeyError): + self.pool.FindFieldByName( + 'google.protobuf.python.internal.Factory2Message.one_more_field') + with self.assertRaises(KeyError): + self.pool.FindExtensionByName( + 'google.protobuf.python.internal.Factory1Message.list_value') + + def testFindService(self): + service = self.pool.FindServiceByName('protobuf_unittest.TestService') + self.assertEqual(service.full_name, 'protobuf_unittest.TestService') + with self.assertRaises(KeyError): + self.pool.FindServiceByName('Does not exist') + + method = self.pool.FindMethodByName('protobuf_unittest.TestService.Foo') + self.assertIs(method.containing_service, service) + with self.assertRaises(KeyError): + self.pool.FindMethodByName('protobuf_unittest.TestService.Doesnotexist') + + def testUserDefinedDB(self): + db = descriptor_database.DescriptorDatabase() + self.pool = descriptor_pool.DescriptorPool(db) + db.Add(self.factory_test1_fd) + db.Add(self.factory_test2_fd) + self.testFindMessageTypeByName() + + def testAddSerializedFile(self): + if isinstance(self, SecondaryDescriptorFromDescriptorDB): + if api_implementation.Type() != 'python': + # Cpp extension cannot call Add on a DescriptorPool + # that uses a DescriptorDatabase. + # TODO(jieluo): Fix python and cpp extension diff. + return + self.pool = descriptor_pool.DescriptorPool() + file1 = self.pool.AddSerializedFile( + self.factory_test1_fd.SerializeToString()) + file2 = self.pool.AddSerializedFile( + self.factory_test2_fd.SerializeToString()) + self.assertEqual(file1.name, + 'google/protobuf/internal/factory_test1.proto') + self.assertEqual(file2.name, + 'google/protobuf/internal/factory_test2.proto') + self.testFindMessageTypeByName() + file_json = self.pool.AddSerializedFile( + more_messages_pb2.DESCRIPTOR.serialized_pb) + field = file_json.message_types_by_name['class'].fields_by_name['int_field'] + self.assertEqual(field.json_name, 'json_int') + + + def testEnumDefaultValue(self): + """Test the default value of enums which don't start at zero.""" + def _CheckDefaultValue(file_descriptor): + default_value = (file_descriptor + .message_types_by_name['DescriptorPoolTest1'] + .fields_by_name['nested_enum'] + .default_value) + self.assertEqual(default_value, + descriptor_pool_test1_pb2.DescriptorPoolTest1.BETA) + # First check what the generated descriptor contains. + _CheckDefaultValue(descriptor_pool_test1_pb2.DESCRIPTOR) + # Then check the generated pool. Normally this is the same descriptor. + file_descriptor = symbol_database.Default().pool.FindFileByName( + 'google/protobuf/internal/descriptor_pool_test1.proto') + self.assertIs(file_descriptor, descriptor_pool_test1_pb2.DESCRIPTOR) + _CheckDefaultValue(file_descriptor) + + if isinstance(self, SecondaryDescriptorFromDescriptorDB): + if api_implementation.Type() != 'python': + # Cpp extension cannot call Add on a DescriptorPool + # that uses a DescriptorDatabase. + # TODO(jieluo): Fix python and cpp extension diff. + return + # Then check the dynamic pool and its internal DescriptorDatabase. + descriptor_proto = descriptor_pb2.FileDescriptorProto.FromString( + descriptor_pool_test1_pb2.DESCRIPTOR.serialized_pb) + self.pool.Add(descriptor_proto) + # And do the same check as above + file_descriptor = self.pool.FindFileByName( + 'google/protobuf/internal/descriptor_pool_test1.proto') + _CheckDefaultValue(file_descriptor) + + def testDefaultValueForCustomMessages(self): + """Check the value returned by non-existent fields.""" + def _CheckValueAndType(value, expected_value, expected_type): + self.assertEqual(value, expected_value) + self.assertIsInstance(value, expected_type) + + def _CheckDefaultValues(msg): + try: + int64 = long + except NameError: # Python3 + int64 = int + try: + unicode_type = unicode + except NameError: # Python3 + unicode_type = str + _CheckValueAndType(msg.optional_int32, 0, int) + _CheckValueAndType(msg.optional_uint64, 0, (int64, int)) + _CheckValueAndType(msg.optional_float, 0, (float, int)) + _CheckValueAndType(msg.optional_double, 0, (float, int)) + _CheckValueAndType(msg.optional_bool, False, bool) + _CheckValueAndType(msg.optional_string, u'', unicode_type) + _CheckValueAndType(msg.optional_bytes, b'', bytes) + _CheckValueAndType(msg.optional_nested_enum, msg.FOO, int) + # First for the generated message + _CheckDefaultValues(unittest_pb2.TestAllTypes()) + # Then for a message built with from the DescriptorPool. + pool = descriptor_pool.DescriptorPool() + pool.Add(descriptor_pb2.FileDescriptorProto.FromString( + unittest_import_public_pb2.DESCRIPTOR.serialized_pb)) + pool.Add(descriptor_pb2.FileDescriptorProto.FromString( + unittest_import_pb2.DESCRIPTOR.serialized_pb)) + pool.Add(descriptor_pb2.FileDescriptorProto.FromString( + unittest_pb2.DESCRIPTOR.serialized_pb)) + message_class = message_factory.MessageFactory(pool).GetPrototype( + pool.FindMessageTypeByName( + unittest_pb2.TestAllTypes.DESCRIPTOR.full_name)) + _CheckDefaultValues(message_class()) + + def testAddFileDescriptor(self): + if isinstance(self, SecondaryDescriptorFromDescriptorDB): + if api_implementation.Type() != 'python': + # Cpp extension cannot call Add on a DescriptorPool + # that uses a DescriptorDatabase. + # TODO(jieluo): Fix python and cpp extension diff. + return + file_desc = descriptor_pb2.FileDescriptorProto(name='some/file.proto') + self.pool.Add(file_desc) + self.pool.AddSerializedFile(file_desc.SerializeToString()) + + def testComplexNesting(self): + if isinstance(self, SecondaryDescriptorFromDescriptorDB): + if api_implementation.Type() != 'python': + # Cpp extension cannot call Add on a DescriptorPool + # that uses a DescriptorDatabase. + # TODO(jieluo): Fix python and cpp extension diff. + return + more_messages_desc = descriptor_pb2.FileDescriptorProto.FromString( + more_messages_pb2.DESCRIPTOR.serialized_pb) + test1_desc = descriptor_pb2.FileDescriptorProto.FromString( + descriptor_pool_test1_pb2.DESCRIPTOR.serialized_pb) + test2_desc = descriptor_pb2.FileDescriptorProto.FromString( + descriptor_pool_test2_pb2.DESCRIPTOR.serialized_pb) + self.pool.Add(more_messages_desc) + self.pool.Add(test1_desc) + self.pool.Add(test2_desc) + TEST1_FILE.CheckFile(self, self.pool) + TEST2_FILE.CheckFile(self, self.pool) + + def testConflictRegister(self): + if isinstance(self, SecondaryDescriptorFromDescriptorDB): + if api_implementation.Type() != 'python': + # Cpp extension cannot call Add on a DescriptorPool + # that uses a DescriptorDatabase. + # TODO(jieluo): Fix python and cpp extension diff. + return + unittest_fd = descriptor_pb2.FileDescriptorProto.FromString( + unittest_pb2.DESCRIPTOR.serialized_pb) + conflict_fd = copy.deepcopy(unittest_fd) + conflict_fd.name = 'other_file' + if api_implementation.Type() != 'python': + pass + else: + pool = copy.deepcopy(self.pool) + file_descriptor = unittest_pb2.DESCRIPTOR + pool._AddDescriptor( + file_descriptor.message_types_by_name['TestAllTypes']) + pool._AddEnumDescriptor( + file_descriptor.enum_types_by_name['ForeignEnum']) + pool._AddServiceDescriptor( + file_descriptor.services_by_name['TestService']) + pool._AddExtensionDescriptor( + file_descriptor.extensions_by_name['optional_int32_extension']) + pool.Add(unittest_fd) + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + pool.Add(conflict_fd) + self.assertTrue(len(w)) + self.assertIs(w[0].category, RuntimeWarning) + self.assertIn('Conflict register for file "other_file": ', + str(w[0].message)) + pool.FindFileByName(unittest_fd.name) + with self.assertRaises(TypeError): + pool.FindFileByName(conflict_fd.name) + + +@testing_refleaks.TestCase +class DefaultDescriptorPoolTest(DescriptorPoolTestBase, unittest.TestCase): + + def setUp(self): + self.pool = descriptor_pool.Default() + self.factory_test1_fd = descriptor_pb2.FileDescriptorProto.FromString( + factory_test1_pb2.DESCRIPTOR.serialized_pb) + self.factory_test2_fd = descriptor_pb2.FileDescriptorProto.FromString( + factory_test2_pb2.DESCRIPTOR.serialized_pb) + + def testFindMethods(self): + self.assertIs( + self.pool.FindFileByName('google/protobuf/unittest.proto'), + unittest_pb2.DESCRIPTOR) + self.assertIs( + self.pool.FindMessageTypeByName('protobuf_unittest.TestAllTypes'), + unittest_pb2.TestAllTypes.DESCRIPTOR) + self.assertIs( + self.pool.FindFieldByName( + 'protobuf_unittest.TestAllTypes.optional_int32'), + unittest_pb2.TestAllTypes.DESCRIPTOR.fields_by_name['optional_int32']) + self.assertIs( + self.pool.FindEnumTypeByName('protobuf_unittest.ForeignEnum'), + unittest_pb2.ForeignEnum.DESCRIPTOR) + self.assertIs( + self.pool.FindExtensionByName( + 'protobuf_unittest.optional_int32_extension'), + unittest_pb2.DESCRIPTOR.extensions_by_name['optional_int32_extension']) + self.assertIs( + self.pool.FindOneofByName('protobuf_unittest.TestAllTypes.oneof_field'), + unittest_pb2.TestAllTypes.DESCRIPTOR.oneofs_by_name['oneof_field']) + self.assertIs( + self.pool.FindServiceByName('protobuf_unittest.TestService'), + unittest_pb2.DESCRIPTOR.services_by_name['TestService']) + + +@testing_refleaks.TestCase +class CreateDescriptorPoolTest(DescriptorPoolTestBase, unittest.TestCase): + + def setUp(self): + self.pool = descriptor_pool.DescriptorPool() + self.factory_test1_fd = descriptor_pb2.FileDescriptorProto.FromString( + factory_test1_pb2.DESCRIPTOR.serialized_pb) + self.factory_test2_fd = descriptor_pb2.FileDescriptorProto.FromString( + factory_test2_pb2.DESCRIPTOR.serialized_pb) + self.pool.Add(self.factory_test1_fd) + self.pool.Add(self.factory_test2_fd) + + self.pool.Add(descriptor_pb2.FileDescriptorProto.FromString( + unittest_import_public_pb2.DESCRIPTOR.serialized_pb)) + self.pool.Add(descriptor_pb2.FileDescriptorProto.FromString( + unittest_import_pb2.DESCRIPTOR.serialized_pb)) + self.pool.Add(descriptor_pb2.FileDescriptorProto.FromString( + unittest_pb2.DESCRIPTOR.serialized_pb)) + self.pool.Add(descriptor_pb2.FileDescriptorProto.FromString( + no_package_pb2.DESCRIPTOR.serialized_pb)) + + +@testing_refleaks.TestCase +class SecondaryDescriptorFromDescriptorDB(DescriptorPoolTestBase, + unittest.TestCase): + + def setUp(self): + self.factory_test1_fd = descriptor_pb2.FileDescriptorProto.FromString( + factory_test1_pb2.DESCRIPTOR.serialized_pb) + self.factory_test2_fd = descriptor_pb2.FileDescriptorProto.FromString( + factory_test2_pb2.DESCRIPTOR.serialized_pb) + self.db = descriptor_database.DescriptorDatabase() + self.db.Add(self.factory_test1_fd) + self.db.Add(self.factory_test2_fd) + self.db.Add(descriptor_pb2.FileDescriptorProto.FromString( + unittest_import_public_pb2.DESCRIPTOR.serialized_pb)) + self.db.Add(descriptor_pb2.FileDescriptorProto.FromString( + unittest_import_pb2.DESCRIPTOR.serialized_pb)) + self.db.Add(descriptor_pb2.FileDescriptorProto.FromString( + unittest_pb2.DESCRIPTOR.serialized_pb)) + self.db.Add(descriptor_pb2.FileDescriptorProto.FromString( + no_package_pb2.DESCRIPTOR.serialized_pb)) + self.pool = descriptor_pool.DescriptorPool(descriptor_db=self.db) + + def testErrorCollector(self): + file_proto = descriptor_pb2.FileDescriptorProto() + file_proto.package = 'collector' + file_proto.name = 'error_file' + message_type = file_proto.message_type.add() + message_type.name = 'ErrorMessage' + field = message_type.field.add() + field.number = 1 + field.name = 'nested_message_field' + field.label = descriptor.FieldDescriptor.LABEL_OPTIONAL + field.type = descriptor.FieldDescriptor.TYPE_MESSAGE + field.type_name = 'SubMessage' + oneof = message_type.oneof_decl.add() + oneof.name = 'MyOneof' + enum_type = file_proto.enum_type.add() + enum_type.name = 'MyEnum' + enum_value = enum_type.value.add() + enum_value.name = 'MyEnumValue' + enum_value.number = 0 + self.db.Add(file_proto) + + self.assertRaisesRegex(KeyError, 'SubMessage', + self.pool.FindMessageTypeByName, + 'collector.ErrorMessage') + self.assertRaisesRegex(KeyError, 'SubMessage', self.pool.FindFileByName, + 'error_file') + with self.assertRaises(KeyError) as exc: + self.pool.FindFileByName('none_file') + self.assertIn(str(exc.exception), ('\'none_file\'', + '\"Couldn\'t find file none_file\"')) + + # Pure python _ConvertFileProtoToFileDescriptor() method has side effect + # that all the symbols found in the file will load into the pool even the + # file can not build. So when FindMessageTypeByName('ErrorMessage') was + # called the first time, a KeyError will be raised but call the find + # method later will return a descriptor which is not build. + # TODO(jieluo): fix pure python to revert the load if file can not be build + if api_implementation.Type() != 'python': + error_msg = ('Invalid proto descriptor for file "error_file":\\n ' + 'collector.ErrorMessage.nested_message_field: "SubMessage" ' + 'is not defined.\\n collector.ErrorMessage.MyOneof: Oneof ' + 'must have at least one field.\\n\'') + with self.assertRaises(KeyError) as exc: + self.pool.FindMessageTypeByName('collector.ErrorMessage') + self.assertEqual(str(exc.exception), '\'Couldn\\\'t build file for ' + 'message collector.ErrorMessage\\n' + error_msg) + + with self.assertRaises(KeyError) as exc: + self.pool.FindFieldByName('collector.ErrorMessage.nested_message_field') + self.assertEqual(str(exc.exception), '\'Couldn\\\'t build file for field' + ' collector.ErrorMessage.nested_message_field\\n' + + error_msg) + + with self.assertRaises(KeyError) as exc: + self.pool.FindEnumTypeByName('collector.MyEnum') + self.assertEqual(str(exc.exception), '\'Couldn\\\'t build file for enum' + ' collector.MyEnum\\n' + error_msg) + + with self.assertRaises(KeyError) as exc: + self.pool.FindFileContainingSymbol('collector.MyEnumValue') + self.assertEqual(str(exc.exception), '\'Couldn\\\'t build file for symbol' + ' collector.MyEnumValue\\n' + error_msg) + + with self.assertRaises(KeyError) as exc: + self.pool.FindOneofByName('collector.ErrorMessage.MyOneof') + self.assertEqual(str(exc.exception), '\'Couldn\\\'t build file for oneof' + ' collector.ErrorMessage.MyOneof\\n' + error_msg) + + +class ProtoFile(object): + + def __init__(self, name, package, messages, dependencies=None, + public_dependencies=None): + self.name = name + self.package = package + self.messages = messages + self.dependencies = dependencies or [] + self.public_dependencies = public_dependencies or [] + + def CheckFile(self, test, pool): + file_desc = pool.FindFileByName(self.name) + test.assertEqual(self.name, file_desc.name) + test.assertEqual(self.package, file_desc.package) + dependencies_names = [f.name for f in file_desc.dependencies] + test.assertEqual(self.dependencies, dependencies_names) + public_dependencies_names = [f.name for f in file_desc.public_dependencies] + test.assertEqual(self.public_dependencies, public_dependencies_names) + for name, msg_type in self.messages.items(): + msg_type.CheckType(test, None, name, file_desc) + + +class EnumType(object): + + def __init__(self, values): + self.values = values + + def CheckType(self, test, msg_desc, name, file_desc): + enum_desc = msg_desc.enum_types_by_name[name] + test.assertEqual(name, enum_desc.name) + expected_enum_full_name = '.'.join([msg_desc.full_name, name]) + test.assertEqual(expected_enum_full_name, enum_desc.full_name) + test.assertEqual(msg_desc, enum_desc.containing_type) + test.assertEqual(file_desc, enum_desc.file) + for index, (value, number) in enumerate(self.values): + value_desc = enum_desc.values_by_name[value] + test.assertEqual(value, value_desc.name) + test.assertEqual(index, value_desc.index) + test.assertEqual(number, value_desc.number) + test.assertEqual(enum_desc, value_desc.type) + test.assertIn(value, msg_desc.enum_values_by_name) + + +class MessageType(object): + + def __init__(self, type_dict, field_list, is_extendable=False, + extensions=None): + self.type_dict = type_dict + self.field_list = field_list + self.is_extendable = is_extendable + self.extensions = extensions or [] + + def CheckType(self, test, containing_type_desc, name, file_desc): + if containing_type_desc is None: + desc = file_desc.message_types_by_name[name] + expected_full_name = '.'.join([file_desc.package, name]) + else: + desc = containing_type_desc.nested_types_by_name[name] + expected_full_name = '.'.join([containing_type_desc.full_name, name]) + + test.assertEqual(name, desc.name) + test.assertEqual(expected_full_name, desc.full_name) + test.assertEqual(containing_type_desc, desc.containing_type) + test.assertEqual(desc.file, file_desc) + test.assertEqual(self.is_extendable, desc.is_extendable) + for name, subtype in self.type_dict.items(): + subtype.CheckType(test, desc, name, file_desc) + + for index, (name, field) in enumerate(self.field_list): + field.CheckField(test, desc, name, index, file_desc) + + for index, (name, field) in enumerate(self.extensions): + field.CheckField(test, desc, name, index, file_desc) + + +class EnumField(object): + + def __init__(self, number, type_name, default_value): + self.number = number + self.type_name = type_name + self.default_value = default_value + + def CheckField(self, test, msg_desc, name, index, file_desc): + field_desc = msg_desc.fields_by_name[name] + enum_desc = msg_desc.enum_types_by_name[self.type_name] + test.assertEqual(name, field_desc.name) + expected_field_full_name = '.'.join([msg_desc.full_name, name]) + test.assertEqual(expected_field_full_name, field_desc.full_name) + test.assertEqual(index, field_desc.index) + test.assertEqual(self.number, field_desc.number) + test.assertEqual(descriptor.FieldDescriptor.TYPE_ENUM, field_desc.type) + test.assertEqual(descriptor.FieldDescriptor.CPPTYPE_ENUM, + field_desc.cpp_type) + test.assertTrue(field_desc.has_default_value) + test.assertEqual(enum_desc.values_by_name[self.default_value].number, + field_desc.default_value) + test.assertFalse(enum_desc.values_by_name[self.default_value].has_options) + test.assertEqual(msg_desc, field_desc.containing_type) + test.assertEqual(enum_desc, field_desc.enum_type) + test.assertEqual(file_desc, enum_desc.file) + + +class MessageField(object): + + def __init__(self, number, type_name): + self.number = number + self.type_name = type_name + + def CheckField(self, test, msg_desc, name, index, file_desc): + field_desc = msg_desc.fields_by_name[name] + field_type_desc = msg_desc.nested_types_by_name[self.type_name] + test.assertEqual(name, field_desc.name) + expected_field_full_name = '.'.join([msg_desc.full_name, name]) + test.assertEqual(expected_field_full_name, field_desc.full_name) + test.assertEqual(index, field_desc.index) + test.assertEqual(self.number, field_desc.number) + test.assertEqual(descriptor.FieldDescriptor.TYPE_MESSAGE, field_desc.type) + test.assertEqual(descriptor.FieldDescriptor.CPPTYPE_MESSAGE, + field_desc.cpp_type) + test.assertFalse(field_desc.has_default_value) + test.assertEqual(msg_desc, field_desc.containing_type) + test.assertEqual(field_type_desc, field_desc.message_type) + test.assertEqual(file_desc, field_desc.file) + test.assertEqual(field_desc.default_value, None) + + +class StringField(object): + + def __init__(self, number, default_value): + self.number = number + self.default_value = default_value + + def CheckField(self, test, msg_desc, name, index, file_desc): + field_desc = msg_desc.fields_by_name[name] + test.assertEqual(name, field_desc.name) + expected_field_full_name = '.'.join([msg_desc.full_name, name]) + test.assertEqual(expected_field_full_name, field_desc.full_name) + test.assertEqual(index, field_desc.index) + test.assertEqual(self.number, field_desc.number) + test.assertEqual(descriptor.FieldDescriptor.TYPE_STRING, field_desc.type) + test.assertEqual(descriptor.FieldDescriptor.CPPTYPE_STRING, + field_desc.cpp_type) + test.assertTrue(field_desc.has_default_value) + test.assertEqual(self.default_value, field_desc.default_value) + test.assertEqual(file_desc, field_desc.file) + + +class ExtensionField(object): + + def __init__(self, number, extended_type): + self.number = number + self.extended_type = extended_type + + def CheckField(self, test, msg_desc, name, index, file_desc): + field_desc = msg_desc.extensions_by_name[name] + test.assertEqual(name, field_desc.name) + expected_field_full_name = '.'.join([msg_desc.full_name, name]) + test.assertEqual(expected_field_full_name, field_desc.full_name) + test.assertEqual(self.number, field_desc.number) + test.assertEqual(index, field_desc.index) + test.assertEqual(descriptor.FieldDescriptor.TYPE_MESSAGE, field_desc.type) + test.assertEqual(descriptor.FieldDescriptor.CPPTYPE_MESSAGE, + field_desc.cpp_type) + test.assertFalse(field_desc.has_default_value) + test.assertTrue(field_desc.is_extension) + test.assertEqual(msg_desc, field_desc.extension_scope) + test.assertEqual(msg_desc, field_desc.message_type) + test.assertEqual(self.extended_type, field_desc.containing_type.name) + test.assertEqual(file_desc, field_desc.file) + + +@testing_refleaks.TestCase +class AddDescriptorTest(unittest.TestCase): + + def _TestMessage(self, prefix): + pool = descriptor_pool.DescriptorPool() + pool._AddDescriptor(unittest_pb2.TestAllTypes.DESCRIPTOR) + self.assertEqual( + 'protobuf_unittest.TestAllTypes', + pool.FindMessageTypeByName( + prefix + 'protobuf_unittest.TestAllTypes').full_name) + + # AddDescriptor is not recursive. + with self.assertRaises(KeyError): + pool.FindMessageTypeByName( + prefix + 'protobuf_unittest.TestAllTypes.NestedMessage') + + pool._AddDescriptor(unittest_pb2.TestAllTypes.NestedMessage.DESCRIPTOR) + self.assertEqual( + 'protobuf_unittest.TestAllTypes.NestedMessage', + pool.FindMessageTypeByName( + prefix + 'protobuf_unittest.TestAllTypes.NestedMessage').full_name) + + # Files are implicitly also indexed when messages are added. + self.assertEqual( + 'google/protobuf/unittest.proto', + pool.FindFileByName( + 'google/protobuf/unittest.proto').name) + + self.assertEqual( + 'google/protobuf/unittest.proto', + pool.FindFileContainingSymbol( + prefix + 'protobuf_unittest.TestAllTypes.NestedMessage').name) + + @unittest.skipIf(api_implementation.Type() != 'python', + 'Only pure python allows _Add*()') + def testMessage(self): + self._TestMessage('') + self._TestMessage('.') + + def _TestEnum(self, prefix): + pool = descriptor_pool.DescriptorPool() + if api_implementation.Type() == 'cpp': + pool.AddEnumDescriptor(unittest_pb2.ForeignEnum.DESCRIPTOR) + else: + pool._AddEnumDescriptor(unittest_pb2.ForeignEnum.DESCRIPTOR) + self.assertEqual( + 'protobuf_unittest.ForeignEnum', + pool.FindEnumTypeByName( + prefix + 'protobuf_unittest.ForeignEnum').full_name) + + # AddEnumDescriptor is not recursive. + with self.assertRaises(KeyError): + pool.FindEnumTypeByName( + prefix + 'protobuf_unittest.ForeignEnum.NestedEnum') + + if api_implementation.Type() == 'cpp': + pool.AddEnumDescriptor(unittest_pb2.TestAllTypes.NestedEnum.DESCRIPTOR) + else: + pool._AddEnumDescriptor(unittest_pb2.TestAllTypes.NestedEnum.DESCRIPTOR) + self.assertEqual( + 'protobuf_unittest.TestAllTypes.NestedEnum', + pool.FindEnumTypeByName( + prefix + 'protobuf_unittest.TestAllTypes.NestedEnum').full_name) + + # Files are implicitly also indexed when enums are added. + self.assertEqual( + 'google/protobuf/unittest.proto', + pool.FindFileByName( + 'google/protobuf/unittest.proto').name) + + self.assertEqual( + 'google/protobuf/unittest.proto', + pool.FindFileContainingSymbol( + prefix + 'protobuf_unittest.TestAllTypes.NestedEnum').name) + + @unittest.skipIf(api_implementation.Type() != 'python', + 'Only pure python allows _Add*()') + def testEnum(self): + self._TestEnum('') + self._TestEnum('.') + + @unittest.skipIf(api_implementation.Type() != 'python', + 'Only pure python allows _Add*()') + def testService(self): + pool = descriptor_pool.DescriptorPool() + with self.assertRaises(KeyError): + pool.FindServiceByName('protobuf_unittest.TestService') + pool._AddServiceDescriptor(unittest_pb2._TESTSERVICE) + self.assertEqual( + 'protobuf_unittest.TestService', + pool.FindServiceByName('protobuf_unittest.TestService').full_name) + + @unittest.skipIf(api_implementation.Type() != 'python', + 'Only pure python allows _Add*()') + def testFile(self): + pool = descriptor_pool.DescriptorPool() + pool._AddFileDescriptor(unittest_pb2.DESCRIPTOR) + self.assertEqual( + 'google/protobuf/unittest.proto', + pool.FindFileByName( + 'google/protobuf/unittest.proto').name) + + # AddFileDescriptor is not recursive; messages and enums within files must + # be explicitly registered. + with self.assertRaises(KeyError): + pool.FindFileContainingSymbol( + 'protobuf_unittest.TestAllTypes') + + def testEmptyDescriptorPool(self): + # Check that an empty DescriptorPool() contains no messages. + pool = descriptor_pool.DescriptorPool() + proto_file_name = descriptor_pb2.DESCRIPTOR.name + self.assertRaises(KeyError, pool.FindFileByName, proto_file_name) + # Add the above file to the pool + file_descriptor = descriptor_pb2.FileDescriptorProto() + descriptor_pb2.DESCRIPTOR.CopyToProto(file_descriptor) + pool.Add(file_descriptor) + # Now it exists. + self.assertTrue(pool.FindFileByName(proto_file_name)) + + def testCustomDescriptorPool(self): + # Create a new pool, and add a file descriptor. + pool = descriptor_pool.DescriptorPool() + file_desc = descriptor_pb2.FileDescriptorProto( + name='some/file.proto', package='package') + file_desc.message_type.add(name='Message') + pool.Add(file_desc) + self.assertEqual(pool.FindFileByName('some/file.proto').name, + 'some/file.proto') + self.assertEqual(pool.FindMessageTypeByName('package.Message').name, + 'Message') + # Test no package + file_proto = descriptor_pb2.FileDescriptorProto( + name='some/filename/container.proto') + message_proto = file_proto.message_type.add( + name='TopMessage') + message_proto.field.add( + name='bb', + number=1, + type=descriptor_pb2.FieldDescriptorProto.TYPE_INT32, + label=descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL) + enum_proto = file_proto.enum_type.add(name='TopEnum') + enum_proto.value.add(name='FOREIGN_FOO', number=4) + file_proto.service.add(name='TopService') + pool = descriptor_pool.DescriptorPool() + pool.Add(file_proto) + self.assertEqual('TopMessage', + pool.FindMessageTypeByName('TopMessage').name) + self.assertEqual('TopEnum', pool.FindEnumTypeByName('TopEnum').name) + self.assertEqual('TopService', pool.FindServiceByName('TopService').name) + + def testFileDescriptorOptionsWithCustomDescriptorPool(self): + # Create a descriptor pool, and add a new FileDescriptorProto to it. + pool = descriptor_pool.DescriptorPool() + file_name = 'file_descriptor_options_with_custom_descriptor_pool.proto' + file_descriptor_proto = descriptor_pb2.FileDescriptorProto(name=file_name) + extension_id = file_options_test_pb2.foo_options + file_descriptor_proto.options.Extensions[extension_id].foo_name = 'foo' + pool.Add(file_descriptor_proto) + # The options set on the FileDescriptorProto should be available in the + # descriptor even if they contain extensions that cannot be deserialized + # using the pool. + file_descriptor = pool.FindFileByName(file_name) + options = file_descriptor.GetOptions() + self.assertEqual('foo', options.Extensions[extension_id].foo_name) + # The object returned by GetOptions() is cached. + self.assertIs(options, file_descriptor.GetOptions()) + + def testAddTypeError(self): + pool = descriptor_pool.DescriptorPool() + if api_implementation.Type() != 'python': + with self.assertRaises(TypeError): + pool.AddDescriptor(0) + with self.assertRaises(TypeError): + pool.AddEnumDescriptor(0) + with self.assertRaises(TypeError): + pool.AddServiceDescriptor(0) + with self.assertRaises(TypeError): + pool.AddExtensionDescriptor(0) + with self.assertRaises(TypeError): + pool.AddFileDescriptor(0) + else: + with self.assertRaises(TypeError): + pool._AddDescriptor(0) + with self.assertRaises(TypeError): + pool._AddEnumDescriptor(0) + with self.assertRaises(TypeError): + pool._AddServiceDescriptor(0) + with self.assertRaises(TypeError): + pool._AddExtensionDescriptor(0) + with self.assertRaises(TypeError): + pool._AddFileDescriptor(0) + + +TEST1_FILE = ProtoFile( + 'google/protobuf/internal/descriptor_pool_test1.proto', + 'google.protobuf.python.internal', + { + 'DescriptorPoolTest1': MessageType({ + 'NestedEnum': EnumType([('ALPHA', 1), ('BETA', 2)]), + 'NestedMessage': MessageType({ + 'NestedEnum': EnumType([('EPSILON', 5), ('ZETA', 6)]), + 'DeepNestedMessage': MessageType({ + 'NestedEnum': EnumType([('ETA', 7), ('THETA', 8)]), + }, [ + ('nested_enum', EnumField(1, 'NestedEnum', 'ETA')), + ('nested_field', StringField(2, 'theta')), + ]), + }, [ + ('nested_enum', EnumField(1, 'NestedEnum', 'ZETA')), + ('nested_field', StringField(2, 'beta')), + ('deep_nested_message', MessageField(3, 'DeepNestedMessage')), + ]) + }, [ + ('nested_enum', EnumField(1, 'NestedEnum', 'BETA')), + ('nested_message', MessageField(2, 'NestedMessage')), + ], is_extendable=True), + + 'DescriptorPoolTest2': MessageType({ + 'NestedEnum': EnumType([('GAMMA', 3), ('DELTA', 4)]), + 'NestedMessage': MessageType({ + 'NestedEnum': EnumType([('IOTA', 9), ('KAPPA', 10)]), + 'DeepNestedMessage': MessageType({ + 'NestedEnum': EnumType([('LAMBDA', 11), ('MU', 12)]), + }, [ + ('nested_enum', EnumField(1, 'NestedEnum', 'MU')), + ('nested_field', StringField(2, 'lambda')), + ]), + }, [ + ('nested_enum', EnumField(1, 'NestedEnum', 'IOTA')), + ('nested_field', StringField(2, 'delta')), + ('deep_nested_message', MessageField(3, 'DeepNestedMessage')), + ]) + }, [ + ('nested_enum', EnumField(1, 'NestedEnum', 'GAMMA')), + ('nested_message', MessageField(2, 'NestedMessage')), + ]), + }) + + +TEST2_FILE = ProtoFile( + 'google/protobuf/internal/descriptor_pool_test2.proto', + 'google.protobuf.python.internal', + { + 'DescriptorPoolTest3': MessageType({ + 'NestedEnum': EnumType([('NU', 13), ('XI', 14)]), + 'NestedMessage': MessageType({ + 'NestedEnum': EnumType([('OMICRON', 15), ('PI', 16)]), + 'DeepNestedMessage': MessageType({ + 'NestedEnum': EnumType([('RHO', 17), ('SIGMA', 18)]), + }, [ + ('nested_enum', EnumField(1, 'NestedEnum', 'RHO')), + ('nested_field', StringField(2, 'sigma')), + ]), + }, [ + ('nested_enum', EnumField(1, 'NestedEnum', 'PI')), + ('nested_field', StringField(2, 'nu')), + ('deep_nested_message', MessageField(3, 'DeepNestedMessage')), + ]) + }, [ + ('nested_enum', EnumField(1, 'NestedEnum', 'XI')), + ('nested_message', MessageField(2, 'NestedMessage')), + ], extensions=[ + ('descriptor_pool_test', + ExtensionField(1001, 'DescriptorPoolTest1')), + ]), + }, + dependencies=['google/protobuf/internal/descriptor_pool_test1.proto', + 'google/protobuf/internal/more_messages.proto'], + public_dependencies=['google/protobuf/internal/more_messages.proto']) + + +if __name__ == '__main__': + unittest.main() diff --git a/virt/lib/python3.9/site-packages/google/protobuf/internal/descriptor_test 3.py b/virt/lib/python3.9/site-packages/google/protobuf/internal/descriptor_test 3.py new file mode 100644 index 00000000..b8a35c73 --- /dev/null +++ b/virt/lib/python3.9/site-packages/google/protobuf/internal/descriptor_test 3.py @@ -0,0 +1,1076 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# 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 Google 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 +# OWNER 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. + +"""Unittest for google.protobuf.internal.descriptor.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import unittest +import warnings + +from google.protobuf import unittest_custom_options_pb2 +from google.protobuf import unittest_import_pb2 +from google.protobuf import unittest_pb2 +from google.protobuf import descriptor_pb2 +from google.protobuf.internal import api_implementation +from google.protobuf.internal import test_util +from google.protobuf import descriptor +from google.protobuf import descriptor_pool +from google.protobuf import symbol_database +from google.protobuf import text_format + + +TEST_EMPTY_MESSAGE_DESCRIPTOR_ASCII = """ +name: 'TestEmptyMessage' +""" + +TEST_FILE_DESCRIPTOR_DEBUG = """syntax = "proto2"; + +package protobuf_unittest; + +message NestedMessage { + enum ForeignEnum { + FOREIGN_FOO = 4; + FOREIGN_BAR = 5; + FOREIGN_BAZ = 6; + } + optional int32 bb = 1; +} + +message ResponseMessage { +} + +service Service { + rpc CallMethod(.protobuf_unittest.NestedMessage) returns (.protobuf_unittest.ResponseMessage); +} + +""" + + +warnings.simplefilter('error', DeprecationWarning) + + +class DescriptorTest(unittest.TestCase): + + def setUp(self): + file_proto = descriptor_pb2.FileDescriptorProto( + name='some/filename/some.proto', + package='protobuf_unittest') + message_proto = file_proto.message_type.add( + name='NestedMessage') + message_proto.field.add( + name='bb', + number=1, + type=descriptor_pb2.FieldDescriptorProto.TYPE_INT32, + label=descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL) + enum_proto = message_proto.enum_type.add( + name='ForeignEnum') + enum_proto.value.add(name='FOREIGN_FOO', number=4) + enum_proto.value.add(name='FOREIGN_BAR', number=5) + enum_proto.value.add(name='FOREIGN_BAZ', number=6) + + file_proto.message_type.add(name='ResponseMessage') + service_proto = file_proto.service.add( + name='Service') + method_proto = service_proto.method.add( + name='CallMethod', + input_type='.protobuf_unittest.NestedMessage', + output_type='.protobuf_unittest.ResponseMessage') + + # Note: Calling DescriptorPool.Add() multiple times with the same file only + # works if the input is canonical; in particular, all type names must be + # fully qualified. + self.pool = self.GetDescriptorPool() + self.pool.Add(file_proto) + self.my_file = self.pool.FindFileByName(file_proto.name) + self.my_message = self.my_file.message_types_by_name[message_proto.name] + self.my_enum = self.my_message.enum_types_by_name[enum_proto.name] + self.my_service = self.my_file.services_by_name[service_proto.name] + self.my_method = self.my_service.methods_by_name[method_proto.name] + + def GetDescriptorPool(self): + return symbol_database.Default().pool + + def testEnumValueName(self): + self.assertEqual(self.my_message.EnumValueName('ForeignEnum', 4), + 'FOREIGN_FOO') + + self.assertEqual( + self.my_message.enum_types_by_name[ + 'ForeignEnum'].values_by_number[4].name, + self.my_message.EnumValueName('ForeignEnum', 4)) + with self.assertRaises(KeyError): + self.my_message.EnumValueName('ForeignEnum', 999) + with self.assertRaises(KeyError): + self.my_message.EnumValueName('NoneEnum', 999) + with self.assertRaises(TypeError): + self.my_message.EnumValueName() + + def testEnumFixups(self): + self.assertEqual(self.my_enum, self.my_enum.values[0].type) + + def testContainingTypeFixups(self): + self.assertEqual(self.my_message, self.my_message.fields[0].containing_type) + self.assertEqual(self.my_message, self.my_enum.containing_type) + + def testContainingServiceFixups(self): + self.assertEqual(self.my_service, self.my_method.containing_service) + + @unittest.skipIf( + api_implementation.Type() == 'python', + 'GetDebugString is only available with the cpp implementation', + ) + def testGetDebugString(self): + self.assertEqual(self.my_file.GetDebugString(), TEST_FILE_DESCRIPTOR_DEBUG) + + def testGetOptions(self): + self.assertEqual(self.my_enum.GetOptions(), + descriptor_pb2.EnumOptions()) + self.assertEqual(self.my_enum.values[0].GetOptions(), + descriptor_pb2.EnumValueOptions()) + self.assertEqual(self.my_message.GetOptions(), + descriptor_pb2.MessageOptions()) + self.assertEqual(self.my_message.fields[0].GetOptions(), + descriptor_pb2.FieldOptions()) + self.assertEqual(self.my_method.GetOptions(), + descriptor_pb2.MethodOptions()) + self.assertEqual(self.my_service.GetOptions(), + descriptor_pb2.ServiceOptions()) + + def testSimpleCustomOptions(self): + file_descriptor = unittest_custom_options_pb2.DESCRIPTOR + message_descriptor = (unittest_custom_options_pb2. + TestMessageWithCustomOptions.DESCRIPTOR) + field_descriptor = message_descriptor.fields_by_name['field1'] + oneof_descriptor = message_descriptor.oneofs_by_name['AnOneof'] + enum_descriptor = message_descriptor.enum_types_by_name['AnEnum'] + enum_value_descriptor = (message_descriptor. + enum_values_by_name['ANENUM_VAL2']) + other_enum_value_descriptor = (message_descriptor. + enum_values_by_name['ANENUM_VAL1']) + service_descriptor = (unittest_custom_options_pb2. + TestServiceWithCustomOptions.DESCRIPTOR) + method_descriptor = service_descriptor.FindMethodByName('Foo') + + file_options = file_descriptor.GetOptions() + file_opt1 = unittest_custom_options_pb2.file_opt1 + self.assertEqual(9876543210, file_options.Extensions[file_opt1]) + message_options = message_descriptor.GetOptions() + message_opt1 = unittest_custom_options_pb2.message_opt1 + self.assertEqual(-56, message_options.Extensions[message_opt1]) + field_options = field_descriptor.GetOptions() + field_opt1 = unittest_custom_options_pb2.field_opt1 + self.assertEqual(8765432109, field_options.Extensions[field_opt1]) + field_opt2 = unittest_custom_options_pb2.field_opt2 + self.assertEqual(42, field_options.Extensions[field_opt2]) + oneof_options = oneof_descriptor.GetOptions() + oneof_opt1 = unittest_custom_options_pb2.oneof_opt1 + self.assertEqual(-99, oneof_options.Extensions[oneof_opt1]) + enum_options = enum_descriptor.GetOptions() + enum_opt1 = unittest_custom_options_pb2.enum_opt1 + self.assertEqual(-789, enum_options.Extensions[enum_opt1]) + enum_value_options = enum_value_descriptor.GetOptions() + enum_value_opt1 = unittest_custom_options_pb2.enum_value_opt1 + self.assertEqual(123, enum_value_options.Extensions[enum_value_opt1]) + + service_options = service_descriptor.GetOptions() + service_opt1 = unittest_custom_options_pb2.service_opt1 + self.assertEqual(-9876543210, service_options.Extensions[service_opt1]) + method_options = method_descriptor.GetOptions() + method_opt1 = unittest_custom_options_pb2.method_opt1 + self.assertEqual(unittest_custom_options_pb2.METHODOPT1_VAL2, + method_options.Extensions[method_opt1]) + + message_descriptor = ( + unittest_custom_options_pb2.DummyMessageContainingEnum.DESCRIPTOR) + self.assertTrue(file_descriptor.has_options) + self.assertFalse(message_descriptor.has_options) + self.assertTrue(field_descriptor.has_options) + self.assertTrue(oneof_descriptor.has_options) + self.assertTrue(enum_descriptor.has_options) + self.assertTrue(enum_value_descriptor.has_options) + self.assertFalse(other_enum_value_descriptor.has_options) + + def testCustomOptionsCopyTo(self): + message_descriptor = (unittest_custom_options_pb2. + TestMessageWithCustomOptions.DESCRIPTOR) + message_proto = descriptor_pb2.DescriptorProto() + message_descriptor.CopyToProto(message_proto) + self.assertEqual(len(message_proto.options.ListFields()), + 2) + + def testDifferentCustomOptionTypes(self): + kint32min = -2**31 + kint64min = -2**63 + kint32max = 2**31 - 1 + kint64max = 2**63 - 1 + kuint32max = 2**32 - 1 + kuint64max = 2**64 - 1 + + message_descriptor =\ + unittest_custom_options_pb2.CustomOptionMinIntegerValues.DESCRIPTOR + message_options = message_descriptor.GetOptions() + self.assertEqual(False, message_options.Extensions[ + unittest_custom_options_pb2.bool_opt]) + self.assertEqual(kint32min, message_options.Extensions[ + unittest_custom_options_pb2.int32_opt]) + self.assertEqual(kint64min, message_options.Extensions[ + unittest_custom_options_pb2.int64_opt]) + self.assertEqual(0, message_options.Extensions[ + unittest_custom_options_pb2.uint32_opt]) + self.assertEqual(0, message_options.Extensions[ + unittest_custom_options_pb2.uint64_opt]) + self.assertEqual(kint32min, message_options.Extensions[ + unittest_custom_options_pb2.sint32_opt]) + self.assertEqual(kint64min, message_options.Extensions[ + unittest_custom_options_pb2.sint64_opt]) + self.assertEqual(0, message_options.Extensions[ + unittest_custom_options_pb2.fixed32_opt]) + self.assertEqual(0, message_options.Extensions[ + unittest_custom_options_pb2.fixed64_opt]) + self.assertEqual(kint32min, message_options.Extensions[ + unittest_custom_options_pb2.sfixed32_opt]) + self.assertEqual(kint64min, message_options.Extensions[ + unittest_custom_options_pb2.sfixed64_opt]) + + message_descriptor =\ + unittest_custom_options_pb2.CustomOptionMaxIntegerValues.DESCRIPTOR + message_options = message_descriptor.GetOptions() + self.assertEqual(True, message_options.Extensions[ + unittest_custom_options_pb2.bool_opt]) + self.assertEqual(kint32max, message_options.Extensions[ + unittest_custom_options_pb2.int32_opt]) + self.assertEqual(kint64max, message_options.Extensions[ + unittest_custom_options_pb2.int64_opt]) + self.assertEqual(kuint32max, message_options.Extensions[ + unittest_custom_options_pb2.uint32_opt]) + self.assertEqual(kuint64max, message_options.Extensions[ + unittest_custom_options_pb2.uint64_opt]) + self.assertEqual(kint32max, message_options.Extensions[ + unittest_custom_options_pb2.sint32_opt]) + self.assertEqual(kint64max, message_options.Extensions[ + unittest_custom_options_pb2.sint64_opt]) + self.assertEqual(kuint32max, message_options.Extensions[ + unittest_custom_options_pb2.fixed32_opt]) + self.assertEqual(kuint64max, message_options.Extensions[ + unittest_custom_options_pb2.fixed64_opt]) + self.assertEqual(kint32max, message_options.Extensions[ + unittest_custom_options_pb2.sfixed32_opt]) + self.assertEqual(kint64max, message_options.Extensions[ + unittest_custom_options_pb2.sfixed64_opt]) + + message_descriptor =\ + unittest_custom_options_pb2.CustomOptionOtherValues.DESCRIPTOR + message_options = message_descriptor.GetOptions() + self.assertEqual(-100, message_options.Extensions[ + unittest_custom_options_pb2.int32_opt]) + self.assertAlmostEqual(12.3456789, message_options.Extensions[ + unittest_custom_options_pb2.float_opt], 6) + self.assertAlmostEqual(1.234567890123456789, message_options.Extensions[ + unittest_custom_options_pb2.double_opt]) + self.assertEqual("Hello, \"World\"", message_options.Extensions[ + unittest_custom_options_pb2.string_opt]) + self.assertEqual(b"Hello\0World", message_options.Extensions[ + unittest_custom_options_pb2.bytes_opt]) + dummy_enum = unittest_custom_options_pb2.DummyMessageContainingEnum + self.assertEqual( + dummy_enum.TEST_OPTION_ENUM_TYPE2, + message_options.Extensions[unittest_custom_options_pb2.enum_opt]) + + message_descriptor =\ + unittest_custom_options_pb2.SettingRealsFromPositiveInts.DESCRIPTOR + message_options = message_descriptor.GetOptions() + self.assertAlmostEqual(12, message_options.Extensions[ + unittest_custom_options_pb2.float_opt], 6) + self.assertAlmostEqual(154, message_options.Extensions[ + unittest_custom_options_pb2.double_opt]) + + message_descriptor =\ + unittest_custom_options_pb2.SettingRealsFromNegativeInts.DESCRIPTOR + message_options = message_descriptor.GetOptions() + self.assertAlmostEqual(-12, message_options.Extensions[ + unittest_custom_options_pb2.float_opt], 6) + self.assertAlmostEqual(-154, message_options.Extensions[ + unittest_custom_options_pb2.double_opt]) + + def testComplexExtensionOptions(self): + descriptor =\ + unittest_custom_options_pb2.VariousComplexOptions.DESCRIPTOR + options = descriptor.GetOptions() + self.assertEqual(42, options.Extensions[ + unittest_custom_options_pb2.complex_opt1].foo) + self.assertEqual(324, options.Extensions[ + unittest_custom_options_pb2.complex_opt1].Extensions[ + unittest_custom_options_pb2.mooo]) + self.assertEqual(876, options.Extensions[ + unittest_custom_options_pb2.complex_opt1].Extensions[ + unittest_custom_options_pb2.corge].moo) + self.assertEqual(987, options.Extensions[ + unittest_custom_options_pb2.complex_opt2].baz) + self.assertEqual(654, options.Extensions[ + unittest_custom_options_pb2.complex_opt2].Extensions[ + unittest_custom_options_pb2.grault]) + self.assertEqual(743, options.Extensions[ + unittest_custom_options_pb2.complex_opt2].bar.foo) + self.assertEqual(1999, options.Extensions[ + unittest_custom_options_pb2.complex_opt2].bar.Extensions[ + unittest_custom_options_pb2.mooo]) + self.assertEqual(2008, options.Extensions[ + unittest_custom_options_pb2.complex_opt2].bar.Extensions[ + unittest_custom_options_pb2.corge].moo) + self.assertEqual(741, options.Extensions[ + unittest_custom_options_pb2.complex_opt2].Extensions[ + unittest_custom_options_pb2.garply].foo) + self.assertEqual(1998, options.Extensions[ + unittest_custom_options_pb2.complex_opt2].Extensions[ + unittest_custom_options_pb2.garply].Extensions[ + unittest_custom_options_pb2.mooo]) + self.assertEqual(2121, options.Extensions[ + unittest_custom_options_pb2.complex_opt2].Extensions[ + unittest_custom_options_pb2.garply].Extensions[ + unittest_custom_options_pb2.corge].moo) + self.assertEqual(1971, options.Extensions[ + unittest_custom_options_pb2.ComplexOptionType2 + .ComplexOptionType4.complex_opt4].waldo) + self.assertEqual(321, options.Extensions[ + unittest_custom_options_pb2.complex_opt2].fred.waldo) + self.assertEqual(9, options.Extensions[ + unittest_custom_options_pb2.complex_opt3].moo) + self.assertEqual(22, options.Extensions[ + unittest_custom_options_pb2.complex_opt3].complexoptiontype5.plugh) + self.assertEqual(24, options.Extensions[ + unittest_custom_options_pb2.complexopt6].xyzzy) + + # Check that aggregate options were parsed and saved correctly in + # the appropriate descriptors. + def testAggregateOptions(self): + file_descriptor = unittest_custom_options_pb2.DESCRIPTOR + message_descriptor =\ + unittest_custom_options_pb2.AggregateMessage.DESCRIPTOR + field_descriptor = message_descriptor.fields_by_name["fieldname"] + enum_descriptor = unittest_custom_options_pb2.AggregateEnum.DESCRIPTOR + enum_value_descriptor = enum_descriptor.values_by_name["VALUE"] + service_descriptor =\ + unittest_custom_options_pb2.AggregateService.DESCRIPTOR + method_descriptor = service_descriptor.FindMethodByName("Method") + + # Tests for the different types of data embedded in fileopt + file_options = file_descriptor.GetOptions().Extensions[ + unittest_custom_options_pb2.fileopt] + self.assertEqual(100, file_options.i) + self.assertEqual("FileAnnotation", file_options.s) + self.assertEqual("NestedFileAnnotation", file_options.sub.s) + self.assertEqual("FileExtensionAnnotation", file_options.file.Extensions[ + unittest_custom_options_pb2.fileopt].s) + self.assertEqual("EmbeddedMessageSetElement", file_options.mset.Extensions[ + unittest_custom_options_pb2.AggregateMessageSetElement + .message_set_extension].s) + + # Simple tests for all the other types of annotations + self.assertEqual( + "MessageAnnotation", + message_descriptor.GetOptions().Extensions[ + unittest_custom_options_pb2.msgopt].s) + self.assertEqual( + "FieldAnnotation", + field_descriptor.GetOptions().Extensions[ + unittest_custom_options_pb2.fieldopt].s) + self.assertEqual( + "EnumAnnotation", + enum_descriptor.GetOptions().Extensions[ + unittest_custom_options_pb2.enumopt].s) + self.assertEqual( + "EnumValueAnnotation", + enum_value_descriptor.GetOptions().Extensions[ + unittest_custom_options_pb2.enumvalopt].s) + self.assertEqual( + "ServiceAnnotation", + service_descriptor.GetOptions().Extensions[ + unittest_custom_options_pb2.serviceopt].s) + self.assertEqual( + "MethodAnnotation", + method_descriptor.GetOptions().Extensions[ + unittest_custom_options_pb2.methodopt].s) + + def testNestedOptions(self): + nested_message =\ + unittest_custom_options_pb2.NestedOptionType.NestedMessage.DESCRIPTOR + self.assertEqual(1001, nested_message.GetOptions().Extensions[ + unittest_custom_options_pb2.message_opt1]) + nested_field = nested_message.fields_by_name["nested_field"] + self.assertEqual(1002, nested_field.GetOptions().Extensions[ + unittest_custom_options_pb2.field_opt1]) + outer_message =\ + unittest_custom_options_pb2.NestedOptionType.DESCRIPTOR + nested_enum = outer_message.enum_types_by_name["NestedEnum"] + self.assertEqual(1003, nested_enum.GetOptions().Extensions[ + unittest_custom_options_pb2.enum_opt1]) + nested_enum_value = outer_message.enum_values_by_name["NESTED_ENUM_VALUE"] + self.assertEqual(1004, nested_enum_value.GetOptions().Extensions[ + unittest_custom_options_pb2.enum_value_opt1]) + nested_extension = outer_message.extensions_by_name["nested_extension"] + self.assertEqual(1005, nested_extension.GetOptions().Extensions[ + unittest_custom_options_pb2.field_opt2]) + + def testFileDescriptorReferences(self): + self.assertEqual(self.my_enum.file, self.my_file) + self.assertEqual(self.my_message.file, self.my_file) + + def testFileDescriptor(self): + self.assertEqual(self.my_file.name, 'some/filename/some.proto') + self.assertEqual(self.my_file.package, 'protobuf_unittest') + self.assertEqual(self.my_file.pool, self.pool) + self.assertFalse(self.my_file.has_options) + self.assertEqual('proto2', self.my_file.syntax) + file_proto = descriptor_pb2.FileDescriptorProto() + self.my_file.CopyToProto(file_proto) + self.assertEqual(self.my_file.serialized_pb, + file_proto.SerializeToString()) + # Generated modules also belong to the default pool. + self.assertEqual(unittest_pb2.DESCRIPTOR.pool, descriptor_pool.Default()) + + @unittest.skipIf( + api_implementation.Type() == 'python', + 'Immutability of descriptors is only enforced in v2 implementation') + def testImmutableCppDescriptor(self): + file_descriptor = unittest_pb2.DESCRIPTOR + message_descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR + field_descriptor = message_descriptor.fields_by_name['optional_int32'] + enum_descriptor = message_descriptor.enum_types_by_name['NestedEnum'] + oneof_descriptor = message_descriptor.oneofs_by_name['oneof_field'] + with self.assertRaises(AttributeError): + message_descriptor.fields_by_name = None + with self.assertRaises(TypeError): + message_descriptor.fields_by_name['Another'] = None + with self.assertRaises(TypeError): + message_descriptor.fields.append(None) + with self.assertRaises(AttributeError): + field_descriptor.containing_type = message_descriptor + with self.assertRaises(AttributeError): + file_descriptor.has_options = False + with self.assertRaises(AttributeError): + field_descriptor.has_options = False + with self.assertRaises(AttributeError): + oneof_descriptor.has_options = False + with self.assertRaises(AttributeError): + enum_descriptor.has_options = False + with self.assertRaises(AttributeError) as e: + message_descriptor.has_options = True + self.assertEqual('attribute is not writable: has_options', + str(e.exception)) + + def testDefault(self): + message_descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR + field = message_descriptor.fields_by_name['repeated_int32'] + self.assertEqual(field.default_value, []) + field = message_descriptor.fields_by_name['repeated_nested_message'] + self.assertEqual(field.default_value, []) + field = message_descriptor.fields_by_name['optionalgroup'] + self.assertEqual(field.default_value, None) + field = message_descriptor.fields_by_name['optional_nested_message'] + self.assertEqual(field.default_value, None) + + +class NewDescriptorTest(DescriptorTest): + """Redo the same tests as above, but with a separate DescriptorPool.""" + + def GetDescriptorPool(self): + return descriptor_pool.DescriptorPool() + + +class GeneratedDescriptorTest(unittest.TestCase): + """Tests for the properties of descriptors in generated code.""" + + def CheckMessageDescriptor(self, message_descriptor): + # Basic properties + self.assertEqual(message_descriptor.name, 'TestAllTypes') + self.assertEqual(message_descriptor.full_name, + 'protobuf_unittest.TestAllTypes') + # Test equality and hashability + self.assertEqual(message_descriptor, message_descriptor) + self.assertEqual(message_descriptor.fields[0].containing_type, + message_descriptor) + self.assertIn(message_descriptor, [message_descriptor]) + self.assertIn(message_descriptor, {message_descriptor: None}) + # Test field containers + self.CheckDescriptorSequence(message_descriptor.fields) + self.CheckDescriptorMapping(message_descriptor.fields_by_name) + self.CheckDescriptorMapping(message_descriptor.fields_by_number) + self.CheckDescriptorMapping(message_descriptor.fields_by_camelcase_name) + self.CheckDescriptorMapping(message_descriptor.enum_types_by_name) + self.CheckDescriptorMapping(message_descriptor.enum_values_by_name) + self.CheckDescriptorMapping(message_descriptor.oneofs_by_name) + self.CheckDescriptorMapping(message_descriptor.enum_types[0].values_by_name) + # Test extension range + self.assertEqual(message_descriptor.extension_ranges, []) + + def CheckFieldDescriptor(self, field_descriptor): + # Basic properties + self.assertEqual(field_descriptor.name, 'optional_int32') + self.assertEqual(field_descriptor.camelcase_name, 'optionalInt32') + self.assertEqual(field_descriptor.full_name, + 'protobuf_unittest.TestAllTypes.optional_int32') + self.assertEqual(field_descriptor.containing_type.name, 'TestAllTypes') + self.assertEqual(field_descriptor.file, unittest_pb2.DESCRIPTOR) + # Test equality and hashability + self.assertEqual(field_descriptor, field_descriptor) + self.assertEqual( + field_descriptor.containing_type.fields_by_name['optional_int32'], + field_descriptor) + self.assertEqual( + field_descriptor.containing_type.fields_by_camelcase_name[ + 'optionalInt32'], + field_descriptor) + self.assertIn(field_descriptor, [field_descriptor]) + self.assertIn(field_descriptor, {field_descriptor: None}) + self.assertEqual(None, field_descriptor.extension_scope) + self.assertEqual(None, field_descriptor.enum_type) + self.assertTrue(field_descriptor.has_presence) + if api_implementation.Type() == 'cpp': + # For test coverage only + self.assertEqual(field_descriptor.id, field_descriptor.id) + + def CheckDescriptorSequence(self, sequence): + # Verifies that a property like 'messageDescriptor.fields' has all the + # properties of an immutable abc.Sequence. + self.assertNotEqual(sequence, + unittest_pb2.TestAllExtensions.DESCRIPTOR.fields) + self.assertNotEqual(sequence, []) + self.assertNotEqual(sequence, 1) + self.assertFalse(sequence == 1) # Only for cpp test coverage + self.assertEqual(sequence, sequence) + expected_list = list(sequence) + self.assertEqual(expected_list, sequence) + self.assertGreater(len(sequence), 0) # Sized + self.assertEqual(len(sequence), len(expected_list)) # Iterable + self.assertEqual(sequence[len(sequence) -1], sequence[-1]) + item = sequence[0] + self.assertEqual(item, sequence[0]) + self.assertIn(item, sequence) # Container + self.assertEqual(sequence.index(item), 0) + self.assertEqual(sequence.count(item), 1) + other_item = unittest_pb2.NestedTestAllTypes.DESCRIPTOR.fields[0] + self.assertNotIn(other_item, sequence) + self.assertEqual(sequence.count(other_item), 0) + self.assertRaises(ValueError, sequence.index, other_item) + self.assertRaises(ValueError, sequence.index, []) + reversed_iterator = reversed(sequence) + self.assertEqual(list(reversed_iterator), list(sequence)[::-1]) + self.assertRaises(StopIteration, next, reversed_iterator) + expected_list[0] = 'change value' + self.assertNotEqual(expected_list, sequence) + # TODO(jieluo): Change __repr__ support for DescriptorSequence. + if api_implementation.Type() == 'python': + self.assertEqual(str(list(sequence)), str(sequence)) + else: + self.assertEqual(str(sequence)[0], '<') + + def CheckDescriptorMapping(self, mapping): + # Verifies that a property like 'messageDescriptor.fields' has all the + # properties of an immutable abc.Mapping. + self.assertNotEqual( + mapping, unittest_pb2.TestAllExtensions.DESCRIPTOR.fields_by_name) + self.assertNotEqual(mapping, {}) + self.assertNotEqual(mapping, 1) + self.assertFalse(mapping == 1) # Only for cpp test coverage + excepted_dict = dict(mapping.items()) + self.assertEqual(mapping, excepted_dict) + self.assertEqual(mapping, mapping) + self.assertGreater(len(mapping), 0) # Sized + self.assertEqual(len(mapping), len(excepted_dict)) # Iterable + key, item = next(iter(mapping.items())) + self.assertIn(key, mapping) # Container + self.assertEqual(mapping.get(key), item) + with self.assertRaises(TypeError): + mapping.get() + # TODO(jieluo): Fix python and cpp extension diff. + if api_implementation.Type() == 'python': + self.assertRaises(TypeError, mapping.get, []) + else: + self.assertEqual(None, mapping.get([])) + # keys(), iterkeys() &co + item = (next(iter(mapping.keys())), next(iter(mapping.values()))) + self.assertEqual(item, next(iter(mapping.items()))) + excepted_dict[key] = 'change value' + self.assertNotEqual(mapping, excepted_dict) + del excepted_dict[key] + excepted_dict['new_key'] = 'new' + self.assertNotEqual(mapping, excepted_dict) + self.assertRaises(KeyError, mapping.__getitem__, 'key_error') + self.assertRaises(KeyError, mapping.__getitem__, len(mapping) + 1) + # TODO(jieluo): Add __repr__ support for DescriptorMapping. + if api_implementation.Type() == 'python': + self.assertEqual(len(str(dict(mapping.items()))), len(str(mapping))) + else: + self.assertEqual(str(mapping)[0], '<') + + def testDescriptor(self): + message_descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR + self.CheckMessageDescriptor(message_descriptor) + field_descriptor = message_descriptor.fields_by_name['optional_int32'] + self.CheckFieldDescriptor(field_descriptor) + field_descriptor = message_descriptor.fields_by_camelcase_name[ + 'optionalInt32'] + self.CheckFieldDescriptor(field_descriptor) + enum_descriptor = unittest_pb2.DESCRIPTOR.enum_types_by_name[ + 'ForeignEnum'] + self.assertEqual(None, enum_descriptor.containing_type) + # Test extension range + self.assertEqual( + unittest_pb2.TestAllExtensions.DESCRIPTOR.extension_ranges, + [(1, 536870912)]) + self.assertEqual( + unittest_pb2.TestMultipleExtensionRanges.DESCRIPTOR.extension_ranges, + [(42, 43), (4143, 4244), (65536, 536870912)]) + + def testCppDescriptorContainer(self): + containing_file = unittest_pb2.DESCRIPTOR + self.CheckDescriptorSequence(containing_file.dependencies) + self.CheckDescriptorMapping(containing_file.message_types_by_name) + self.CheckDescriptorMapping(containing_file.enum_types_by_name) + self.CheckDescriptorMapping(containing_file.services_by_name) + self.CheckDescriptorMapping(containing_file.extensions_by_name) + self.CheckDescriptorMapping( + unittest_pb2.TestNestedExtension.DESCRIPTOR.extensions_by_name) + + def testCppDescriptorContainer_Iterator(self): + # Same test with the iterator + enum = unittest_pb2.TestAllTypes.DESCRIPTOR.enum_types_by_name['NestedEnum'] + values_iter = iter(enum.values) + del enum + self.assertEqual('FOO', next(values_iter).name) + + def testDescriptorNestedTypesContainer(self): + message_descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR + nested_message_descriptor = unittest_pb2.TestAllTypes.NestedMessage.DESCRIPTOR + self.assertEqual(len(message_descriptor.nested_types), 3) + self.assertFalse(None in message_descriptor.nested_types) + self.assertTrue( + nested_message_descriptor in message_descriptor.nested_types) + + def testServiceDescriptor(self): + service_descriptor = unittest_pb2.DESCRIPTOR.services_by_name['TestService'] + self.assertEqual(service_descriptor.name, 'TestService') + self.assertEqual(service_descriptor.methods[0].name, 'Foo') + self.assertIs(service_descriptor.file, unittest_pb2.DESCRIPTOR) + self.assertEqual(service_descriptor.index, 0) + self.CheckDescriptorMapping(service_descriptor.methods_by_name) + + def testOneofDescriptor(self): + message_descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR + oneof_descriptor = message_descriptor.oneofs_by_name['oneof_field'] + self.assertFalse(oneof_descriptor.has_options) + self.assertEqual(message_descriptor, oneof_descriptor.containing_type) + self.assertEqual('oneof_field', oneof_descriptor.name) + self.assertEqual('protobuf_unittest.TestAllTypes.oneof_field', + oneof_descriptor.full_name) + self.assertEqual(0, oneof_descriptor.index) + + +class DescriptorCopyToProtoTest(unittest.TestCase): + """Tests for CopyTo functions of Descriptor.""" + + def _AssertProtoEqual(self, actual_proto, expected_class, expected_ascii): + expected_proto = expected_class() + text_format.Merge(expected_ascii, expected_proto) + + self.assertEqual( + actual_proto, expected_proto, + 'Not equal,\nActual:\n%s\nExpected:\n%s\n' + % (str(actual_proto), str(expected_proto))) + + def _InternalTestCopyToProto(self, desc, expected_proto_class, + expected_proto_ascii): + actual = expected_proto_class() + desc.CopyToProto(actual) + self._AssertProtoEqual( + actual, expected_proto_class, expected_proto_ascii) + + def testCopyToProto_EmptyMessage(self): + self._InternalTestCopyToProto( + unittest_pb2.TestEmptyMessage.DESCRIPTOR, + descriptor_pb2.DescriptorProto, + TEST_EMPTY_MESSAGE_DESCRIPTOR_ASCII) + + def testCopyToProto_NestedMessage(self): + TEST_NESTED_MESSAGE_ASCII = """ + name: 'NestedMessage' + field: < + name: 'bb' + number: 1 + label: 1 # Optional + type: 5 # TYPE_INT32 + > + """ + + self._InternalTestCopyToProto( + unittest_pb2.TestAllTypes.NestedMessage.DESCRIPTOR, + descriptor_pb2.DescriptorProto, + TEST_NESTED_MESSAGE_ASCII) + + def testCopyToProto_ForeignNestedMessage(self): + TEST_FOREIGN_NESTED_ASCII = """ + name: 'TestForeignNested' + field: < + name: 'foreign_nested' + number: 1 + label: 1 # Optional + type: 11 # TYPE_MESSAGE + type_name: '.protobuf_unittest.TestAllTypes.NestedMessage' + > + """ + + self._InternalTestCopyToProto( + unittest_pb2.TestForeignNested.DESCRIPTOR, + descriptor_pb2.DescriptorProto, + TEST_FOREIGN_NESTED_ASCII) + + def testCopyToProto_ForeignEnum(self): + TEST_FOREIGN_ENUM_ASCII = """ + name: 'ForeignEnum' + value: < + name: 'FOREIGN_FOO' + number: 4 + > + value: < + name: 'FOREIGN_BAR' + number: 5 + > + value: < + name: 'FOREIGN_BAZ' + number: 6 + > + """ + + self._InternalTestCopyToProto( + unittest_pb2.ForeignEnum.DESCRIPTOR, + descriptor_pb2.EnumDescriptorProto, + TEST_FOREIGN_ENUM_ASCII) + + def testCopyToProto_Options(self): + TEST_DEPRECATED_FIELDS_ASCII = """ + name: 'TestDeprecatedFields' + field: < + name: 'deprecated_int32' + number: 1 + label: 1 # Optional + type: 5 # TYPE_INT32 + options: < + deprecated: true + > + > + field { + name: "deprecated_int32_in_oneof" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_INT32 + options { + deprecated: true + } + oneof_index: 0 + } + oneof_decl { + name: "oneof_fields" + } + """ + + self._InternalTestCopyToProto( + unittest_pb2.TestDeprecatedFields.DESCRIPTOR, + descriptor_pb2.DescriptorProto, + TEST_DEPRECATED_FIELDS_ASCII) + + def testCopyToProto_AllExtensions(self): + TEST_EMPTY_MESSAGE_WITH_EXTENSIONS_ASCII = """ + name: 'TestEmptyMessageWithExtensions' + extension_range: < + start: 1 + end: 536870912 + > + """ + + self._InternalTestCopyToProto( + unittest_pb2.TestEmptyMessageWithExtensions.DESCRIPTOR, + descriptor_pb2.DescriptorProto, + TEST_EMPTY_MESSAGE_WITH_EXTENSIONS_ASCII) + + def testCopyToProto_SeveralExtensions(self): + TEST_MESSAGE_WITH_SEVERAL_EXTENSIONS_ASCII = """ + name: 'TestMultipleExtensionRanges' + extension_range: < + start: 42 + end: 43 + > + extension_range: < + start: 4143 + end: 4244 + > + extension_range: < + start: 65536 + end: 536870912 + > + """ + + self._InternalTestCopyToProto( + unittest_pb2.TestMultipleExtensionRanges.DESCRIPTOR, + descriptor_pb2.DescriptorProto, + TEST_MESSAGE_WITH_SEVERAL_EXTENSIONS_ASCII) + + def testCopyToProto_FileDescriptor(self): + UNITTEST_IMPORT_FILE_DESCRIPTOR_ASCII = (""" + name: 'google/protobuf/unittest_import.proto' + package: 'protobuf_unittest_import' + dependency: 'google/protobuf/unittest_import_public.proto' + message_type: < + name: 'ImportMessage' + field: < + name: 'd' + number: 1 + label: 1 # Optional + type: 5 # TYPE_INT32 + > + > + """ + + """enum_type: < + name: 'ImportEnum' + value: < + name: 'IMPORT_FOO' + number: 7 + > + value: < + name: 'IMPORT_BAR' + number: 8 + > + value: < + name: 'IMPORT_BAZ' + number: 9 + > + > + enum_type: < + name: 'ImportEnumForMap' + value: < + name: 'UNKNOWN' + number: 0 + > + value: < + name: 'FOO' + number: 1 + > + value: < + name: 'BAR' + number: 2 + > + > + options: < + java_package: 'com.google.protobuf.test' + optimize_for: 1 # SPEED + """ + + """ + cc_enable_arenas: true + > + public_dependency: 0 + """) + self._InternalTestCopyToProto( + unittest_import_pb2.DESCRIPTOR, + descriptor_pb2.FileDescriptorProto, + UNITTEST_IMPORT_FILE_DESCRIPTOR_ASCII) + + def testCopyToProto_ServiceDescriptor(self): + TEST_SERVICE_ASCII = """ + name: 'TestService' + method: < + name: 'Foo' + input_type: '.protobuf_unittest.FooRequest' + output_type: '.protobuf_unittest.FooResponse' + > + method: < + name: 'Bar' + input_type: '.protobuf_unittest.BarRequest' + output_type: '.protobuf_unittest.BarResponse' + > + """ + self._InternalTestCopyToProto( + unittest_pb2.TestService.DESCRIPTOR, + descriptor_pb2.ServiceDescriptorProto, + TEST_SERVICE_ASCII) + + def testCopyToProto_MethodDescriptor(self): + expected_ascii = """ + name: 'Foo' + input_type: '.protobuf_unittest.FooRequest' + output_type: '.protobuf_unittest.FooResponse' + """ + method_descriptor = unittest_pb2.TestService.DESCRIPTOR.FindMethodByName( + 'Foo') + self._InternalTestCopyToProto( + method_descriptor, + descriptor_pb2.MethodDescriptorProto, + expected_ascii) + + @unittest.skipIf( + api_implementation.Type() == 'python', + 'Pure python does not raise error.') + # TODO(jieluo): Fix pure python to check with the proto type. + def testCopyToProto_TypeError(self): + file_proto = descriptor_pb2.FileDescriptorProto() + self.assertRaises(TypeError, + unittest_pb2.TestEmptyMessage.DESCRIPTOR.CopyToProto, + file_proto) + self.assertRaises(TypeError, + unittest_pb2.ForeignEnum.DESCRIPTOR.CopyToProto, + file_proto) + self.assertRaises(TypeError, + unittest_pb2.TestService.DESCRIPTOR.CopyToProto, + file_proto) + proto = descriptor_pb2.DescriptorProto() + self.assertRaises(TypeError, + unittest_import_pb2.DESCRIPTOR.CopyToProto, + proto) + + +class MakeDescriptorTest(unittest.TestCase): + + def testMakeDescriptorWithNestedFields(self): + file_descriptor_proto = descriptor_pb2.FileDescriptorProto() + file_descriptor_proto.name = 'Foo2' + message_type = file_descriptor_proto.message_type.add() + message_type.name = file_descriptor_proto.name + nested_type = message_type.nested_type.add() + nested_type.name = 'Sub' + enum_type = nested_type.enum_type.add() + enum_type.name = 'FOO' + enum_type_val = enum_type.value.add() + enum_type_val.name = 'BAR' + enum_type_val.number = 3 + field = message_type.field.add() + field.number = 1 + field.name = 'uint64_field' + field.label = descriptor.FieldDescriptor.LABEL_REQUIRED + field.type = descriptor.FieldDescriptor.TYPE_UINT64 + field = message_type.field.add() + field.number = 2 + field.name = 'nested_message_field' + field.label = descriptor.FieldDescriptor.LABEL_REQUIRED + field.type = descriptor.FieldDescriptor.TYPE_MESSAGE + field.type_name = 'Sub' + enum_field = nested_type.field.add() + enum_field.number = 2 + enum_field.name = 'bar_field' + enum_field.label = descriptor.FieldDescriptor.LABEL_REQUIRED + enum_field.type = descriptor.FieldDescriptor.TYPE_ENUM + enum_field.type_name = 'Foo2.Sub.FOO' + + result = descriptor.MakeDescriptor(message_type) + self.assertEqual(result.fields[0].cpp_type, + descriptor.FieldDescriptor.CPPTYPE_UINT64) + self.assertEqual(result.fields[1].cpp_type, + descriptor.FieldDescriptor.CPPTYPE_MESSAGE) + self.assertEqual(result.fields[1].message_type.containing_type, + result) + self.assertEqual(result.nested_types[0].fields[0].full_name, + 'Foo2.Sub.bar_field') + self.assertEqual(result.nested_types[0].fields[0].enum_type, + result.nested_types[0].enum_types[0]) + self.assertFalse(result.has_options) + self.assertFalse(result.fields[0].has_options) + if api_implementation.Type() == 'cpp': + with self.assertRaises(AttributeError): + result.fields[0].has_options = False + + def testMakeDescriptorWithUnsignedIntField(self): + file_descriptor_proto = descriptor_pb2.FileDescriptorProto() + file_descriptor_proto.name = 'Foo' + message_type = file_descriptor_proto.message_type.add() + message_type.name = file_descriptor_proto.name + enum_type = message_type.enum_type.add() + enum_type.name = 'FOO' + enum_type_val = enum_type.value.add() + enum_type_val.name = 'BAR' + enum_type_val.number = 3 + field = message_type.field.add() + field.number = 1 + field.name = 'uint64_field' + field.label = descriptor.FieldDescriptor.LABEL_REQUIRED + field.type = descriptor.FieldDescriptor.TYPE_UINT64 + enum_field = message_type.field.add() + enum_field.number = 2 + enum_field.name = 'bar_field' + enum_field.label = descriptor.FieldDescriptor.LABEL_REQUIRED + enum_field.type = descriptor.FieldDescriptor.TYPE_ENUM + enum_field.type_name = 'Foo.FOO' + + result = descriptor.MakeDescriptor(message_type) + self.assertEqual(result.fields[0].cpp_type, + descriptor.FieldDescriptor.CPPTYPE_UINT64) + + + def testMakeDescriptorWithOptions(self): + descriptor_proto = descriptor_pb2.DescriptorProto() + aggregate_message = unittest_custom_options_pb2.AggregateMessage + aggregate_message.DESCRIPTOR.CopyToProto(descriptor_proto) + reformed_descriptor = descriptor.MakeDescriptor(descriptor_proto) + + options = reformed_descriptor.GetOptions() + self.assertEqual(101, + options.Extensions[unittest_custom_options_pb2.msgopt].i) + + def testCamelcaseName(self): + descriptor_proto = descriptor_pb2.DescriptorProto() + descriptor_proto.name = 'Bar' + names = ['foo_foo', 'FooBar', 'fooBaz', 'fooFoo', 'foobar'] + camelcase_names = ['fooFoo', 'fooBar', 'fooBaz', 'fooFoo', 'foobar'] + for index in range(len(names)): + field = descriptor_proto.field.add() + field.number = index + 1 + field.name = names[index] + result = descriptor.MakeDescriptor(descriptor_proto) + for index in range(len(camelcase_names)): + self.assertEqual(result.fields[index].camelcase_name, + camelcase_names[index]) + + def testJsonName(self): + descriptor_proto = descriptor_pb2.DescriptorProto() + descriptor_proto.name = 'TestJsonName' + names = ['field_name', 'fieldName', 'FieldName', + '_field_name', 'FIELD_NAME', 'json_name'] + json_names = ['fieldName', 'fieldName', 'FieldName', + 'FieldName', 'FIELDNAME', '@type'] + for index in range(len(names)): + field = descriptor_proto.field.add() + field.number = index + 1 + field.name = names[index] + field.json_name = '@type' + result = descriptor.MakeDescriptor(descriptor_proto) + for index in range(len(json_names)): + self.assertEqual(result.fields[index].json_name, + json_names[index]) + + +if __name__ == '__main__': + unittest.main() diff --git a/virt/lib/python3.9/site-packages/google/protobuf/internal/json_format_test 3.py b/virt/lib/python3.9/site-packages/google/protobuf/internal/json_format_test 3.py new file mode 100644 index 00000000..c8cd1521 --- /dev/null +++ b/virt/lib/python3.9/site-packages/google/protobuf/internal/json_format_test 3.py @@ -0,0 +1,1285 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# 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 Google 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 +# OWNER 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. + +"""Test for google.protobuf.json_format.""" + +__author__ = 'jieluo@google.com (Jie Luo)' + +import json +import math +import struct + +import unittest + +from google.protobuf import any_pb2 +from google.protobuf import duration_pb2 +from google.protobuf import field_mask_pb2 +from google.protobuf import struct_pb2 +from google.protobuf import timestamp_pb2 +from google.protobuf import wrappers_pb2 +from google.protobuf import any_test_pb2 +from google.protobuf import unittest_mset_pb2 +from google.protobuf import unittest_pb2 +from google.protobuf.internal import test_proto3_optional_pb2 +from google.protobuf import descriptor_pool +from google.protobuf import json_format +from google.protobuf.util import json_format_pb2 +from google.protobuf.util import json_format_proto3_pb2 + + +class JsonFormatBase(unittest.TestCase): + + def FillAllFields(self, message): + message.int32_value = 20 + message.int64_value = -20 + message.uint32_value = 3120987654 + message.uint64_value = 12345678900 + message.float_value = float('-inf') + message.double_value = 3.1415 + message.bool_value = True + message.string_value = 'foo' + message.bytes_value = b'bar' + message.message_value.value = 10 + message.enum_value = json_format_proto3_pb2.BAR + # Repeated + message.repeated_int32_value.append(0x7FFFFFFF) + message.repeated_int32_value.append(-2147483648) + message.repeated_int64_value.append(9007199254740992) + message.repeated_int64_value.append(-9007199254740992) + message.repeated_uint32_value.append(0xFFFFFFF) + message.repeated_uint32_value.append(0x7FFFFFF) + message.repeated_uint64_value.append(9007199254740992) + message.repeated_uint64_value.append(9007199254740991) + message.repeated_float_value.append(0) + + message.repeated_double_value.append(1E-15) + message.repeated_double_value.append(float('inf')) + message.repeated_bool_value.append(True) + message.repeated_bool_value.append(False) + message.repeated_string_value.append('Few symbols!#$,;') + message.repeated_string_value.append('bar') + message.repeated_bytes_value.append(b'foo') + message.repeated_bytes_value.append(b'bar') + message.repeated_message_value.add().value = 10 + message.repeated_message_value.add().value = 11 + message.repeated_enum_value.append(json_format_proto3_pb2.FOO) + message.repeated_enum_value.append(json_format_proto3_pb2.BAR) + self.message = message + + def CheckParseBack(self, message, parsed_message): + json_format.Parse(json_format.MessageToJson(message), + parsed_message) + self.assertEqual(message, parsed_message) + + def CheckError(self, text, error_message): + message = json_format_proto3_pb2.TestMessage() + self.assertRaisesRegex(json_format.ParseError, error_message, + json_format.Parse, text, message) + + +class JsonFormatTest(JsonFormatBase): + + def testEmptyMessageToJson(self): + message = json_format_proto3_pb2.TestMessage() + self.assertEqual(json_format.MessageToJson(message), + '{}') + parsed_message = json_format_proto3_pb2.TestMessage() + self.CheckParseBack(message, parsed_message) + + def testPartialMessageToJson(self): + message = json_format_proto3_pb2.TestMessage( + string_value='test', + repeated_int32_value=[89, 4]) + self.assertEqual(json.loads(json_format.MessageToJson(message)), + json.loads('{"stringValue": "test", ' + '"repeatedInt32Value": [89, 4]}')) + parsed_message = json_format_proto3_pb2.TestMessage() + self.CheckParseBack(message, parsed_message) + + def testAllFieldsToJson(self): + message = json_format_proto3_pb2.TestMessage() + text = ('{"int32Value": 20, ' + '"int64Value": "-20", ' + '"uint32Value": 3120987654,' + '"uint64Value": "12345678900",' + '"floatValue": "-Infinity",' + '"doubleValue": 3.1415,' + '"boolValue": true,' + '"stringValue": "foo",' + '"bytesValue": "YmFy",' + '"messageValue": {"value": 10},' + '"enumValue": "BAR",' + '"repeatedInt32Value": [2147483647, -2147483648],' + '"repeatedInt64Value": ["9007199254740992", "-9007199254740992"],' + '"repeatedUint32Value": [268435455, 134217727],' + '"repeatedUint64Value": ["9007199254740992", "9007199254740991"],' + '"repeatedFloatValue": [0],' + '"repeatedDoubleValue": [1e-15, "Infinity"],' + '"repeatedBoolValue": [true, false],' + '"repeatedStringValue": ["Few symbols!#$,;", "bar"],' + '"repeatedBytesValue": ["Zm9v", "YmFy"],' + '"repeatedMessageValue": [{"value": 10}, {"value": 11}],' + '"repeatedEnumValue": ["FOO", "BAR"]' + '}') + self.FillAllFields(message) + self.assertEqual( + json.loads(json_format.MessageToJson(message)), + json.loads(text)) + parsed_message = json_format_proto3_pb2.TestMessage() + json_format.Parse(text, parsed_message) + self.assertEqual(message, parsed_message) + + def testUnknownEnumToJsonAndBack(self): + text = '{\n "enumValue": 999\n}' + message = json_format_proto3_pb2.TestMessage() + message.enum_value = 999 + self.assertEqual(json_format.MessageToJson(message), + text) + parsed_message = json_format_proto3_pb2.TestMessage() + json_format.Parse(text, parsed_message) + self.assertEqual(message, parsed_message) + + def testExtensionToJsonAndBack(self): + message = unittest_mset_pb2.TestMessageSetContainer() + ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension + ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension + message.message_set.Extensions[ext1].i = 23 + message.message_set.Extensions[ext2].str = 'foo' + message_text = json_format.MessageToJson( + message + ) + parsed_message = unittest_mset_pb2.TestMessageSetContainer() + json_format.Parse(message_text, parsed_message) + self.assertEqual(message, parsed_message) + + def testExtensionErrors(self): + self.CheckError('{"[extensionField]": {}}', + 'Message type proto3.TestMessage does not have extensions') + + def testExtensionToDictAndBack(self): + message = unittest_mset_pb2.TestMessageSetContainer() + ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension + ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension + message.message_set.Extensions[ext1].i = 23 + message.message_set.Extensions[ext2].str = 'foo' + message_dict = json_format.MessageToDict( + message + ) + parsed_message = unittest_mset_pb2.TestMessageSetContainer() + json_format.ParseDict(message_dict, parsed_message) + self.assertEqual(message, parsed_message) + + def testExtensionToDictAndBackWithScalar(self): + message = unittest_pb2.TestAllExtensions() + ext1 = unittest_pb2.TestNestedExtension.test + message.Extensions[ext1] = 'data' + message_dict = json_format.MessageToDict( + message + ) + parsed_message = unittest_pb2.TestAllExtensions() + json_format.ParseDict(message_dict, parsed_message) + self.assertEqual(message, parsed_message) + + def testJsonParseDictToAnyDoesNotAlterInput(self): + orig_dict = { + 'int32Value': 20, + '@type': 'type.googleapis.com/proto3.TestMessage' + } + copied_dict = json.loads(json.dumps(orig_dict)) + parsed_message = any_pb2.Any() + json_format.ParseDict(copied_dict, parsed_message) + self.assertEqual(copied_dict, orig_dict) + + def testExtensionSerializationDictMatchesProto3Spec(self): + """See go/proto3-json-spec for spec. + """ + message = unittest_mset_pb2.TestMessageSetContainer() + ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension + ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension + message.message_set.Extensions[ext1].i = 23 + message.message_set.Extensions[ext2].str = 'foo' + message_dict = json_format.MessageToDict( + message + ) + golden_dict = { + 'messageSet': { + '[protobuf_unittest.' + 'TestMessageSetExtension1.message_set_extension]': { + 'i': 23, + }, + '[protobuf_unittest.' + 'TestMessageSetExtension2.message_set_extension]': { + 'str': u'foo', + }, + }, + } + self.assertEqual(golden_dict, message_dict) + parsed_msg = unittest_mset_pb2.TestMessageSetContainer() + json_format.ParseDict(golden_dict, parsed_msg) + self.assertEqual(message, parsed_msg) + + def testExtensionSerializationDictMatchesProto3SpecMore(self): + """See go/proto3-json-spec for spec. + """ + message = json_format_pb2.TestMessageWithExtension() + ext = json_format_pb2.TestExtension.ext + message.Extensions[ext].value = 'stuff' + message_dict = json_format.MessageToDict( + message + ) + expected_dict = { + '[protobuf_unittest.TestExtension.ext]': { + 'value': u'stuff', + }, + } + self.assertEqual(expected_dict, message_dict) + + def testExtensionSerializationJsonMatchesProto3Spec(self): + """See go/proto3-json-spec for spec. + """ + message = unittest_mset_pb2.TestMessageSetContainer() + ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension + ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension + message.message_set.Extensions[ext1].i = 23 + message.message_set.Extensions[ext2].str = 'foo' + message_text = json_format.MessageToJson( + message + ) + ext1_text = ('protobuf_unittest.TestMessageSetExtension1.' + 'message_set_extension') + ext2_text = ('protobuf_unittest.TestMessageSetExtension2.' + 'message_set_extension') + golden_text = ('{"messageSet": {' + ' "[%s]": {' + ' "i": 23' + ' },' + ' "[%s]": {' + ' "str": "foo"' + ' }' + '}}') % (ext1_text, ext2_text) + self.assertEqual(json.loads(golden_text), json.loads(message_text)) + + def testJsonEscapeString(self): + message = json_format_proto3_pb2.TestMessage() + message.string_value = '&\n<\"\r>\b\t\f\\\001/' + message.string_value += (b'\xe2\x80\xa8\xe2\x80\xa9').decode('utf-8') + self.assertEqual( + json_format.MessageToJson(message), + '{\n "stringValue": ' + '"&\\n<\\\"\\r>\\b\\t\\f\\\\\\u0001/\\u2028\\u2029"\n}') + parsed_message = json_format_proto3_pb2.TestMessage() + self.CheckParseBack(message, parsed_message) + text = u'{"int32Value": "\u0031"}' + json_format.Parse(text, message) + self.assertEqual(message.int32_value, 1) + + def testAlwaysSeriliaze(self): + message = json_format_proto3_pb2.TestMessage( + string_value='foo') + self.assertEqual( + json.loads(json_format.MessageToJson(message, True)), + json.loads('{' + '"repeatedStringValue": [],' + '"stringValue": "foo",' + '"repeatedBoolValue": [],' + '"repeatedUint32Value": [],' + '"repeatedInt32Value": [],' + '"enumValue": "FOO",' + '"int32Value": 0,' + '"floatValue": 0,' + '"int64Value": "0",' + '"uint32Value": 0,' + '"repeatedBytesValue": [],' + '"repeatedUint64Value": [],' + '"repeatedDoubleValue": [],' + '"bytesValue": "",' + '"boolValue": false,' + '"repeatedEnumValue": [],' + '"uint64Value": "0",' + '"doubleValue": 0,' + '"repeatedFloatValue": [],' + '"repeatedInt64Value": [],' + '"repeatedMessageValue": []}')) + parsed_message = json_format_proto3_pb2.TestMessage() + self.CheckParseBack(message, parsed_message) + + def testProto3Optional(self): + message = test_proto3_optional_pb2.TestProto3Optional() + self.assertEqual( + json.loads( + json_format.MessageToJson( + message, including_default_value_fields=True)), + json.loads('{}')) + message.optional_int32 = 0 + self.assertEqual( + json.loads( + json_format.MessageToJson( + message, including_default_value_fields=True)), + json.loads('{"optionalInt32": 0}')) + + def testIntegersRepresentedAsFloat(self): + message = json_format_proto3_pb2.TestMessage() + json_format.Parse('{"int32Value": -2.147483648e9}', message) + self.assertEqual(message.int32_value, -2147483648) + json_format.Parse('{"int32Value": 1e5}', message) + self.assertEqual(message.int32_value, 100000) + json_format.Parse('{"int32Value": 1.0}', message) + self.assertEqual(message.int32_value, 1) + + def testMapFields(self): + message = json_format_proto3_pb2.TestNestedMap() + self.assertEqual( + json.loads(json_format.MessageToJson(message, True)), + json.loads('{' + '"boolMap": {},' + '"int32Map": {},' + '"int64Map": {},' + '"uint32Map": {},' + '"uint64Map": {},' + '"stringMap": {},' + '"mapMap": {}' + '}')) + message.bool_map[True] = 1 + message.bool_map[False] = 2 + message.int32_map[1] = 2 + message.int32_map[2] = 3 + message.int64_map[1] = 2 + message.int64_map[2] = 3 + message.uint32_map[1] = 2 + message.uint32_map[2] = 3 + message.uint64_map[1] = 2 + message.uint64_map[2] = 3 + message.string_map['1'] = 2 + message.string_map['null'] = 3 + message.map_map['1'].bool_map[True] = 3 + self.assertEqual( + json.loads(json_format.MessageToJson(message, False)), + json.loads('{' + '"boolMap": {"false": 2, "true": 1},' + '"int32Map": {"1": 2, "2": 3},' + '"int64Map": {"1": 2, "2": 3},' + '"uint32Map": {"1": 2, "2": 3},' + '"uint64Map": {"1": 2, "2": 3},' + '"stringMap": {"1": 2, "null": 3},' + '"mapMap": {"1": {"boolMap": {"true": 3}}}' + '}')) + parsed_message = json_format_proto3_pb2.TestNestedMap() + self.CheckParseBack(message, parsed_message) + + def testOneofFields(self): + message = json_format_proto3_pb2.TestOneof() + # Always print does not affect oneof fields. + self.assertEqual( + json_format.MessageToJson(message, True), + '{}') + message.oneof_int32_value = 0 + self.assertEqual( + json_format.MessageToJson(message, True), + '{\n' + ' "oneofInt32Value": 0\n' + '}') + parsed_message = json_format_proto3_pb2.TestOneof() + self.CheckParseBack(message, parsed_message) + + def testSurrogates(self): + # Test correct surrogate handling. + message = json_format_proto3_pb2.TestMessage() + json_format.Parse('{"stringValue": "\\uD83D\\uDE01"}', message) + self.assertEqual(message.string_value, + b'\xF0\x9F\x98\x81'.decode('utf-8', 'strict')) + + # Error case: unpaired high surrogate. + self.CheckError( + '{"stringValue": "\\uD83D"}', + r'Invalid \\uXXXX escape|Unpaired.*surrogate') + + # Unpaired low surrogate. + self.CheckError( + '{"stringValue": "\\uDE01"}', + r'Invalid \\uXXXX escape|Unpaired.*surrogate') + + def testTimestampMessage(self): + message = json_format_proto3_pb2.TestTimestamp() + message.value.seconds = 0 + message.value.nanos = 0 + message.repeated_value.add().seconds = 20 + message.repeated_value[0].nanos = 1 + message.repeated_value.add().seconds = 0 + message.repeated_value[1].nanos = 10000 + message.repeated_value.add().seconds = 100000000 + message.repeated_value[2].nanos = 0 + # Maximum time + message.repeated_value.add().seconds = 253402300799 + message.repeated_value[3].nanos = 999999999 + # Minimum time + message.repeated_value.add().seconds = -62135596800 + message.repeated_value[4].nanos = 0 + self.assertEqual( + json.loads(json_format.MessageToJson(message, True)), + json.loads('{' + '"value": "1970-01-01T00:00:00Z",' + '"repeatedValue": [' + ' "1970-01-01T00:00:20.000000001Z",' + ' "1970-01-01T00:00:00.000010Z",' + ' "1973-03-03T09:46:40Z",' + ' "9999-12-31T23:59:59.999999999Z",' + ' "0001-01-01T00:00:00Z"' + ']' + '}')) + parsed_message = json_format_proto3_pb2.TestTimestamp() + self.CheckParseBack(message, parsed_message) + text = (r'{"value": "1970-01-01T00:00:00.01+08:00",' + r'"repeatedValue":[' + r' "1970-01-01T00:00:00.01+08:30",' + r' "1970-01-01T00:00:00.01-01:23"]}') + json_format.Parse(text, parsed_message) + self.assertEqual(parsed_message.value.seconds, -8 * 3600) + self.assertEqual(parsed_message.value.nanos, 10000000) + self.assertEqual(parsed_message.repeated_value[0].seconds, -8.5 * 3600) + self.assertEqual(parsed_message.repeated_value[1].seconds, 3600 + 23 * 60) + + def testDurationMessage(self): + message = json_format_proto3_pb2.TestDuration() + message.value.seconds = 1 + message.repeated_value.add().seconds = 0 + message.repeated_value[0].nanos = 10 + message.repeated_value.add().seconds = -1 + message.repeated_value[1].nanos = -1000 + message.repeated_value.add().seconds = 10 + message.repeated_value[2].nanos = 11000000 + message.repeated_value.add().seconds = -315576000000 + message.repeated_value.add().seconds = 315576000000 + self.assertEqual( + json.loads(json_format.MessageToJson(message, True)), + json.loads('{' + '"value": "1s",' + '"repeatedValue": [' + ' "0.000000010s",' + ' "-1.000001s",' + ' "10.011s",' + ' "-315576000000s",' + ' "315576000000s"' + ']' + '}')) + parsed_message = json_format_proto3_pb2.TestDuration() + self.CheckParseBack(message, parsed_message) + + def testFieldMaskMessage(self): + message = json_format_proto3_pb2.TestFieldMask() + message.value.paths.append('foo.bar') + message.value.paths.append('bar') + self.assertEqual( + json_format.MessageToJson(message, True), + '{\n' + ' "value": "foo.bar,bar"\n' + '}') + parsed_message = json_format_proto3_pb2.TestFieldMask() + self.CheckParseBack(message, parsed_message) + + message.value.Clear() + self.assertEqual( + json_format.MessageToJson(message, True), + '{\n' + ' "value": ""\n' + '}') + self.CheckParseBack(message, parsed_message) + + def testWrapperMessage(self): + message = json_format_proto3_pb2.TestWrapper() + message.bool_value.value = False + message.int32_value.value = 0 + message.string_value.value = '' + message.bytes_value.value = b'' + message.repeated_bool_value.add().value = True + message.repeated_bool_value.add().value = False + message.repeated_int32_value.add() + self.assertEqual( + json.loads(json_format.MessageToJson(message, True)), + json.loads('{\n' + ' "int32Value": 0,' + ' "boolValue": false,' + ' "stringValue": "",' + ' "bytesValue": "",' + ' "repeatedBoolValue": [true, false],' + ' "repeatedInt32Value": [0],' + ' "repeatedUint32Value": [],' + ' "repeatedFloatValue": [],' + ' "repeatedDoubleValue": [],' + ' "repeatedBytesValue": [],' + ' "repeatedInt64Value": [],' + ' "repeatedUint64Value": [],' + ' "repeatedStringValue": []' + '}')) + parsed_message = json_format_proto3_pb2.TestWrapper() + self.CheckParseBack(message, parsed_message) + + def testStructMessage(self): + message = json_format_proto3_pb2.TestStruct() + message.value['name'] = 'Jim' + message.value['age'] = 10 + message.value['attend'] = True + message.value['email'] = None + message.value.get_or_create_struct('address')['city'] = 'SFO' + message.value['address']['house_number'] = 1024 + message.value.get_or_create_struct('empty_struct') + message.value.get_or_create_list('empty_list') + struct_list = message.value.get_or_create_list('list') + struct_list.extend([6, 'seven', True, False, None]) + struct_list.add_struct()['subkey2'] = 9 + message.repeated_value.add()['age'] = 11 + message.repeated_value.add() + self.assertEqual( + json.loads(json_format.MessageToJson(message, False)), + json.loads( + '{' + ' "value": {' + ' "address": {' + ' "city": "SFO", ' + ' "house_number": 1024' + ' }, ' + ' "empty_struct": {}, ' + ' "empty_list": [], ' + ' "age": 10, ' + ' "name": "Jim", ' + ' "attend": true, ' + ' "email": null, ' + ' "list": [6, "seven", true, false, null, {"subkey2": 9}]' + ' },' + ' "repeatedValue": [{"age": 11}, {}]' + '}')) + parsed_message = json_format_proto3_pb2.TestStruct() + self.CheckParseBack(message, parsed_message) + # check for regression; this used to raise + parsed_message.value['empty_struct'] + parsed_message.value['empty_list'] + + def testValueMessage(self): + message = json_format_proto3_pb2.TestValue() + message.value.string_value = 'hello' + message.repeated_value.add().number_value = 11.1 + message.repeated_value.add().bool_value = False + message.repeated_value.add().null_value = 0 + self.assertEqual( + json.loads(json_format.MessageToJson(message, False)), + json.loads( + '{' + ' "value": "hello",' + ' "repeatedValue": [11.1, false, null]' + '}')) + parsed_message = json_format_proto3_pb2.TestValue() + self.CheckParseBack(message, parsed_message) + # Can't parse back if the Value message is not set. + message.repeated_value.add() + self.assertEqual( + json.loads(json_format.MessageToJson(message, False)), + json.loads( + '{' + ' "value": "hello",' + ' "repeatedValue": [11.1, false, null, null]' + '}')) + message.Clear() + json_format.Parse('{"value": null}', message) + self.assertEqual(message.value.WhichOneof('kind'), 'null_value') + + def testListValueMessage(self): + message = json_format_proto3_pb2.TestListValue() + message.value.values.add().number_value = 11.1 + message.value.values.add().null_value = 0 + message.value.values.add().bool_value = True + message.value.values.add().string_value = 'hello' + message.value.values.add().struct_value['name'] = 'Jim' + message.repeated_value.add().values.add().number_value = 1 + message.repeated_value.add() + self.assertEqual( + json.loads(json_format.MessageToJson(message, False)), + json.loads( + '{"value": [11.1, null, true, "hello", {"name": "Jim"}]\n,' + '"repeatedValue": [[1], []]}')) + parsed_message = json_format_proto3_pb2.TestListValue() + self.CheckParseBack(message, parsed_message) + + def testNullValue(self): + message = json_format_proto3_pb2.TestOneof() + message.oneof_null_value = 0 + self.assertEqual(json_format.MessageToJson(message), + '{\n "oneofNullValue": null\n}') + parsed_message = json_format_proto3_pb2.TestOneof() + self.CheckParseBack(message, parsed_message) + # Check old format is also accepted + new_message = json_format_proto3_pb2.TestOneof() + json_format.Parse('{\n "oneofNullValue": "NULL_VALUE"\n}', + new_message) + self.assertEqual(json_format.MessageToJson(new_message), + '{\n "oneofNullValue": null\n}') + + def testAnyMessage(self): + message = json_format_proto3_pb2.TestAny() + value1 = json_format_proto3_pb2.MessageType() + value2 = json_format_proto3_pb2.MessageType() + value1.value = 1234 + value2.value = 5678 + message.value.Pack(value1) + message.repeated_value.add().Pack(value1) + message.repeated_value.add().Pack(value2) + message.repeated_value.add() + self.assertEqual( + json.loads(json_format.MessageToJson(message, True)), + json.loads( + '{\n' + ' "repeatedValue": [ {\n' + ' "@type": "type.googleapis.com/proto3.MessageType",\n' + ' "value": 1234\n' + ' }, {\n' + ' "@type": "type.googleapis.com/proto3.MessageType",\n' + ' "value": 5678\n' + ' },\n' + ' {}],\n' + ' "value": {\n' + ' "@type": "type.googleapis.com/proto3.MessageType",\n' + ' "value": 1234\n' + ' }\n' + '}\n')) + parsed_message = json_format_proto3_pb2.TestAny() + self.CheckParseBack(message, parsed_message) + # Must print @type first + test_message = json_format_proto3_pb2.TestMessage( + bool_value=True, + int32_value=20, + int64_value=-20, + uint32_value=20, + uint64_value=20, + double_value=3.14, + string_value='foo') + message.Clear() + message.value.Pack(test_message) + self.assertEqual( + json_format.MessageToJson(message, False)[0:68], + '{\n' + ' "value": {\n' + ' "@type": "type.googleapis.com/proto3.TestMessage"') + + def testAnyMessageDescriptorPoolMissingType(self): + packed_message = unittest_pb2.OneString() + packed_message.data = 'string' + message = any_test_pb2.TestAny() + message.any_value.Pack(packed_message) + empty_pool = descriptor_pool.DescriptorPool() + with self.assertRaises(TypeError) as cm: + json_format.MessageToJson(message, True, descriptor_pool=empty_pool) + self.assertEqual( + 'Can not find message descriptor by type_url:' + ' type.googleapis.com/protobuf_unittest.OneString', str(cm.exception)) + + def testWellKnownInAnyMessage(self): + message = any_pb2.Any() + int32_value = wrappers_pb2.Int32Value() + int32_value.value = 1234 + message.Pack(int32_value) + self.assertEqual( + json.loads(json_format.MessageToJson(message, True)), + json.loads( + '{\n' + ' "@type": \"type.googleapis.com/google.protobuf.Int32Value\",\n' + ' "value": 1234\n' + '}\n')) + parsed_message = any_pb2.Any() + self.CheckParseBack(message, parsed_message) + + timestamp = timestamp_pb2.Timestamp() + message.Pack(timestamp) + self.assertEqual( + json.loads(json_format.MessageToJson(message, True)), + json.loads( + '{\n' + ' "@type": "type.googleapis.com/google.protobuf.Timestamp",\n' + ' "value": "1970-01-01T00:00:00Z"\n' + '}\n')) + self.CheckParseBack(message, parsed_message) + + duration = duration_pb2.Duration() + duration.seconds = 1 + message.Pack(duration) + self.assertEqual( + json.loads(json_format.MessageToJson(message, True)), + json.loads( + '{\n' + ' "@type": "type.googleapis.com/google.protobuf.Duration",\n' + ' "value": "1s"\n' + '}\n')) + self.CheckParseBack(message, parsed_message) + + field_mask = field_mask_pb2.FieldMask() + field_mask.paths.append('foo.bar') + field_mask.paths.append('bar') + message.Pack(field_mask) + self.assertEqual( + json.loads(json_format.MessageToJson(message, True)), + json.loads( + '{\n' + ' "@type": "type.googleapis.com/google.protobuf.FieldMask",\n' + ' "value": "foo.bar,bar"\n' + '}\n')) + self.CheckParseBack(message, parsed_message) + + struct_message = struct_pb2.Struct() + struct_message['name'] = 'Jim' + message.Pack(struct_message) + self.assertEqual( + json.loads(json_format.MessageToJson(message, True)), + json.loads( + '{\n' + ' "@type": "type.googleapis.com/google.protobuf.Struct",\n' + ' "value": {"name": "Jim"}\n' + '}\n')) + self.CheckParseBack(message, parsed_message) + + nested_any = any_pb2.Any() + int32_value.value = 5678 + nested_any.Pack(int32_value) + message.Pack(nested_any) + self.assertEqual( + json.loads(json_format.MessageToJson(message, True)), + json.loads( + '{\n' + ' "@type": "type.googleapis.com/google.protobuf.Any",\n' + ' "value": {\n' + ' "@type": "type.googleapis.com/google.protobuf.Int32Value",\n' + ' "value": 5678\n' + ' }\n' + '}\n')) + self.CheckParseBack(message, parsed_message) + + def testParseNull(self): + message = json_format_proto3_pb2.TestMessage() + parsed_message = json_format_proto3_pb2.TestMessage() + self.FillAllFields(parsed_message) + json_format.Parse('{"int32Value": null, ' + '"int64Value": null, ' + '"uint32Value": null,' + '"uint64Value": null,' + '"floatValue": null,' + '"doubleValue": null,' + '"boolValue": null,' + '"stringValue": null,' + '"bytesValue": null,' + '"messageValue": null,' + '"enumValue": null,' + '"repeatedInt32Value": null,' + '"repeatedInt64Value": null,' + '"repeatedUint32Value": null,' + '"repeatedUint64Value": null,' + '"repeatedFloatValue": null,' + '"repeatedDoubleValue": null,' + '"repeatedBoolValue": null,' + '"repeatedStringValue": null,' + '"repeatedBytesValue": null,' + '"repeatedMessageValue": null,' + '"repeatedEnumValue": null' + '}', + parsed_message) + self.assertEqual(message, parsed_message) + # Null and {} should have different behavior for sub message. + self.assertFalse(parsed_message.HasField('message_value')) + json_format.Parse('{"messageValue": {}}', parsed_message) + self.assertTrue(parsed_message.HasField('message_value')) + # Null is not allowed to be used as an element in repeated field. + self.assertRaisesRegex( + json_format.ParseError, r'Failed to parse repeatedInt32Value field: ' + r'null is not allowed to be used as an element in a repeated field ' + r'at TestMessage.repeatedInt32Value\[1\].', json_format.Parse, + '{"repeatedInt32Value":[1, null]}', parsed_message) + self.CheckError( + '{"repeatedMessageValue":[null]}', + r'Failed to parse repeatedMessageValue field: null is not' + r' allowed to be used as an element in a repeated field ' + r'at TestMessage.repeatedMessageValue\[0\].') + + def testNanFloat(self): + message = json_format_proto3_pb2.TestMessage() + message.float_value = float('nan') + text = '{\n "floatValue": "NaN"\n}' + self.assertEqual(json_format.MessageToJson(message), text) + parsed_message = json_format_proto3_pb2.TestMessage() + json_format.Parse(text, parsed_message) + self.assertTrue(math.isnan(parsed_message.float_value)) + + def testParseDoubleToFloat(self): + message = json_format_proto3_pb2.TestMessage() + text = ('{"repeatedDoubleValue": [3.4028235e+39, 1.4028235e-39]\n}') + json_format.Parse(text, message) + self.assertEqual(message.repeated_double_value[0], 3.4028235e+39) + self.assertEqual(message.repeated_double_value[1], 1.4028235e-39) + text = ('{"repeatedFloatValue": [3.4028235e+39, 1.4028235e-39]\n}') + self.CheckError( + text, r'Failed to parse repeatedFloatValue field: ' + r'Float value too large at TestMessage.repeatedFloatValue\[0\].') + + def testFloatPrecision(self): + message = json_format_proto3_pb2.TestMessage() + message.float_value = 1.123456789 + # Set to 8 valid digits. + text = '{\n "floatValue": 1.1234568\n}' + self.assertEqual( + json_format.MessageToJson(message, float_precision=8), text) + # Set to 7 valid digits. + text = '{\n "floatValue": 1.123457\n}' + self.assertEqual( + json_format.MessageToJson(message, float_precision=7), text) + + # Default float_precision will automatic print shortest float. + message.float_value = 1.1000000011 + text = '{\n "floatValue": 1.1\n}' + self.assertEqual( + json_format.MessageToJson(message), text) + message.float_value = 1.00000075e-36 + text = '{\n "floatValue": 1.00000075e-36\n}' + self.assertEqual( + json_format.MessageToJson(message), text) + message.float_value = 12345678912345e+11 + text = '{\n "floatValue": 1.234568e+24\n}' + self.assertEqual( + json_format.MessageToJson(message), text) + + # Test a bunch of data and check json encode/decode do not + # lose precision + value_list = [0x00, 0xD8, 0x6E, 0x00] + msg2 = json_format_proto3_pb2.TestMessage() + for a in range(0, 256): + value_list[3] = a + for b in range(0, 256): + value_list[0] = b + byte_array = bytearray(value_list) + message.float_value = struct.unpack('<f', byte_array)[0] + self.CheckParseBack(message, msg2) + + def testParseEmptyText(self): + self.CheckError('', + r'Failed to load JSON: (Expecting value)|(No JSON).') + + def testParseEnumValue(self): + message = json_format_proto3_pb2.TestMessage() + text = '{"enumValue": 0}' + json_format.Parse(text, message) + text = '{"enumValue": 1}' + json_format.Parse(text, message) + self.CheckError( + '{"enumValue": "baz"}', + 'Failed to parse enumValue field: Invalid enum value baz ' + 'for enum type proto3.EnumType at TestMessage.enumValue.') + # Proto3 accepts numeric unknown enums. + text = '{"enumValue": 12345}' + json_format.Parse(text, message) + # Proto2 does not accept unknown enums. + message = unittest_pb2.TestAllTypes() + self.assertRaisesRegex( + json_format.ParseError, + 'Failed to parse optionalNestedEnum field: Invalid enum value 12345 ' + 'for enum type protobuf_unittest.TestAllTypes.NestedEnum at ' + 'TestAllTypes.optionalNestedEnum.', json_format.Parse, + '{"optionalNestedEnum": 12345}', message) + + def testBytes(self): + message = json_format_proto3_pb2.TestMessage() + # Test url base64 + text = '{"bytesValue": "-_"}' + json_format.Parse(text, message) + self.assertEqual(message.bytes_value, b'\xfb') + # Test padding + text = '{"bytesValue": "AQI="}' + json_format.Parse(text, message) + self.assertEqual(message.bytes_value, b'\x01\x02') + text = '{"bytesValue": "AQI"}' + json_format.Parse(text, message) + self.assertEqual(message.bytes_value, b'\x01\x02') + text = '{"bytesValue": "AQI*"}' + json_format.Parse(text, message) + self.assertEqual(message.bytes_value, b'\x01\x02') + + def testParseBadIdentifer(self): + self.CheckError('{int32Value: 1}', + (r'Failed to load JSON: Expecting property name' + r'( enclosed in double quotes)?: line 1')) + self.CheckError( + '{"unknownName": 1}', + 'Message type "proto3.TestMessage" has no field named ' + '"unknownName" at "TestMessage".') + + def testIgnoreUnknownField(self): + text = '{"unknownName": 1}' + parsed_message = json_format_proto3_pb2.TestMessage() + json_format.Parse(text, parsed_message, ignore_unknown_fields=True) + text = ('{\n' + ' "repeatedValue": [ {\n' + ' "@type": "type.googleapis.com/proto3.MessageType",\n' + ' "unknownName": 1\n' + ' }]\n' + '}\n') + parsed_message = json_format_proto3_pb2.TestAny() + json_format.Parse(text, parsed_message, ignore_unknown_fields=True) + + def testDuplicateField(self): + self.CheckError('{"int32Value": 1,\n"int32Value":2}', + 'Failed to load JSON: duplicate key int32Value.') + + def testInvalidBoolValue(self): + self.CheckError( + '{"boolValue": 1}', 'Failed to parse boolValue field: ' + 'Expected true or false without quotes at TestMessage.boolValue.') + self.CheckError( + '{"boolValue": "true"}', 'Failed to parse boolValue field: ' + 'Expected true or false without quotes at TestMessage.boolValue.') + + def testInvalidIntegerValue(self): + message = json_format_proto3_pb2.TestMessage() + text = '{"int32Value": 0x12345}' + self.assertRaises(json_format.ParseError, + json_format.Parse, text, message) + self.CheckError( + '{"int32Value": 1.5}', 'Failed to parse int32Value field: ' + 'Couldn\'t parse integer: 1.5 at TestMessage.int32Value.') + self.CheckError('{"int32Value": 012345}', + (r'Failed to load JSON: Expecting \'?,\'? delimiter: ' + r'line 1.')) + self.CheckError( + '{"int32Value": " 1 "}', 'Failed to parse int32Value field: ' + 'Couldn\'t parse integer: " 1 " at TestMessage.int32Value.') + self.CheckError( + '{"int32Value": "1 "}', 'Failed to parse int32Value field: ' + 'Couldn\'t parse integer: "1 " at TestMessage.int32Value.') + self.CheckError( + '{"int32Value": false}', + 'Failed to parse int32Value field: Bool value False ' + 'is not acceptable for integer field at TestMessage.int32Value.') + self.CheckError('{"int32Value": 12345678901234567890}', + 'Failed to parse int32Value field: Value out of range: ' + '12345678901234567890.') + self.CheckError('{"uint32Value": -1}', + 'Failed to parse uint32Value field: ' + 'Value out of range: -1.') + + def testInvalidFloatValue(self): + self.CheckError( + '{"floatValue": "nan"}', 'Failed to parse floatValue field: Couldn\'t ' + 'parse float "nan", use "NaN" instead at TestMessage.floatValue.') + self.CheckError('{"floatValue": NaN}', + 'Failed to parse floatValue field: Couldn\'t ' + 'parse NaN, use quoted "NaN" instead.') + self.CheckError('{"floatValue": Infinity}', + 'Failed to parse floatValue field: Couldn\'t parse Infinity' + ' or value too large, use quoted "Infinity" instead.') + self.CheckError('{"floatValue": -Infinity}', + 'Failed to parse floatValue field: Couldn\'t parse ' + '-Infinity or value too small, ' + 'use quoted "-Infinity" instead.') + self.CheckError('{"doubleValue": -1.89769e+308}', + 'Failed to parse doubleValue field: Couldn\'t parse ' + '-Infinity or value too small, ' + 'use quoted "-Infinity" instead.') + self.CheckError('{"floatValue": 3.4028235e+39}', + 'Failed to parse floatValue field: Float value too large.') + self.CheckError('{"floatValue": -3.502823e+38}', + 'Failed to parse floatValue field: Float value too small.') + + def testInvalidRepeated(self): + self.CheckError( + '{"repeatedInt32Value": 12345}', + (r'Failed to parse repeatedInt32Value field: repeated field' + r' repeatedInt32Value must be in \[\] which is 12345 at TestMessage.')) + + def testInvalidMap(self): + message = json_format_proto3_pb2.TestMap() + text = '{"int32Map": {"null": 2, "2": 3}}' + self.assertRaisesRegex(json_format.ParseError, + 'Failed to parse int32Map field: invalid literal', + json_format.Parse, text, message) + text = '{"int32Map": {1: 2, "2": 3}}' + self.assertRaisesRegex(json_format.ParseError, + (r'Failed to load JSON: Expecting property name' + r'( enclosed in double quotes)?: line 1'), + json_format.Parse, text, message) + text = '{"boolMap": {"null": 1}}' + self.assertRaisesRegex( + json_format.ParseError, + 'Failed to parse boolMap field: Expected "true" or "false", not null at ' + 'TestMap.boolMap.key', json_format.Parse, text, message) + text = r'{"stringMap": {"a": 3, "\u0061": 2}}' + self.assertRaisesRegex(json_format.ParseError, + 'Failed to load JSON: duplicate key a', + json_format.Parse, text, message) + text = r'{"stringMap": 0}' + self.assertRaisesRegex( + json_format.ParseError, + 'Failed to parse stringMap field: Map field string_map must be ' + 'in a dict which is 0 at TestMap.stringMap.', json_format.Parse, text, + message) + + def testInvalidTimestamp(self): + message = json_format_proto3_pb2.TestTimestamp() + text = '{"value": "10000-01-01T00:00:00.00Z"}' + self.assertRaisesRegexp( + json_format.ParseError, 'Failed to parse value field: ' + 'time data \'10000-01-01T00:00:00\' does not match' + ' format \'%Y-%m-%dT%H:%M:%S\' at TestTimestamp.value.', + json_format.Parse, text, message) + text = '{"value": "1970-01-01T00:00:00.0123456789012Z"}' + self.assertRaisesRegex( + json_format.ParseError, + 'nanos 0123456789012 more than 9 fractional digits.', json_format.Parse, + text, message) + text = '{"value": "1972-01-01T01:00:00.01+08"}' + self.assertRaisesRegex(json_format.ParseError, + (r'Invalid timezone offset value: \+08.'), + json_format.Parse, text, message) + # Time smaller than minimum time. + text = '{"value": "0000-01-01T00:00:00Z"}' + self.assertRaisesRegex( + json_format.ParseError, + 'Failed to parse value field: year (0 )?is out of range.', + json_format.Parse, text, message) + # Time bigger than maximum time. + message.value.seconds = 253402300800 + self.assertRaisesRegex(OverflowError, 'date value out of range', + json_format.MessageToJson, message) + # Lower case t does not accept. + text = '{"value": "0001-01-01t00:00:00Z"}' + with self.assertRaises(json_format.ParseError) as e: + json_format.Parse(text, message) + self.assertEqual( + 'Failed to parse value field: ' + 'time data \'0001-01-01t00:00:00\' does not match format ' + '\'%Y-%m-%dT%H:%M:%S\', lowercase \'t\' is not accepted ' + 'at TestTimestamp.value.', str(e.exception)) + + def testInvalidOneof(self): + message = json_format_proto3_pb2.TestOneof() + text = '{"oneofInt32Value": 1, "oneofStringValue": "2"}' + self.assertRaisesRegexp( + json_format.ParseError, 'Message type "proto3.TestOneof"' + ' should not have multiple "oneof_value" oneof fields at "TestOneof".', + json_format.Parse, text, message) + + def testInvalidListValue(self): + message = json_format_proto3_pb2.TestListValue() + text = '{"value": 1234}' + self.assertRaisesRegex( + json_format.ParseError, + r'Failed to parse value field: ListValue must be in \[\] which is ' + '1234 at TestListValue.value.', json_format.Parse, text, message) + + class UnknownClass(object): + + def __str__(self): + return 'v' + self.assertRaisesRegex( + json_format.ParseError, + r' at TestListValue.value\[1\].fake.', + json_format.ParseDict, + {'value': ['hello', {'fake': UnknownClass()}]}, message) + + def testInvalidStruct(self): + message = json_format_proto3_pb2.TestStruct() + text = '{"value": 1234}' + self.assertRaisesRegex( + json_format.ParseError, + 'Failed to parse value field: Struct must be in a dict which is ' + '1234 at TestStruct.value', json_format.Parse, text, message) + + def testTimestampInvalidStringValue(self): + message = json_format_proto3_pb2.TestTimestamp() + text = '{"value": {"foo": 123}}' + self.assertRaisesRegex( + json_format.ParseError, + r"Timestamp JSON value not a string: {u?'foo': 123}", json_format.Parse, + text, message) + + def testDurationInvalidStringValue(self): + message = json_format_proto3_pb2.TestDuration() + text = '{"value": {"foo": 123}}' + self.assertRaisesRegex(json_format.ParseError, + r"Duration JSON value not a string: {u?'foo': 123}", + json_format.Parse, text, message) + + def testFieldMaskInvalidStringValue(self): + message = json_format_proto3_pb2.TestFieldMask() + text = '{"value": {"foo": 123}}' + self.assertRaisesRegex( + json_format.ParseError, + r"FieldMask JSON value not a string: {u?'foo': 123}", json_format.Parse, + text, message) + + def testInvalidAny(self): + message = any_pb2.Any() + text = '{"@type": "type.googleapis.com/google.protobuf.Int32Value"}' + self.assertRaisesRegex(KeyError, 'value', json_format.Parse, text, message) + text = '{"value": 1234}' + self.assertRaisesRegex(json_format.ParseError, + '@type is missing when parsing any message at Any', + json_format.Parse, text, message) + text = '{"@type": "type.googleapis.com/MessageNotExist", "value": 1234}' + self.assertRaisesRegex( + json_format.ParseError, 'Can not find message descriptor by type_url: ' + 'type.googleapis.com/MessageNotExist at Any', json_format.Parse, text, + message) + # Only last part is to be used: b/25630112 + text = (r'{"@type": "incorrect.googleapis.com/google.protobuf.Int32Value",' + r'"value": 1234}') + json_format.Parse(text, message) + + def testPreservingProtoFieldNames(self): + message = json_format_proto3_pb2.TestMessage() + message.int32_value = 12345 + self.assertEqual('{\n "int32Value": 12345\n}', + json_format.MessageToJson(message)) + self.assertEqual('{\n "int32_value": 12345\n}', + json_format.MessageToJson(message, False, True)) + # When including_default_value_fields is True. + message = json_format_proto3_pb2.TestTimestamp() + self.assertEqual('{\n "repeatedValue": []\n}', + json_format.MessageToJson(message, True, False)) + self.assertEqual('{\n "repeated_value": []\n}', + json_format.MessageToJson(message, True, True)) + + # Parsers accept both original proto field names and lowerCamelCase names. + message = json_format_proto3_pb2.TestMessage() + json_format.Parse('{"int32Value": 54321}', message) + self.assertEqual(54321, message.int32_value) + json_format.Parse('{"int32_value": 12345}', message) + self.assertEqual(12345, message.int32_value) + + def testIndent(self): + message = json_format_proto3_pb2.TestMessage() + message.int32_value = 12345 + self.assertEqual('{\n"int32Value": 12345\n}', + json_format.MessageToJson(message, indent=0)) + + def testFormatEnumsAsInts(self): + message = json_format_proto3_pb2.TestMessage() + message.enum_value = json_format_proto3_pb2.BAR + message.repeated_enum_value.append(json_format_proto3_pb2.FOO) + message.repeated_enum_value.append(json_format_proto3_pb2.BAR) + self.assertEqual(json.loads('{\n' + ' "enumValue": 1,\n' + ' "repeatedEnumValue": [0, 1]\n' + '}\n'), + json.loads(json_format.MessageToJson( + message, use_integers_for_enums=True))) + + def testParseDict(self): + expected = 12345 + js_dict = {'int32Value': expected} + message = json_format_proto3_pb2.TestMessage() + json_format.ParseDict(js_dict, message) + self.assertEqual(expected, message.int32_value) + + def testParseDictAnyDescriptorPoolMissingType(self): + # Confirm that ParseDict does not raise ParseError with default pool + js_dict = { + 'any_value': { + '@type': 'type.googleapis.com/proto3.MessageType', + 'value': 1234 + } + } + json_format.ParseDict(js_dict, any_test_pb2.TestAny()) + # Check ParseDict raises ParseError with empty pool + js_dict = { + 'any_value': { + '@type': 'type.googleapis.com/proto3.MessageType', + 'value': 1234 + } + } + with self.assertRaises(json_format.ParseError) as cm: + empty_pool = descriptor_pool.DescriptorPool() + json_format.ParseDict(js_dict, + any_test_pb2.TestAny(), + descriptor_pool=empty_pool) + self.assertEqual( + str(cm.exception), + 'Failed to parse any_value field: Can not find message descriptor by' + ' type_url: type.googleapis.com/proto3.MessageType at ' + 'TestAny.any_value.' + ) + + def testParseDictUnknownValueType(self): + class UnknownClass(object): + + def __repr__(self): + return 'v' + message = json_format_proto3_pb2.TestValue() + self.assertRaisesRegex( + json_format.ParseError, + r"Value v has unexpected type <class '.*\.UnknownClass'>.", + json_format.ParseDict, {'value': UnknownClass()}, message) + + def testMessageToDict(self): + message = json_format_proto3_pb2.TestMessage() + message.int32_value = 12345 + expected = {'int32Value': 12345} + self.assertEqual(expected, + json_format.MessageToDict(message)) + + def testJsonName(self): + message = json_format_proto3_pb2.TestCustomJsonName() + message.value = 12345 + self.assertEqual('{\n "@value": 12345\n}', + json_format.MessageToJson(message)) + parsed_message = json_format_proto3_pb2.TestCustomJsonName() + self.CheckParseBack(message, parsed_message) + + def testSortKeys(self): + # Testing sort_keys is not perfectly working, as by random luck we could + # get the output sorted. We just use a selection of names. + message = json_format_proto3_pb2.TestMessage(bool_value=True, + int32_value=1, + int64_value=3, + uint32_value=4, + string_value='bla') + self.assertEqual( + json_format.MessageToJson(message, sort_keys=True), + # We use json.dumps() instead of a hardcoded string due to differences + # between Python 2 and Python 3. + json.dumps({'boolValue': True, 'int32Value': 1, 'int64Value': '3', + 'uint32Value': 4, 'stringValue': 'bla'}, + indent=2, sort_keys=True)) + + def testNestedRecursiveLimit(self): + message = unittest_pb2.NestedTestAllTypes() + self.assertRaisesRegex( + json_format.ParseError, + 'Message too deep. Max recursion depth is 3', + json_format.Parse, + '{"child": {"child": {"child" : {}}}}', + message, + max_recursion_depth=3) + # The following one can pass + json_format.Parse('{"payload": {}, "child": {"child":{}}}', + message, max_recursion_depth=3) + +if __name__ == '__main__': + unittest.main() diff --git a/virt/lib/python3.9/site-packages/google/protobuf/internal/python_message 3.py b/virt/lib/python3.9/site-packages/google/protobuf/internal/python_message 3.py new file mode 100644 index 00000000..67a5f344 --- /dev/null +++ b/virt/lib/python3.9/site-packages/google/protobuf/internal/python_message 3.py @@ -0,0 +1,1539 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# 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 Google 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 +# OWNER 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. + +# This code is meant to work on Python 2.4 and above only. +# +# TODO(robinson): Helpers for verbose, common checks like seeing if a +# descriptor's cpp_type is CPPTYPE_MESSAGE. + +"""Contains a metaclass and helper functions used to create +protocol message classes from Descriptor objects at runtime. + +Recall that a metaclass is the "type" of a class. +(A class is to a metaclass what an instance is to a class.) + +In this case, we use the GeneratedProtocolMessageType metaclass +to inject all the useful functionality into the classes +output by the protocol compiler at compile-time. + +The upshot of all this is that the real implementation +details for ALL pure-Python protocol buffers are *here in +this file*. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +from io import BytesIO +import struct +import sys +import weakref + +# We use "as" to avoid name collisions with variables. +from google.protobuf.internal import api_implementation +from google.protobuf.internal import containers +from google.protobuf.internal import decoder +from google.protobuf.internal import encoder +from google.protobuf.internal import enum_type_wrapper +from google.protobuf.internal import extension_dict +from google.protobuf.internal import message_listener as message_listener_mod +from google.protobuf.internal import type_checkers +from google.protobuf.internal import well_known_types +from google.protobuf.internal import wire_format +from google.protobuf import descriptor as descriptor_mod +from google.protobuf import message as message_mod +from google.protobuf import text_format + +_FieldDescriptor = descriptor_mod.FieldDescriptor +_AnyFullTypeName = 'google.protobuf.Any' +_ExtensionDict = extension_dict._ExtensionDict + +class GeneratedProtocolMessageType(type): + + """Metaclass for protocol message classes created at runtime from Descriptors. + + We add implementations for all methods described in the Message class. We + also create properties to allow getting/setting all fields in the protocol + message. Finally, we create slots to prevent users from accidentally + "setting" nonexistent fields in the protocol message, which then wouldn't get + serialized / deserialized properly. + + The protocol compiler currently uses this metaclass to create protocol + message classes at runtime. Clients can also manually create their own + classes at runtime, as in this example: + + mydescriptor = Descriptor(.....) + factory = symbol_database.Default() + factory.pool.AddDescriptor(mydescriptor) + MyProtoClass = factory.GetPrototype(mydescriptor) + myproto_instance = MyProtoClass() + myproto.foo_field = 23 + ... + """ + + # Must be consistent with the protocol-compiler code in + # proto2/compiler/internal/generator.*. + _DESCRIPTOR_KEY = 'DESCRIPTOR' + + def __new__(cls, name, bases, dictionary): + """Custom allocation for runtime-generated class types. + + We override __new__ because this is apparently the only place + where we can meaningfully set __slots__ on the class we're creating(?). + (The interplay between metaclasses and slots is not very well-documented). + + Args: + name: Name of the class (ignored, but required by the + metaclass protocol). + bases: Base classes of the class we're constructing. + (Should be message.Message). We ignore this field, but + it's required by the metaclass protocol + dictionary: The class dictionary of the class we're + constructing. dictionary[_DESCRIPTOR_KEY] must contain + a Descriptor object describing this protocol message + type. + + Returns: + Newly-allocated class. + + Raises: + RuntimeError: Generated code only work with python cpp extension. + """ + descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY] + + if isinstance(descriptor, str): + raise RuntimeError('The generated code only work with python cpp ' + 'extension, but it is using pure python runtime.') + + # If a concrete class already exists for this descriptor, don't try to + # create another. Doing so will break any messages that already exist with + # the existing class. + # + # The C++ implementation appears to have its own internal `PyMessageFactory` + # to achieve similar results. + # + # This most commonly happens in `text_format.py` when using descriptors from + # a custom pool; it calls symbol_database.Global().getPrototype() on a + # descriptor which already has an existing concrete class. + new_class = getattr(descriptor, '_concrete_class', None) + if new_class: + return new_class + + if descriptor.full_name in well_known_types.WKTBASES: + bases += (well_known_types.WKTBASES[descriptor.full_name],) + _AddClassAttributesForNestedExtensions(descriptor, dictionary) + _AddSlots(descriptor, dictionary) + + superclass = super(GeneratedProtocolMessageType, cls) + new_class = superclass.__new__(cls, name, bases, dictionary) + return new_class + + def __init__(cls, name, bases, dictionary): + """Here we perform the majority of our work on the class. + We add enum getters, an __init__ method, implementations + of all Message methods, and properties for all fields + in the protocol type. + + Args: + name: Name of the class (ignored, but required by the + metaclass protocol). + bases: Base classes of the class we're constructing. + (Should be message.Message). We ignore this field, but + it's required by the metaclass protocol + dictionary: The class dictionary of the class we're + constructing. dictionary[_DESCRIPTOR_KEY] must contain + a Descriptor object describing this protocol message + type. + """ + descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY] + + # If this is an _existing_ class looked up via `_concrete_class` in the + # __new__ method above, then we don't need to re-initialize anything. + existing_class = getattr(descriptor, '_concrete_class', None) + if existing_class: + assert existing_class is cls, ( + 'Duplicate `GeneratedProtocolMessageType` created for descriptor %r' + % (descriptor.full_name)) + return + + cls._decoders_by_tag = {} + if (descriptor.has_options and + descriptor.GetOptions().message_set_wire_format): + cls._decoders_by_tag[decoder.MESSAGE_SET_ITEM_TAG] = ( + decoder.MessageSetItemDecoder(descriptor), None) + + # Attach stuff to each FieldDescriptor for quick lookup later on. + for field in descriptor.fields: + _AttachFieldHelpers(cls, field) + + descriptor._concrete_class = cls # pylint: disable=protected-access + _AddEnumValues(descriptor, cls) + _AddInitMethod(descriptor, cls) + _AddPropertiesForFields(descriptor, cls) + _AddPropertiesForExtensions(descriptor, cls) + _AddStaticMethods(cls) + _AddMessageMethods(descriptor, cls) + _AddPrivateHelperMethods(descriptor, cls) + + superclass = super(GeneratedProtocolMessageType, cls) + superclass.__init__(name, bases, dictionary) + + +# Stateless helpers for GeneratedProtocolMessageType below. +# Outside clients should not access these directly. +# +# I opted not to make any of these methods on the metaclass, to make it more +# clear that I'm not really using any state there and to keep clients from +# thinking that they have direct access to these construction helpers. + + +def _PropertyName(proto_field_name): + """Returns the name of the public property attribute which + clients can use to get and (in some cases) set the value + of a protocol message field. + + Args: + proto_field_name: The protocol message field name, exactly + as it appears (or would appear) in a .proto file. + """ + # TODO(robinson): Escape Python keywords (e.g., yield), and test this support. + # nnorwitz makes my day by writing: + # """ + # FYI. See the keyword module in the stdlib. This could be as simple as: + # + # if keyword.iskeyword(proto_field_name): + # return proto_field_name + "_" + # return proto_field_name + # """ + # Kenton says: The above is a BAD IDEA. People rely on being able to use + # getattr() and setattr() to reflectively manipulate field values. If we + # rename the properties, then every such user has to also make sure to apply + # the same transformation. Note that currently if you name a field "yield", + # you can still access it just fine using getattr/setattr -- it's not even + # that cumbersome to do so. + # TODO(kenton): Remove this method entirely if/when everyone agrees with my + # position. + return proto_field_name + + +def _AddSlots(message_descriptor, dictionary): + """Adds a __slots__ entry to dictionary, containing the names of all valid + attributes for this message type. + + Args: + message_descriptor: A Descriptor instance describing this message type. + dictionary: Class dictionary to which we'll add a '__slots__' entry. + """ + dictionary['__slots__'] = ['_cached_byte_size', + '_cached_byte_size_dirty', + '_fields', + '_unknown_fields', + '_unknown_field_set', + '_is_present_in_parent', + '_listener', + '_listener_for_children', + '__weakref__', + '_oneofs'] + + +def _IsMessageSetExtension(field): + return (field.is_extension and + field.containing_type.has_options and + field.containing_type.GetOptions().message_set_wire_format and + field.type == _FieldDescriptor.TYPE_MESSAGE and + field.label == _FieldDescriptor.LABEL_OPTIONAL) + + +def _IsMapField(field): + return (field.type == _FieldDescriptor.TYPE_MESSAGE and + field.message_type.has_options and + field.message_type.GetOptions().map_entry) + + +def _IsMessageMapField(field): + value_type = field.message_type.fields_by_name['value'] + return value_type.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE + + +def _AttachFieldHelpers(cls, field_descriptor): + is_repeated = (field_descriptor.label == _FieldDescriptor.LABEL_REPEATED) + is_packable = (is_repeated and + wire_format.IsTypePackable(field_descriptor.type)) + is_proto3 = field_descriptor.containing_type.syntax == 'proto3' + if not is_packable: + is_packed = False + elif field_descriptor.containing_type.syntax == 'proto2': + is_packed = (field_descriptor.has_options and + field_descriptor.GetOptions().packed) + else: + has_packed_false = (field_descriptor.has_options and + field_descriptor.GetOptions().HasField('packed') and + field_descriptor.GetOptions().packed == False) + is_packed = not has_packed_false + is_map_entry = _IsMapField(field_descriptor) + + if is_map_entry: + field_encoder = encoder.MapEncoder(field_descriptor) + sizer = encoder.MapSizer(field_descriptor, + _IsMessageMapField(field_descriptor)) + elif _IsMessageSetExtension(field_descriptor): + field_encoder = encoder.MessageSetItemEncoder(field_descriptor.number) + sizer = encoder.MessageSetItemSizer(field_descriptor.number) + else: + field_encoder = type_checkers.TYPE_TO_ENCODER[field_descriptor.type]( + field_descriptor.number, is_repeated, is_packed) + sizer = type_checkers.TYPE_TO_SIZER[field_descriptor.type]( + field_descriptor.number, is_repeated, is_packed) + + field_descriptor._encoder = field_encoder + field_descriptor._sizer = sizer + field_descriptor._default_constructor = _DefaultValueConstructorForField( + field_descriptor) + + def AddDecoder(wiretype, is_packed): + tag_bytes = encoder.TagBytes(field_descriptor.number, wiretype) + decode_type = field_descriptor.type + if (decode_type == _FieldDescriptor.TYPE_ENUM and + type_checkers.SupportsOpenEnums(field_descriptor)): + decode_type = _FieldDescriptor.TYPE_INT32 + + oneof_descriptor = None + clear_if_default = False + if field_descriptor.containing_oneof is not None: + oneof_descriptor = field_descriptor + elif (is_proto3 and not is_repeated and + field_descriptor.cpp_type != _FieldDescriptor.CPPTYPE_MESSAGE): + clear_if_default = True + + if is_map_entry: + is_message_map = _IsMessageMapField(field_descriptor) + + field_decoder = decoder.MapDecoder( + field_descriptor, _GetInitializeDefaultForMap(field_descriptor), + is_message_map) + elif decode_type == _FieldDescriptor.TYPE_STRING: + field_decoder = decoder.StringDecoder( + field_descriptor.number, is_repeated, is_packed, + field_descriptor, field_descriptor._default_constructor, + clear_if_default) + elif field_descriptor.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + field_decoder = type_checkers.TYPE_TO_DECODER[decode_type]( + field_descriptor.number, is_repeated, is_packed, + field_descriptor, field_descriptor._default_constructor) + else: + field_decoder = type_checkers.TYPE_TO_DECODER[decode_type]( + field_descriptor.number, is_repeated, is_packed, + # pylint: disable=protected-access + field_descriptor, field_descriptor._default_constructor, + clear_if_default) + + cls._decoders_by_tag[tag_bytes] = (field_decoder, oneof_descriptor) + + AddDecoder(type_checkers.FIELD_TYPE_TO_WIRE_TYPE[field_descriptor.type], + False) + + if is_repeated and wire_format.IsTypePackable(field_descriptor.type): + # To support wire compatibility of adding packed = true, add a decoder for + # packed values regardless of the field's options. + AddDecoder(wire_format.WIRETYPE_LENGTH_DELIMITED, True) + + +def _AddClassAttributesForNestedExtensions(descriptor, dictionary): + extensions = descriptor.extensions_by_name + for extension_name, extension_field in extensions.items(): + assert extension_name not in dictionary + dictionary[extension_name] = extension_field + + +def _AddEnumValues(descriptor, cls): + """Sets class-level attributes for all enum fields defined in this message. + + Also exporting a class-level object that can name enum values. + + Args: + descriptor: Descriptor object for this message type. + cls: Class we're constructing for this message type. + """ + for enum_type in descriptor.enum_types: + setattr(cls, enum_type.name, enum_type_wrapper.EnumTypeWrapper(enum_type)) + for enum_value in enum_type.values: + setattr(cls, enum_value.name, enum_value.number) + + +def _GetInitializeDefaultForMap(field): + if field.label != _FieldDescriptor.LABEL_REPEATED: + raise ValueError('map_entry set on non-repeated field %s' % ( + field.name)) + fields_by_name = field.message_type.fields_by_name + key_checker = type_checkers.GetTypeChecker(fields_by_name['key']) + + value_field = fields_by_name['value'] + if _IsMessageMapField(field): + def MakeMessageMapDefault(message): + return containers.MessageMap( + message._listener_for_children, value_field.message_type, key_checker, + field.message_type) + return MakeMessageMapDefault + else: + value_checker = type_checkers.GetTypeChecker(value_field) + def MakePrimitiveMapDefault(message): + return containers.ScalarMap( + message._listener_for_children, key_checker, value_checker, + field.message_type) + return MakePrimitiveMapDefault + +def _DefaultValueConstructorForField(field): + """Returns a function which returns a default value for a field. + + Args: + field: FieldDescriptor object for this field. + + The returned function has one argument: + message: Message instance containing this field, or a weakref proxy + of same. + + That function in turn returns a default value for this field. The default + value may refer back to |message| via a weak reference. + """ + + if _IsMapField(field): + return _GetInitializeDefaultForMap(field) + + if field.label == _FieldDescriptor.LABEL_REPEATED: + if field.has_default_value and field.default_value != []: + raise ValueError('Repeated field default value not empty list: %s' % ( + field.default_value)) + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + # We can't look at _concrete_class yet since it might not have + # been set. (Depends on order in which we initialize the classes). + message_type = field.message_type + def MakeRepeatedMessageDefault(message): + return containers.RepeatedCompositeFieldContainer( + message._listener_for_children, field.message_type) + return MakeRepeatedMessageDefault + else: + type_checker = type_checkers.GetTypeChecker(field) + def MakeRepeatedScalarDefault(message): + return containers.RepeatedScalarFieldContainer( + message._listener_for_children, type_checker) + return MakeRepeatedScalarDefault + + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + # _concrete_class may not yet be initialized. + message_type = field.message_type + def MakeSubMessageDefault(message): + assert getattr(message_type, '_concrete_class', None), ( + 'Uninitialized concrete class found for field %r (message type %r)' + % (field.full_name, message_type.full_name)) + result = message_type._concrete_class() + result._SetListener( + _OneofListener(message, field) + if field.containing_oneof is not None + else message._listener_for_children) + return result + return MakeSubMessageDefault + + def MakeScalarDefault(message): + # TODO(protobuf-team): This may be broken since there may not be + # default_value. Combine with has_default_value somehow. + return field.default_value + return MakeScalarDefault + + +def _ReraiseTypeErrorWithFieldName(message_name, field_name): + """Re-raise the currently-handled TypeError with the field name added.""" + exc = sys.exc_info()[1] + if len(exc.args) == 1 and type(exc) is TypeError: + # simple TypeError; add field name to exception message + exc = TypeError('%s for field %s.%s' % (str(exc), message_name, field_name)) + + # re-raise possibly-amended exception with original traceback: + raise exc.with_traceback(sys.exc_info()[2]) + + +def _AddInitMethod(message_descriptor, cls): + """Adds an __init__ method to cls.""" + + def _GetIntegerEnumValue(enum_type, value): + """Convert a string or integer enum value to an integer. + + If the value is a string, it is converted to the enum value in + enum_type with the same name. If the value is not a string, it's + returned as-is. (No conversion or bounds-checking is done.) + """ + if isinstance(value, str): + try: + return enum_type.values_by_name[value].number + except KeyError: + raise ValueError('Enum type %s: unknown label "%s"' % ( + enum_type.full_name, value)) + return value + + def init(self, **kwargs): + self._cached_byte_size = 0 + self._cached_byte_size_dirty = len(kwargs) > 0 + self._fields = {} + # Contains a mapping from oneof field descriptors to the descriptor + # of the currently set field in that oneof field. + self._oneofs = {} + + # _unknown_fields is () when empty for efficiency, and will be turned into + # a list if fields are added. + self._unknown_fields = () + # _unknown_field_set is None when empty for efficiency, and will be + # turned into UnknownFieldSet struct if fields are added. + self._unknown_field_set = None # pylint: disable=protected-access + self._is_present_in_parent = False + self._listener = message_listener_mod.NullMessageListener() + self._listener_for_children = _Listener(self) + for field_name, field_value in kwargs.items(): + field = _GetFieldByName(message_descriptor, field_name) + if field is None: + raise TypeError('%s() got an unexpected keyword argument "%s"' % + (message_descriptor.name, field_name)) + if field_value is None: + # field=None is the same as no field at all. + continue + if field.label == _FieldDescriptor.LABEL_REPEATED: + copy = field._default_constructor(self) + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: # Composite + if _IsMapField(field): + if _IsMessageMapField(field): + for key in field_value: + copy[key].MergeFrom(field_value[key]) + else: + copy.update(field_value) + else: + for val in field_value: + if isinstance(val, dict): + copy.add(**val) + else: + copy.add().MergeFrom(val) + else: # Scalar + if field.cpp_type == _FieldDescriptor.CPPTYPE_ENUM: + field_value = [_GetIntegerEnumValue(field.enum_type, val) + for val in field_value] + copy.extend(field_value) + self._fields[field] = copy + elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + copy = field._default_constructor(self) + new_val = field_value + if isinstance(field_value, dict): + new_val = field.message_type._concrete_class(**field_value) + try: + copy.MergeFrom(new_val) + except TypeError: + _ReraiseTypeErrorWithFieldName(message_descriptor.name, field_name) + self._fields[field] = copy + else: + if field.cpp_type == _FieldDescriptor.CPPTYPE_ENUM: + field_value = _GetIntegerEnumValue(field.enum_type, field_value) + try: + setattr(self, field_name, field_value) + except TypeError: + _ReraiseTypeErrorWithFieldName(message_descriptor.name, field_name) + + init.__module__ = None + init.__doc__ = None + cls.__init__ = init + + +def _GetFieldByName(message_descriptor, field_name): + """Returns a field descriptor by field name. + + Args: + message_descriptor: A Descriptor describing all fields in message. + field_name: The name of the field to retrieve. + Returns: + The field descriptor associated with the field name. + """ + try: + return message_descriptor.fields_by_name[field_name] + except KeyError: + raise ValueError('Protocol message %s has no "%s" field.' % + (message_descriptor.name, field_name)) + + +def _AddPropertiesForFields(descriptor, cls): + """Adds properties for all fields in this protocol message type.""" + for field in descriptor.fields: + _AddPropertiesForField(field, cls) + + if descriptor.is_extendable: + # _ExtensionDict is just an adaptor with no state so we allocate a new one + # every time it is accessed. + cls.Extensions = property(lambda self: _ExtensionDict(self)) + + +def _AddPropertiesForField(field, cls): + """Adds a public property for a protocol message field. + Clients can use this property to get and (in the case + of non-repeated scalar fields) directly set the value + of a protocol message field. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + # Catch it if we add other types that we should + # handle specially here. + assert _FieldDescriptor.MAX_CPPTYPE == 10 + + constant_name = field.name.upper() + '_FIELD_NUMBER' + setattr(cls, constant_name, field.number) + + if field.label == _FieldDescriptor.LABEL_REPEATED: + _AddPropertiesForRepeatedField(field, cls) + elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + _AddPropertiesForNonRepeatedCompositeField(field, cls) + else: + _AddPropertiesForNonRepeatedScalarField(field, cls) + + +class _FieldProperty(property): + __slots__ = ('DESCRIPTOR',) + + def __init__(self, descriptor, getter, setter, doc): + property.__init__(self, getter, setter, doc=doc) + self.DESCRIPTOR = descriptor + + +def _AddPropertiesForRepeatedField(field, cls): + """Adds a public property for a "repeated" protocol message field. Clients + can use this property to get the value of the field, which will be either a + RepeatedScalarFieldContainer or RepeatedCompositeFieldContainer (see + below). + + Note that when clients add values to these containers, we perform + type-checking in the case of repeated scalar fields, and we also set any + necessary "has" bits as a side-effect. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + proto_field_name = field.name + property_name = _PropertyName(proto_field_name) + + def getter(self): + field_value = self._fields.get(field) + if field_value is None: + # Construct a new object to represent this field. + field_value = field._default_constructor(self) + + # Atomically check if another thread has preempted us and, if not, swap + # in the new object we just created. If someone has preempted us, we + # take that object and discard ours. + # WARNING: We are relying on setdefault() being atomic. This is true + # in CPython but we haven't investigated others. This warning appears + # in several other locations in this file. + field_value = self._fields.setdefault(field, field_value) + return field_value + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + + # We define a setter just so we can throw an exception with a more + # helpful error message. + def setter(self, new_value): + raise AttributeError('Assignment not allowed to repeated field ' + '"%s" in protocol message object.' % proto_field_name) + + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, _FieldProperty(field, getter, setter, doc=doc)) + + +def _AddPropertiesForNonRepeatedScalarField(field, cls): + """Adds a public property for a nonrepeated, scalar protocol message field. + Clients can use this property to get and directly set the value of the field. + Note that when the client sets the value of a field by using this property, + all necessary "has" bits are set as a side-effect, and we also perform + type-checking. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + proto_field_name = field.name + property_name = _PropertyName(proto_field_name) + type_checker = type_checkers.GetTypeChecker(field) + default_value = field.default_value + is_proto3 = field.containing_type.syntax == 'proto3' + + def getter(self): + # TODO(protobuf-team): This may be broken since there may not be + # default_value. Combine with has_default_value somehow. + return self._fields.get(field, default_value) + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + + clear_when_set_to_default = is_proto3 and not field.containing_oneof + + def field_setter(self, new_value): + # pylint: disable=protected-access + # Testing the value for truthiness captures all of the proto3 defaults + # (0, 0.0, enum 0, and False). + try: + new_value = type_checker.CheckValue(new_value) + except TypeError as e: + raise TypeError( + 'Cannot set %s to %.1024r: %s' % (field.full_name, new_value, e)) + if clear_when_set_to_default and not new_value: + self._fields.pop(field, None) + else: + self._fields[field] = new_value + # Check _cached_byte_size_dirty inline to improve performance, since scalar + # setters are called frequently. + if not self._cached_byte_size_dirty: + self._Modified() + + if field.containing_oneof: + def setter(self, new_value): + field_setter(self, new_value) + self._UpdateOneofState(field) + else: + setter = field_setter + + setter.__module__ = None + setter.__doc__ = 'Setter for %s.' % proto_field_name + + # Add a property to encapsulate the getter/setter. + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, _FieldProperty(field, getter, setter, doc=doc)) + + +def _AddPropertiesForNonRepeatedCompositeField(field, cls): + """Adds a public property for a nonrepeated, composite protocol message field. + A composite field is a "group" or "message" field. + + Clients can use this property to get the value of the field, but cannot + assign to the property directly. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + # TODO(robinson): Remove duplication with similar method + # for non-repeated scalars. + proto_field_name = field.name + property_name = _PropertyName(proto_field_name) + + def getter(self): + field_value = self._fields.get(field) + if field_value is None: + # Construct a new object to represent this field. + field_value = field._default_constructor(self) + + # Atomically check if another thread has preempted us and, if not, swap + # in the new object we just created. If someone has preempted us, we + # take that object and discard ours. + # WARNING: We are relying on setdefault() being atomic. This is true + # in CPython but we haven't investigated others. This warning appears + # in several other locations in this file. + field_value = self._fields.setdefault(field, field_value) + return field_value + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + + # We define a setter just so we can throw an exception with a more + # helpful error message. + def setter(self, new_value): + raise AttributeError('Assignment not allowed to composite field ' + '"%s" in protocol message object.' % proto_field_name) + + # Add a property to encapsulate the getter. + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, _FieldProperty(field, getter, setter, doc=doc)) + + +def _AddPropertiesForExtensions(descriptor, cls): + """Adds properties for all fields in this protocol message type.""" + extensions = descriptor.extensions_by_name + for extension_name, extension_field in extensions.items(): + constant_name = extension_name.upper() + '_FIELD_NUMBER' + setattr(cls, constant_name, extension_field.number) + + # TODO(amauryfa): Migrate all users of these attributes to functions like + # pool.FindExtensionByNumber(descriptor). + if descriptor.file is not None: + # TODO(amauryfa): Use cls.MESSAGE_FACTORY.pool when available. + pool = descriptor.file.pool + cls._extensions_by_number = pool._extensions_by_number[descriptor] + cls._extensions_by_name = pool._extensions_by_name[descriptor] + +def _AddStaticMethods(cls): + # TODO(robinson): This probably needs to be thread-safe(?) + def RegisterExtension(extension_handle): + extension_handle.containing_type = cls.DESCRIPTOR + # TODO(amauryfa): Use cls.MESSAGE_FACTORY.pool when available. + # pylint: disable=protected-access + cls.DESCRIPTOR.file.pool._AddExtensionDescriptor(extension_handle) + _AttachFieldHelpers(cls, extension_handle) + cls.RegisterExtension = staticmethod(RegisterExtension) + + def FromString(s): + message = cls() + message.MergeFromString(s) + return message + cls.FromString = staticmethod(FromString) + + +def _IsPresent(item): + """Given a (FieldDescriptor, value) tuple from _fields, return true if the + value should be included in the list returned by ListFields().""" + + if item[0].label == _FieldDescriptor.LABEL_REPEATED: + return bool(item[1]) + elif item[0].cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + return item[1]._is_present_in_parent + else: + return True + + +def _AddListFieldsMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def ListFields(self): + all_fields = [item for item in self._fields.items() if _IsPresent(item)] + all_fields.sort(key = lambda item: item[0].number) + return all_fields + + cls.ListFields = ListFields + +_PROTO3_ERROR_TEMPLATE = \ + ('Protocol message %s has no non-repeated submessage field "%s" ' + 'nor marked as optional') +_PROTO2_ERROR_TEMPLATE = 'Protocol message %s has no non-repeated field "%s"' + +def _AddHasFieldMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + is_proto3 = (message_descriptor.syntax == "proto3") + error_msg = _PROTO3_ERROR_TEMPLATE if is_proto3 else _PROTO2_ERROR_TEMPLATE + + hassable_fields = {} + for field in message_descriptor.fields: + if field.label == _FieldDescriptor.LABEL_REPEATED: + continue + # For proto3, only submessages and fields inside a oneof have presence. + if (is_proto3 and field.cpp_type != _FieldDescriptor.CPPTYPE_MESSAGE and + not field.containing_oneof): + continue + hassable_fields[field.name] = field + + # Has methods are supported for oneof descriptors. + for oneof in message_descriptor.oneofs: + hassable_fields[oneof.name] = oneof + + def HasField(self, field_name): + try: + field = hassable_fields[field_name] + except KeyError: + raise ValueError(error_msg % (message_descriptor.full_name, field_name)) + + if isinstance(field, descriptor_mod.OneofDescriptor): + try: + return HasField(self, self._oneofs[field].name) + except KeyError: + return False + else: + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + value = self._fields.get(field) + return value is not None and value._is_present_in_parent + else: + return field in self._fields + + cls.HasField = HasField + + +def _AddClearFieldMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + def ClearField(self, field_name): + try: + field = message_descriptor.fields_by_name[field_name] + except KeyError: + try: + field = message_descriptor.oneofs_by_name[field_name] + if field in self._oneofs: + field = self._oneofs[field] + else: + return + except KeyError: + raise ValueError('Protocol message %s has no "%s" field.' % + (message_descriptor.name, field_name)) + + if field in self._fields: + # To match the C++ implementation, we need to invalidate iterators + # for map fields when ClearField() happens. + if hasattr(self._fields[field], 'InvalidateIterators'): + self._fields[field].InvalidateIterators() + + # Note: If the field is a sub-message, its listener will still point + # at us. That's fine, because the worst than can happen is that it + # will call _Modified() and invalidate our byte size. Big deal. + del self._fields[field] + + if self._oneofs.get(field.containing_oneof, None) is field: + del self._oneofs[field.containing_oneof] + + # Always call _Modified() -- even if nothing was changed, this is + # a mutating method, and thus calling it should cause the field to become + # present in the parent message. + self._Modified() + + cls.ClearField = ClearField + + +def _AddClearExtensionMethod(cls): + """Helper for _AddMessageMethods().""" + def ClearExtension(self, extension_handle): + extension_dict._VerifyExtensionHandle(self, extension_handle) + + # Similar to ClearField(), above. + if extension_handle in self._fields: + del self._fields[extension_handle] + self._Modified() + cls.ClearExtension = ClearExtension + + +def _AddHasExtensionMethod(cls): + """Helper for _AddMessageMethods().""" + def HasExtension(self, extension_handle): + extension_dict._VerifyExtensionHandle(self, extension_handle) + if extension_handle.label == _FieldDescriptor.LABEL_REPEATED: + raise KeyError('"%s" is repeated.' % extension_handle.full_name) + + if extension_handle.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + value = self._fields.get(extension_handle) + return value is not None and value._is_present_in_parent + else: + return extension_handle in self._fields + cls.HasExtension = HasExtension + +def _InternalUnpackAny(msg): + """Unpacks Any message and returns the unpacked message. + + This internal method is different from public Any Unpack method which takes + the target message as argument. _InternalUnpackAny method does not have + target message type and need to find the message type in descriptor pool. + + Args: + msg: An Any message to be unpacked. + + Returns: + The unpacked message. + """ + # TODO(amauryfa): Don't use the factory of generated messages. + # To make Any work with custom factories, use the message factory of the + # parent message. + # pylint: disable=g-import-not-at-top + from google.protobuf import symbol_database + factory = symbol_database.Default() + + type_url = msg.type_url + + if not type_url: + return None + + # TODO(haberman): For now we just strip the hostname. Better logic will be + # required. + type_name = type_url.split('/')[-1] + descriptor = factory.pool.FindMessageTypeByName(type_name) + + if descriptor is None: + return None + + message_class = factory.GetPrototype(descriptor) + message = message_class() + + message.ParseFromString(msg.value) + return message + + +def _AddEqualsMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + def __eq__(self, other): + if (not isinstance(other, message_mod.Message) or + other.DESCRIPTOR != self.DESCRIPTOR): + return False + + if self is other: + return True + + if self.DESCRIPTOR.full_name == _AnyFullTypeName: + any_a = _InternalUnpackAny(self) + any_b = _InternalUnpackAny(other) + if any_a and any_b: + return any_a == any_b + + if not self.ListFields() == other.ListFields(): + return False + + # TODO(jieluo): Fix UnknownFieldSet to consider MessageSet extensions, + # then use it for the comparison. + unknown_fields = list(self._unknown_fields) + unknown_fields.sort() + other_unknown_fields = list(other._unknown_fields) + other_unknown_fields.sort() + return unknown_fields == other_unknown_fields + + cls.__eq__ = __eq__ + + +def _AddStrMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + def __str__(self): + return text_format.MessageToString(self) + cls.__str__ = __str__ + + +def _AddReprMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + def __repr__(self): + return text_format.MessageToString(self) + cls.__repr__ = __repr__ + + +def _AddUnicodeMethod(unused_message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def __unicode__(self): + return text_format.MessageToString(self, as_utf8=True).decode('utf-8') + cls.__unicode__ = __unicode__ + + +def _BytesForNonRepeatedElement(value, field_number, field_type): + """Returns the number of bytes needed to serialize a non-repeated element. + The returned byte count includes space for tag information and any + other additional space associated with serializing value. + + Args: + value: Value we're serializing. + field_number: Field number of this value. (Since the field number + is stored as part of a varint-encoded tag, this has an impact + on the total bytes required to serialize the value). + field_type: The type of the field. One of the TYPE_* constants + within FieldDescriptor. + """ + try: + fn = type_checkers.TYPE_TO_BYTE_SIZE_FN[field_type] + return fn(field_number, value) + except KeyError: + raise message_mod.EncodeError('Unrecognized field type: %d' % field_type) + + +def _AddByteSizeMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def ByteSize(self): + if not self._cached_byte_size_dirty: + return self._cached_byte_size + + size = 0 + descriptor = self.DESCRIPTOR + if descriptor.GetOptions().map_entry: + # Fields of map entry should always be serialized. + size = descriptor.fields_by_name['key']._sizer(self.key) + size += descriptor.fields_by_name['value']._sizer(self.value) + else: + for field_descriptor, field_value in self.ListFields(): + size += field_descriptor._sizer(field_value) + for tag_bytes, value_bytes in self._unknown_fields: + size += len(tag_bytes) + len(value_bytes) + + self._cached_byte_size = size + self._cached_byte_size_dirty = False + self._listener_for_children.dirty = False + return size + + cls.ByteSize = ByteSize + + +def _AddSerializeToStringMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def SerializeToString(self, **kwargs): + # Check if the message has all of its required fields set. + if not self.IsInitialized(): + raise message_mod.EncodeError( + 'Message %s is missing required fields: %s' % ( + self.DESCRIPTOR.full_name, ','.join(self.FindInitializationErrors()))) + return self.SerializePartialToString(**kwargs) + cls.SerializeToString = SerializeToString + + +def _AddSerializePartialToStringMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def SerializePartialToString(self, **kwargs): + out = BytesIO() + self._InternalSerialize(out.write, **kwargs) + return out.getvalue() + cls.SerializePartialToString = SerializePartialToString + + def InternalSerialize(self, write_bytes, deterministic=None): + if deterministic is None: + deterministic = ( + api_implementation.IsPythonDefaultSerializationDeterministic()) + else: + deterministic = bool(deterministic) + + descriptor = self.DESCRIPTOR + if descriptor.GetOptions().map_entry: + # Fields of map entry should always be serialized. + descriptor.fields_by_name['key']._encoder( + write_bytes, self.key, deterministic) + descriptor.fields_by_name['value']._encoder( + write_bytes, self.value, deterministic) + else: + for field_descriptor, field_value in self.ListFields(): + field_descriptor._encoder(write_bytes, field_value, deterministic) + for tag_bytes, value_bytes in self._unknown_fields: + write_bytes(tag_bytes) + write_bytes(value_bytes) + cls._InternalSerialize = InternalSerialize + + +def _AddMergeFromStringMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + def MergeFromString(self, serialized): + serialized = memoryview(serialized) + length = len(serialized) + try: + if self._InternalParse(serialized, 0, length) != length: + # The only reason _InternalParse would return early is if it + # encountered an end-group tag. + raise message_mod.DecodeError('Unexpected end-group tag.') + except (IndexError, TypeError): + # Now ord(buf[p:p+1]) == ord('') gets TypeError. + raise message_mod.DecodeError('Truncated message.') + except struct.error as e: + raise message_mod.DecodeError(e) + return length # Return this for legacy reasons. + cls.MergeFromString = MergeFromString + + local_ReadTag = decoder.ReadTag + local_SkipField = decoder.SkipField + decoders_by_tag = cls._decoders_by_tag + + def InternalParse(self, buffer, pos, end): + """Create a message from serialized bytes. + + Args: + self: Message, instance of the proto message object. + buffer: memoryview of the serialized data. + pos: int, position to start in the serialized data. + end: int, end position of the serialized data. + + Returns: + Message object. + """ + # Guard against internal misuse, since this function is called internally + # quite extensively, and its easy to accidentally pass bytes. + assert isinstance(buffer, memoryview) + self._Modified() + field_dict = self._fields + # pylint: disable=protected-access + unknown_field_set = self._unknown_field_set + while pos != end: + (tag_bytes, new_pos) = local_ReadTag(buffer, pos) + field_decoder, field_desc = decoders_by_tag.get(tag_bytes, (None, None)) + if field_decoder is None: + if not self._unknown_fields: # pylint: disable=protected-access + self._unknown_fields = [] # pylint: disable=protected-access + if unknown_field_set is None: + # pylint: disable=protected-access + self._unknown_field_set = containers.UnknownFieldSet() + # pylint: disable=protected-access + unknown_field_set = self._unknown_field_set + # pylint: disable=protected-access + (tag, _) = decoder._DecodeVarint(tag_bytes, 0) + field_number, wire_type = wire_format.UnpackTag(tag) + if field_number == 0: + raise message_mod.DecodeError('Field number 0 is illegal.') + # TODO(jieluo): remove old_pos. + old_pos = new_pos + (data, new_pos) = decoder._DecodeUnknownField( + buffer, new_pos, wire_type) # pylint: disable=protected-access + if new_pos == -1: + return pos + # pylint: disable=protected-access + unknown_field_set._add(field_number, wire_type, data) + # TODO(jieluo): remove _unknown_fields. + new_pos = local_SkipField(buffer, old_pos, end, tag_bytes) + if new_pos == -1: + return pos + self._unknown_fields.append( + (tag_bytes, buffer[old_pos:new_pos].tobytes())) + pos = new_pos + else: + pos = field_decoder(buffer, new_pos, end, self, field_dict) + if field_desc: + self._UpdateOneofState(field_desc) + return pos + cls._InternalParse = InternalParse + + +def _AddIsInitializedMethod(message_descriptor, cls): + """Adds the IsInitialized and FindInitializationError methods to the + protocol message class.""" + + required_fields = [field for field in message_descriptor.fields + if field.label == _FieldDescriptor.LABEL_REQUIRED] + + def IsInitialized(self, errors=None): + """Checks if all required fields of a message are set. + + Args: + errors: A list which, if provided, will be populated with the field + paths of all missing required fields. + + Returns: + True iff the specified message has all required fields set. + """ + + # Performance is critical so we avoid HasField() and ListFields(). + + for field in required_fields: + if (field not in self._fields or + (field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE and + not self._fields[field]._is_present_in_parent)): + if errors is not None: + errors.extend(self.FindInitializationErrors()) + return False + + for field, value in list(self._fields.items()): # dict can change size! + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + if field.label == _FieldDescriptor.LABEL_REPEATED: + if (field.message_type.has_options and + field.message_type.GetOptions().map_entry): + continue + for element in value: + if not element.IsInitialized(): + if errors is not None: + errors.extend(self.FindInitializationErrors()) + return False + elif value._is_present_in_parent and not value.IsInitialized(): + if errors is not None: + errors.extend(self.FindInitializationErrors()) + return False + + return True + + cls.IsInitialized = IsInitialized + + def FindInitializationErrors(self): + """Finds required fields which are not initialized. + + Returns: + A list of strings. Each string is a path to an uninitialized field from + the top-level message, e.g. "foo.bar[5].baz". + """ + + errors = [] # simplify things + + for field in required_fields: + if not self.HasField(field.name): + errors.append(field.name) + + for field, value in self.ListFields(): + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + if field.is_extension: + name = '(%s)' % field.full_name + else: + name = field.name + + if _IsMapField(field): + if _IsMessageMapField(field): + for key in value: + element = value[key] + prefix = '%s[%s].' % (name, key) + sub_errors = element.FindInitializationErrors() + errors += [prefix + error for error in sub_errors] + else: + # ScalarMaps can't have any initialization errors. + pass + elif field.label == _FieldDescriptor.LABEL_REPEATED: + for i in range(len(value)): + element = value[i] + prefix = '%s[%d].' % (name, i) + sub_errors = element.FindInitializationErrors() + errors += [prefix + error for error in sub_errors] + else: + prefix = name + '.' + sub_errors = value.FindInitializationErrors() + errors += [prefix + error for error in sub_errors] + + return errors + + cls.FindInitializationErrors = FindInitializationErrors + + +def _FullyQualifiedClassName(klass): + module = klass.__module__ + name = getattr(klass, '__qualname__', klass.__name__) + if module in (None, 'builtins', '__builtin__'): + return name + return module + '.' + name + + +def _AddMergeFromMethod(cls): + LABEL_REPEATED = _FieldDescriptor.LABEL_REPEATED + CPPTYPE_MESSAGE = _FieldDescriptor.CPPTYPE_MESSAGE + + def MergeFrom(self, msg): + if not isinstance(msg, cls): + raise TypeError( + 'Parameter to MergeFrom() must be instance of same class: ' + 'expected %s got %s.' % (_FullyQualifiedClassName(cls), + _FullyQualifiedClassName(msg.__class__))) + + assert msg is not self + self._Modified() + + fields = self._fields + + for field, value in msg._fields.items(): + if field.label == LABEL_REPEATED: + field_value = fields.get(field) + if field_value is None: + # Construct a new object to represent this field. + field_value = field._default_constructor(self) + fields[field] = field_value + field_value.MergeFrom(value) + elif field.cpp_type == CPPTYPE_MESSAGE: + if value._is_present_in_parent: + field_value = fields.get(field) + if field_value is None: + # Construct a new object to represent this field. + field_value = field._default_constructor(self) + fields[field] = field_value + field_value.MergeFrom(value) + else: + self._fields[field] = value + if field.containing_oneof: + self._UpdateOneofState(field) + + if msg._unknown_fields: + if not self._unknown_fields: + self._unknown_fields = [] + self._unknown_fields.extend(msg._unknown_fields) + # pylint: disable=protected-access + if self._unknown_field_set is None: + self._unknown_field_set = containers.UnknownFieldSet() + self._unknown_field_set._extend(msg._unknown_field_set) + + cls.MergeFrom = MergeFrom + + +def _AddWhichOneofMethod(message_descriptor, cls): + def WhichOneof(self, oneof_name): + """Returns the name of the currently set field inside a oneof, or None.""" + try: + field = message_descriptor.oneofs_by_name[oneof_name] + except KeyError: + raise ValueError( + 'Protocol message has no oneof "%s" field.' % oneof_name) + + nested_field = self._oneofs.get(field, None) + if nested_field is not None and self.HasField(nested_field.name): + return nested_field.name + else: + return None + + cls.WhichOneof = WhichOneof + + +def _Clear(self): + # Clear fields. + self._fields = {} + self._unknown_fields = () + # pylint: disable=protected-access + if self._unknown_field_set is not None: + self._unknown_field_set._clear() + self._unknown_field_set = None + + self._oneofs = {} + self._Modified() + + +def _UnknownFields(self): + if self._unknown_field_set is None: # pylint: disable=protected-access + # pylint: disable=protected-access + self._unknown_field_set = containers.UnknownFieldSet() + return self._unknown_field_set # pylint: disable=protected-access + + +def _DiscardUnknownFields(self): + self._unknown_fields = [] + self._unknown_field_set = None # pylint: disable=protected-access + for field, value in self.ListFields(): + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + if _IsMapField(field): + if _IsMessageMapField(field): + for key in value: + value[key].DiscardUnknownFields() + elif field.label == _FieldDescriptor.LABEL_REPEATED: + for sub_message in value: + sub_message.DiscardUnknownFields() + else: + value.DiscardUnknownFields() + + +def _SetListener(self, listener): + if listener is None: + self._listener = message_listener_mod.NullMessageListener() + else: + self._listener = listener + + +def _AddMessageMethods(message_descriptor, cls): + """Adds implementations of all Message methods to cls.""" + _AddListFieldsMethod(message_descriptor, cls) + _AddHasFieldMethod(message_descriptor, cls) + _AddClearFieldMethod(message_descriptor, cls) + if message_descriptor.is_extendable: + _AddClearExtensionMethod(cls) + _AddHasExtensionMethod(cls) + _AddEqualsMethod(message_descriptor, cls) + _AddStrMethod(message_descriptor, cls) + _AddReprMethod(message_descriptor, cls) + _AddUnicodeMethod(message_descriptor, cls) + _AddByteSizeMethod(message_descriptor, cls) + _AddSerializeToStringMethod(message_descriptor, cls) + _AddSerializePartialToStringMethod(message_descriptor, cls) + _AddMergeFromStringMethod(message_descriptor, cls) + _AddIsInitializedMethod(message_descriptor, cls) + _AddMergeFromMethod(cls) + _AddWhichOneofMethod(message_descriptor, cls) + # Adds methods which do not depend on cls. + cls.Clear = _Clear + cls.UnknownFields = _UnknownFields + cls.DiscardUnknownFields = _DiscardUnknownFields + cls._SetListener = _SetListener + + +def _AddPrivateHelperMethods(message_descriptor, cls): + """Adds implementation of private helper methods to cls.""" + + def Modified(self): + """Sets the _cached_byte_size_dirty bit to true, + and propagates this to our listener iff this was a state change. + """ + + # Note: Some callers check _cached_byte_size_dirty before calling + # _Modified() as an extra optimization. So, if this method is ever + # changed such that it does stuff even when _cached_byte_size_dirty is + # already true, the callers need to be updated. + if not self._cached_byte_size_dirty: + self._cached_byte_size_dirty = True + self._listener_for_children.dirty = True + self._is_present_in_parent = True + self._listener.Modified() + + def _UpdateOneofState(self, field): + """Sets field as the active field in its containing oneof. + + Will also delete currently active field in the oneof, if it is different + from the argument. Does not mark the message as modified. + """ + other_field = self._oneofs.setdefault(field.containing_oneof, field) + if other_field is not field: + del self._fields[other_field] + self._oneofs[field.containing_oneof] = field + + cls._Modified = Modified + cls.SetInParent = Modified + cls._UpdateOneofState = _UpdateOneofState + + +class _Listener(object): + + """MessageListener implementation that a parent message registers with its + child message. + + In order to support semantics like: + + foo.bar.baz.moo = 23 + assert foo.HasField('bar') + + ...child objects must have back references to their parents. + This helper class is at the heart of this support. + """ + + def __init__(self, parent_message): + """Args: + parent_message: The message whose _Modified() method we should call when + we receive Modified() messages. + """ + # This listener establishes a back reference from a child (contained) object + # to its parent (containing) object. We make this a weak reference to avoid + # creating cyclic garbage when the client finishes with the 'parent' object + # in the tree. + if isinstance(parent_message, weakref.ProxyType): + self._parent_message_weakref = parent_message + else: + self._parent_message_weakref = weakref.proxy(parent_message) + + # As an optimization, we also indicate directly on the listener whether + # or not the parent message is dirty. This way we can avoid traversing + # up the tree in the common case. + self.dirty = False + + def Modified(self): + if self.dirty: + return + try: + # Propagate the signal to our parents iff this is the first field set. + self._parent_message_weakref._Modified() + except ReferenceError: + # We can get here if a client has kept a reference to a child object, + # and is now setting a field on it, but the child's parent has been + # garbage-collected. This is not an error. + pass + + +class _OneofListener(_Listener): + """Special listener implementation for setting composite oneof fields.""" + + def __init__(self, parent_message, field): + """Args: + parent_message: The message whose _Modified() method we should call when + we receive Modified() messages. + field: The descriptor of the field being set in the parent message. + """ + super(_OneofListener, self).__init__(parent_message) + self._field = field + + def Modified(self): + """Also updates the state of the containing oneof in the parent message.""" + try: + self._parent_message_weakref._UpdateOneofState(self._field) + super(_OneofListener, self).Modified() + except ReferenceError: + pass diff --git a/virt/lib/python3.9/site-packages/google/protobuf/internal/test_util 3.py b/virt/lib/python3.9/site-packages/google/protobuf/internal/test_util 3.py new file mode 100644 index 00000000..12da1ce3 --- /dev/null +++ b/virt/lib/python3.9/site-packages/google/protobuf/internal/test_util 3.py @@ -0,0 +1,878 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# 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 Google 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 +# OWNER 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. + +"""Utilities for Python proto2 tests. + +This is intentionally modeled on C++ code in +//google/protobuf/test_util.*. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import numbers +import operator +import os.path + +from google.protobuf import unittest_import_pb2 +from google.protobuf import unittest_pb2 + +try: + long # Python 2 +except NameError: + long = int # Python 3 + + +# Tests whether the given TestAllTypes message is proto2 or not. +# This is used to gate several fields/features that only exist +# for the proto2 version of the message. +def IsProto2(message): + return message.DESCRIPTOR.syntax == "proto2" + + +def SetAllNonLazyFields(message): + """Sets every non-lazy field in the message to a unique value. + + Args: + message: A TestAllTypes instance. + """ + + # + # Optional fields. + # + + message.optional_int32 = 101 + message.optional_int64 = 102 + message.optional_uint32 = 103 + message.optional_uint64 = 104 + message.optional_sint32 = 105 + message.optional_sint64 = 106 + message.optional_fixed32 = 107 + message.optional_fixed64 = 108 + message.optional_sfixed32 = 109 + message.optional_sfixed64 = 110 + message.optional_float = 111 + message.optional_double = 112 + message.optional_bool = True + message.optional_string = u'115' + message.optional_bytes = b'116' + + if IsProto2(message): + message.optionalgroup.a = 117 + message.optional_nested_message.bb = 118 + message.optional_foreign_message.c = 119 + message.optional_import_message.d = 120 + message.optional_public_import_message.e = 126 + + message.optional_nested_enum = unittest_pb2.TestAllTypes.BAZ + message.optional_foreign_enum = unittest_pb2.FOREIGN_BAZ + if IsProto2(message): + message.optional_import_enum = unittest_import_pb2.IMPORT_BAZ + + message.optional_string_piece = u'124' + message.optional_cord = u'125' + + # + # Repeated fields. + # + + message.repeated_int32.append(201) + message.repeated_int64.append(202) + message.repeated_uint32.append(203) + message.repeated_uint64.append(204) + message.repeated_sint32.append(205) + message.repeated_sint64.append(206) + message.repeated_fixed32.append(207) + message.repeated_fixed64.append(208) + message.repeated_sfixed32.append(209) + message.repeated_sfixed64.append(210) + message.repeated_float.append(211) + message.repeated_double.append(212) + message.repeated_bool.append(True) + message.repeated_string.append(u'215') + message.repeated_bytes.append(b'216') + + if IsProto2(message): + message.repeatedgroup.add().a = 217 + message.repeated_nested_message.add().bb = 218 + message.repeated_foreign_message.add().c = 219 + message.repeated_import_message.add().d = 220 + message.repeated_lazy_message.add().bb = 227 + + message.repeated_nested_enum.append(unittest_pb2.TestAllTypes.BAR) + message.repeated_foreign_enum.append(unittest_pb2.FOREIGN_BAR) + if IsProto2(message): + message.repeated_import_enum.append(unittest_import_pb2.IMPORT_BAR) + + message.repeated_string_piece.append(u'224') + message.repeated_cord.append(u'225') + + # Add a second one of each field and set value by index. + message.repeated_int32.append(0) + message.repeated_int64.append(0) + message.repeated_uint32.append(0) + message.repeated_uint64.append(0) + message.repeated_sint32.append(0) + message.repeated_sint64.append(0) + message.repeated_fixed32.append(0) + message.repeated_fixed64.append(0) + message.repeated_sfixed32.append(0) + message.repeated_sfixed64.append(0) + message.repeated_float.append(0) + message.repeated_double.append(0) + message.repeated_bool.append(True) + message.repeated_string.append(u'0') + message.repeated_bytes.append(b'0') + message.repeated_int32[1] = 301 + message.repeated_int64[1] = 302 + message.repeated_uint32[1] = 303 + message.repeated_uint64[1] = 304 + message.repeated_sint32[1] = 305 + message.repeated_sint64[1] = 306 + message.repeated_fixed32[1] = 307 + message.repeated_fixed64[1] = 308 + message.repeated_sfixed32[1] = 309 + message.repeated_sfixed64[1] = 310 + message.repeated_float[1] = 311 + message.repeated_double[1] = 312 + message.repeated_bool[1] = False + message.repeated_string[1] = u'315' + message.repeated_bytes[1] = b'316' + + if IsProto2(message): + message.repeatedgroup.add().a = 317 + message.repeated_nested_message.add().bb = 318 + message.repeated_foreign_message.add().c = 319 + message.repeated_import_message.add().d = 320 + message.repeated_lazy_message.add().bb = 327 + + message.repeated_nested_enum.append(unittest_pb2.TestAllTypes.BAR) + message.repeated_nested_enum[1] = unittest_pb2.TestAllTypes.BAZ + message.repeated_foreign_enum.append(unittest_pb2.FOREIGN_BAZ) + if IsProto2(message): + message.repeated_import_enum.append(unittest_import_pb2.IMPORT_BAZ) + + message.repeated_string_piece.append(u'324') + message.repeated_cord.append(u'325') + + # + # Fields that have defaults. + # + + if IsProto2(message): + message.default_int32 = 401 + message.default_int64 = 402 + message.default_uint32 = 403 + message.default_uint64 = 404 + message.default_sint32 = 405 + message.default_sint64 = 406 + message.default_fixed32 = 407 + message.default_fixed64 = 408 + message.default_sfixed32 = 409 + message.default_sfixed64 = 410 + message.default_float = 411 + message.default_double = 412 + message.default_bool = False + message.default_string = '415' + message.default_bytes = b'416' + + message.default_nested_enum = unittest_pb2.TestAllTypes.FOO + message.default_foreign_enum = unittest_pb2.FOREIGN_FOO + message.default_import_enum = unittest_import_pb2.IMPORT_FOO + + message.default_string_piece = '424' + message.default_cord = '425' + + message.oneof_uint32 = 601 + message.oneof_nested_message.bb = 602 + message.oneof_string = '603' + message.oneof_bytes = b'604' + + +def SetAllFields(message): + SetAllNonLazyFields(message) + message.optional_lazy_message.bb = 127 + message.optional_unverified_lazy_message.bb = 128 + + +def SetAllExtensions(message): + """Sets every extension in the message to a unique value. + + Args: + message: A unittest_pb2.TestAllExtensions instance. + """ + + extensions = message.Extensions + pb2 = unittest_pb2 + import_pb2 = unittest_import_pb2 + + # + # Optional fields. + # + + extensions[pb2.optional_int32_extension] = 101 + extensions[pb2.optional_int64_extension] = 102 + extensions[pb2.optional_uint32_extension] = 103 + extensions[pb2.optional_uint64_extension] = 104 + extensions[pb2.optional_sint32_extension] = 105 + extensions[pb2.optional_sint64_extension] = 106 + extensions[pb2.optional_fixed32_extension] = 107 + extensions[pb2.optional_fixed64_extension] = 108 + extensions[pb2.optional_sfixed32_extension] = 109 + extensions[pb2.optional_sfixed64_extension] = 110 + extensions[pb2.optional_float_extension] = 111 + extensions[pb2.optional_double_extension] = 112 + extensions[pb2.optional_bool_extension] = True + extensions[pb2.optional_string_extension] = u'115' + extensions[pb2.optional_bytes_extension] = b'116' + + extensions[pb2.optionalgroup_extension].a = 117 + extensions[pb2.optional_nested_message_extension].bb = 118 + extensions[pb2.optional_foreign_message_extension].c = 119 + extensions[pb2.optional_import_message_extension].d = 120 + extensions[pb2.optional_public_import_message_extension].e = 126 + extensions[pb2.optional_lazy_message_extension].bb = 127 + extensions[pb2.optional_unverified_lazy_message_extension].bb = 128 + + extensions[pb2.optional_nested_enum_extension] = pb2.TestAllTypes.BAZ + extensions[pb2.optional_nested_enum_extension] = pb2.TestAllTypes.BAZ + extensions[pb2.optional_foreign_enum_extension] = pb2.FOREIGN_BAZ + extensions[pb2.optional_import_enum_extension] = import_pb2.IMPORT_BAZ + + extensions[pb2.optional_string_piece_extension] = u'124' + extensions[pb2.optional_cord_extension] = u'125' + + # + # Repeated fields. + # + + extensions[pb2.repeated_int32_extension].append(201) + extensions[pb2.repeated_int64_extension].append(202) + extensions[pb2.repeated_uint32_extension].append(203) + extensions[pb2.repeated_uint64_extension].append(204) + extensions[pb2.repeated_sint32_extension].append(205) + extensions[pb2.repeated_sint64_extension].append(206) + extensions[pb2.repeated_fixed32_extension].append(207) + extensions[pb2.repeated_fixed64_extension].append(208) + extensions[pb2.repeated_sfixed32_extension].append(209) + extensions[pb2.repeated_sfixed64_extension].append(210) + extensions[pb2.repeated_float_extension].append(211) + extensions[pb2.repeated_double_extension].append(212) + extensions[pb2.repeated_bool_extension].append(True) + extensions[pb2.repeated_string_extension].append(u'215') + extensions[pb2.repeated_bytes_extension].append(b'216') + + extensions[pb2.repeatedgroup_extension].add().a = 217 + extensions[pb2.repeated_nested_message_extension].add().bb = 218 + extensions[pb2.repeated_foreign_message_extension].add().c = 219 + extensions[pb2.repeated_import_message_extension].add().d = 220 + extensions[pb2.repeated_lazy_message_extension].add().bb = 227 + + extensions[pb2.repeated_nested_enum_extension].append(pb2.TestAllTypes.BAR) + extensions[pb2.repeated_foreign_enum_extension].append(pb2.FOREIGN_BAR) + extensions[pb2.repeated_import_enum_extension].append(import_pb2.IMPORT_BAR) + + extensions[pb2.repeated_string_piece_extension].append(u'224') + extensions[pb2.repeated_cord_extension].append(u'225') + + # Append a second one of each field. + extensions[pb2.repeated_int32_extension].append(301) + extensions[pb2.repeated_int64_extension].append(302) + extensions[pb2.repeated_uint32_extension].append(303) + extensions[pb2.repeated_uint64_extension].append(304) + extensions[pb2.repeated_sint32_extension].append(305) + extensions[pb2.repeated_sint64_extension].append(306) + extensions[pb2.repeated_fixed32_extension].append(307) + extensions[pb2.repeated_fixed64_extension].append(308) + extensions[pb2.repeated_sfixed32_extension].append(309) + extensions[pb2.repeated_sfixed64_extension].append(310) + extensions[pb2.repeated_float_extension].append(311) + extensions[pb2.repeated_double_extension].append(312) + extensions[pb2.repeated_bool_extension].append(False) + extensions[pb2.repeated_string_extension].append(u'315') + extensions[pb2.repeated_bytes_extension].append(b'316') + + extensions[pb2.repeatedgroup_extension].add().a = 317 + extensions[pb2.repeated_nested_message_extension].add().bb = 318 + extensions[pb2.repeated_foreign_message_extension].add().c = 319 + extensions[pb2.repeated_import_message_extension].add().d = 320 + extensions[pb2.repeated_lazy_message_extension].add().bb = 327 + + extensions[pb2.repeated_nested_enum_extension].append(pb2.TestAllTypes.BAZ) + extensions[pb2.repeated_foreign_enum_extension].append(pb2.FOREIGN_BAZ) + extensions[pb2.repeated_import_enum_extension].append(import_pb2.IMPORT_BAZ) + + extensions[pb2.repeated_string_piece_extension].append(u'324') + extensions[pb2.repeated_cord_extension].append(u'325') + + # + # Fields with defaults. + # + + extensions[pb2.default_int32_extension] = 401 + extensions[pb2.default_int64_extension] = 402 + extensions[pb2.default_uint32_extension] = 403 + extensions[pb2.default_uint64_extension] = 404 + extensions[pb2.default_sint32_extension] = 405 + extensions[pb2.default_sint64_extension] = 406 + extensions[pb2.default_fixed32_extension] = 407 + extensions[pb2.default_fixed64_extension] = 408 + extensions[pb2.default_sfixed32_extension] = 409 + extensions[pb2.default_sfixed64_extension] = 410 + extensions[pb2.default_float_extension] = 411 + extensions[pb2.default_double_extension] = 412 + extensions[pb2.default_bool_extension] = False + extensions[pb2.default_string_extension] = u'415' + extensions[pb2.default_bytes_extension] = b'416' + + extensions[pb2.default_nested_enum_extension] = pb2.TestAllTypes.FOO + extensions[pb2.default_foreign_enum_extension] = pb2.FOREIGN_FOO + extensions[pb2.default_import_enum_extension] = import_pb2.IMPORT_FOO + + extensions[pb2.default_string_piece_extension] = u'424' + extensions[pb2.default_cord_extension] = '425' + + extensions[pb2.oneof_uint32_extension] = 601 + extensions[pb2.oneof_nested_message_extension].bb = 602 + extensions[pb2.oneof_string_extension] = u'603' + extensions[pb2.oneof_bytes_extension] = b'604' + + +def SetAllFieldsAndExtensions(message): + """Sets every field and extension in the message to a unique value. + + Args: + message: A unittest_pb2.TestAllExtensions message. + """ + message.my_int = 1 + message.my_string = 'foo' + message.my_float = 1.0 + message.Extensions[unittest_pb2.my_extension_int] = 23 + message.Extensions[unittest_pb2.my_extension_string] = 'bar' + + +def ExpectAllFieldsAndExtensionsInOrder(serialized): + """Ensures that serialized is the serialization we expect for a message + filled with SetAllFieldsAndExtensions(). (Specifically, ensures that the + serialization is in canonical, tag-number order). + """ + my_extension_int = unittest_pb2.my_extension_int + my_extension_string = unittest_pb2.my_extension_string + expected_strings = [] + message = unittest_pb2.TestFieldOrderings() + message.my_int = 1 # Field 1. + expected_strings.append(message.SerializeToString()) + message.Clear() + message.Extensions[my_extension_int] = 23 # Field 5. + expected_strings.append(message.SerializeToString()) + message.Clear() + message.my_string = 'foo' # Field 11. + expected_strings.append(message.SerializeToString()) + message.Clear() + message.Extensions[my_extension_string] = 'bar' # Field 50. + expected_strings.append(message.SerializeToString()) + message.Clear() + message.my_float = 1.0 + expected_strings.append(message.SerializeToString()) + message.Clear() + expected = b''.join(expected_strings) + + if expected != serialized: + raise ValueError('Expected %r, found %r' % (expected, serialized)) + + +def ExpectAllFieldsSet(test_case, message): + """Check all fields for correct values have after Set*Fields() is called.""" + test_case.assertTrue(message.HasField('optional_int32')) + test_case.assertTrue(message.HasField('optional_int64')) + test_case.assertTrue(message.HasField('optional_uint32')) + test_case.assertTrue(message.HasField('optional_uint64')) + test_case.assertTrue(message.HasField('optional_sint32')) + test_case.assertTrue(message.HasField('optional_sint64')) + test_case.assertTrue(message.HasField('optional_fixed32')) + test_case.assertTrue(message.HasField('optional_fixed64')) + test_case.assertTrue(message.HasField('optional_sfixed32')) + test_case.assertTrue(message.HasField('optional_sfixed64')) + test_case.assertTrue(message.HasField('optional_float')) + test_case.assertTrue(message.HasField('optional_double')) + test_case.assertTrue(message.HasField('optional_bool')) + test_case.assertTrue(message.HasField('optional_string')) + test_case.assertTrue(message.HasField('optional_bytes')) + + if IsProto2(message): + test_case.assertTrue(message.HasField('optionalgroup')) + test_case.assertTrue(message.HasField('optional_nested_message')) + test_case.assertTrue(message.HasField('optional_foreign_message')) + test_case.assertTrue(message.HasField('optional_import_message')) + + test_case.assertTrue(message.optionalgroup.HasField('a')) + test_case.assertTrue(message.optional_nested_message.HasField('bb')) + test_case.assertTrue(message.optional_foreign_message.HasField('c')) + test_case.assertTrue(message.optional_import_message.HasField('d')) + + test_case.assertTrue(message.HasField('optional_nested_enum')) + test_case.assertTrue(message.HasField('optional_foreign_enum')) + if IsProto2(message): + test_case.assertTrue(message.HasField('optional_import_enum')) + + test_case.assertTrue(message.HasField('optional_string_piece')) + test_case.assertTrue(message.HasField('optional_cord')) + + test_case.assertEqual(101, message.optional_int32) + test_case.assertEqual(102, message.optional_int64) + test_case.assertEqual(103, message.optional_uint32) + test_case.assertEqual(104, message.optional_uint64) + test_case.assertEqual(105, message.optional_sint32) + test_case.assertEqual(106, message.optional_sint64) + test_case.assertEqual(107, message.optional_fixed32) + test_case.assertEqual(108, message.optional_fixed64) + test_case.assertEqual(109, message.optional_sfixed32) + test_case.assertEqual(110, message.optional_sfixed64) + test_case.assertEqual(111, message.optional_float) + test_case.assertEqual(112, message.optional_double) + test_case.assertEqual(True, message.optional_bool) + test_case.assertEqual('115', message.optional_string) + test_case.assertEqual(b'116', message.optional_bytes) + + if IsProto2(message): + test_case.assertEqual(117, message.optionalgroup.a) + test_case.assertEqual(118, message.optional_nested_message.bb) + test_case.assertEqual(119, message.optional_foreign_message.c) + test_case.assertEqual(120, message.optional_import_message.d) + test_case.assertEqual(126, message.optional_public_import_message.e) + test_case.assertEqual(127, message.optional_lazy_message.bb) + test_case.assertEqual(128, message.optional_unverified_lazy_message.bb) + + test_case.assertEqual(unittest_pb2.TestAllTypes.BAZ, + message.optional_nested_enum) + test_case.assertEqual(unittest_pb2.FOREIGN_BAZ, + message.optional_foreign_enum) + if IsProto2(message): + test_case.assertEqual(unittest_import_pb2.IMPORT_BAZ, + message.optional_import_enum) + + # ----------------------------------------------------------------- + + test_case.assertEqual(2, len(message.repeated_int32)) + test_case.assertEqual(2, len(message.repeated_int64)) + test_case.assertEqual(2, len(message.repeated_uint32)) + test_case.assertEqual(2, len(message.repeated_uint64)) + test_case.assertEqual(2, len(message.repeated_sint32)) + test_case.assertEqual(2, len(message.repeated_sint64)) + test_case.assertEqual(2, len(message.repeated_fixed32)) + test_case.assertEqual(2, len(message.repeated_fixed64)) + test_case.assertEqual(2, len(message.repeated_sfixed32)) + test_case.assertEqual(2, len(message.repeated_sfixed64)) + test_case.assertEqual(2, len(message.repeated_float)) + test_case.assertEqual(2, len(message.repeated_double)) + test_case.assertEqual(2, len(message.repeated_bool)) + test_case.assertEqual(2, len(message.repeated_string)) + test_case.assertEqual(2, len(message.repeated_bytes)) + + if IsProto2(message): + test_case.assertEqual(2, len(message.repeatedgroup)) + test_case.assertEqual(2, len(message.repeated_nested_message)) + test_case.assertEqual(2, len(message.repeated_foreign_message)) + test_case.assertEqual(2, len(message.repeated_import_message)) + test_case.assertEqual(2, len(message.repeated_nested_enum)) + test_case.assertEqual(2, len(message.repeated_foreign_enum)) + if IsProto2(message): + test_case.assertEqual(2, len(message.repeated_import_enum)) + + test_case.assertEqual(2, len(message.repeated_string_piece)) + test_case.assertEqual(2, len(message.repeated_cord)) + + test_case.assertEqual(201, message.repeated_int32[0]) + test_case.assertEqual(202, message.repeated_int64[0]) + test_case.assertEqual(203, message.repeated_uint32[0]) + test_case.assertEqual(204, message.repeated_uint64[0]) + test_case.assertEqual(205, message.repeated_sint32[0]) + test_case.assertEqual(206, message.repeated_sint64[0]) + test_case.assertEqual(207, message.repeated_fixed32[0]) + test_case.assertEqual(208, message.repeated_fixed64[0]) + test_case.assertEqual(209, message.repeated_sfixed32[0]) + test_case.assertEqual(210, message.repeated_sfixed64[0]) + test_case.assertEqual(211, message.repeated_float[0]) + test_case.assertEqual(212, message.repeated_double[0]) + test_case.assertEqual(True, message.repeated_bool[0]) + test_case.assertEqual('215', message.repeated_string[0]) + test_case.assertEqual(b'216', message.repeated_bytes[0]) + + if IsProto2(message): + test_case.assertEqual(217, message.repeatedgroup[0].a) + test_case.assertEqual(218, message.repeated_nested_message[0].bb) + test_case.assertEqual(219, message.repeated_foreign_message[0].c) + test_case.assertEqual(220, message.repeated_import_message[0].d) + test_case.assertEqual(227, message.repeated_lazy_message[0].bb) + + test_case.assertEqual(unittest_pb2.TestAllTypes.BAR, + message.repeated_nested_enum[0]) + test_case.assertEqual(unittest_pb2.FOREIGN_BAR, + message.repeated_foreign_enum[0]) + if IsProto2(message): + test_case.assertEqual(unittest_import_pb2.IMPORT_BAR, + message.repeated_import_enum[0]) + + test_case.assertEqual(301, message.repeated_int32[1]) + test_case.assertEqual(302, message.repeated_int64[1]) + test_case.assertEqual(303, message.repeated_uint32[1]) + test_case.assertEqual(304, message.repeated_uint64[1]) + test_case.assertEqual(305, message.repeated_sint32[1]) + test_case.assertEqual(306, message.repeated_sint64[1]) + test_case.assertEqual(307, message.repeated_fixed32[1]) + test_case.assertEqual(308, message.repeated_fixed64[1]) + test_case.assertEqual(309, message.repeated_sfixed32[1]) + test_case.assertEqual(310, message.repeated_sfixed64[1]) + test_case.assertEqual(311, message.repeated_float[1]) + test_case.assertEqual(312, message.repeated_double[1]) + test_case.assertEqual(False, message.repeated_bool[1]) + test_case.assertEqual('315', message.repeated_string[1]) + test_case.assertEqual(b'316', message.repeated_bytes[1]) + + if IsProto2(message): + test_case.assertEqual(317, message.repeatedgroup[1].a) + test_case.assertEqual(318, message.repeated_nested_message[1].bb) + test_case.assertEqual(319, message.repeated_foreign_message[1].c) + test_case.assertEqual(320, message.repeated_import_message[1].d) + test_case.assertEqual(327, message.repeated_lazy_message[1].bb) + + test_case.assertEqual(unittest_pb2.TestAllTypes.BAZ, + message.repeated_nested_enum[1]) + test_case.assertEqual(unittest_pb2.FOREIGN_BAZ, + message.repeated_foreign_enum[1]) + if IsProto2(message): + test_case.assertEqual(unittest_import_pb2.IMPORT_BAZ, + message.repeated_import_enum[1]) + + # ----------------------------------------------------------------- + + if IsProto2(message): + test_case.assertTrue(message.HasField('default_int32')) + test_case.assertTrue(message.HasField('default_int64')) + test_case.assertTrue(message.HasField('default_uint32')) + test_case.assertTrue(message.HasField('default_uint64')) + test_case.assertTrue(message.HasField('default_sint32')) + test_case.assertTrue(message.HasField('default_sint64')) + test_case.assertTrue(message.HasField('default_fixed32')) + test_case.assertTrue(message.HasField('default_fixed64')) + test_case.assertTrue(message.HasField('default_sfixed32')) + test_case.assertTrue(message.HasField('default_sfixed64')) + test_case.assertTrue(message.HasField('default_float')) + test_case.assertTrue(message.HasField('default_double')) + test_case.assertTrue(message.HasField('default_bool')) + test_case.assertTrue(message.HasField('default_string')) + test_case.assertTrue(message.HasField('default_bytes')) + + test_case.assertTrue(message.HasField('default_nested_enum')) + test_case.assertTrue(message.HasField('default_foreign_enum')) + test_case.assertTrue(message.HasField('default_import_enum')) + + test_case.assertEqual(401, message.default_int32) + test_case.assertEqual(402, message.default_int64) + test_case.assertEqual(403, message.default_uint32) + test_case.assertEqual(404, message.default_uint64) + test_case.assertEqual(405, message.default_sint32) + test_case.assertEqual(406, message.default_sint64) + test_case.assertEqual(407, message.default_fixed32) + test_case.assertEqual(408, message.default_fixed64) + test_case.assertEqual(409, message.default_sfixed32) + test_case.assertEqual(410, message.default_sfixed64) + test_case.assertEqual(411, message.default_float) + test_case.assertEqual(412, message.default_double) + test_case.assertEqual(False, message.default_bool) + test_case.assertEqual('415', message.default_string) + test_case.assertEqual(b'416', message.default_bytes) + + test_case.assertEqual(unittest_pb2.TestAllTypes.FOO, + message.default_nested_enum) + test_case.assertEqual(unittest_pb2.FOREIGN_FOO, + message.default_foreign_enum) + test_case.assertEqual(unittest_import_pb2.IMPORT_FOO, + message.default_import_enum) + + +def GoldenFile(filename): + """Finds the given golden file and returns a file object representing it.""" + + # Search up the directory tree looking for the C++ protobuf source code. + path = '.' + while os.path.exists(path): + if os.path.exists(os.path.join(path, 'src/google/protobuf')): + # Found it. Load the golden file from the testdata directory. + full_path = os.path.join(path, 'src/google/protobuf/testdata', filename) + return open(full_path, 'rb') + path = os.path.join(path, '..') + + # Search internally. + path = '.' + full_path = os.path.join(path, 'third_party/py/google/protobuf/testdata', + filename) + if os.path.exists(full_path): + # Found it. Load the golden file from the testdata directory. + return open(full_path, 'rb') + + # Search for cross-repo path. + full_path = os.path.join('external/com_google_protobuf/src/google/protobuf/testdata', + filename) + if os.path.exists(full_path): + # Found it. Load the golden file from the testdata directory. + return open(full_path, 'rb') + + raise RuntimeError( + 'Could not find golden files. This test must be run from within the ' + 'protobuf source package so that it can read test data files from the ' + 'C++ source tree.') + + +def GoldenFileData(filename): + """Finds the given golden file and returns its contents.""" + with GoldenFile(filename) as f: + return f.read() + + +def SetAllPackedFields(message): + """Sets every field in the message to a unique value. + + Args: + message: A TestPackedTypes instance. + """ + message.packed_int32.extend([601, 701]) + message.packed_int64.extend([602, 702]) + message.packed_uint32.extend([603, 703]) + message.packed_uint64.extend([604, 704]) + message.packed_sint32.extend([605, 705]) + message.packed_sint64.extend([606, 706]) + message.packed_fixed32.extend([607, 707]) + message.packed_fixed64.extend([608, 708]) + message.packed_sfixed32.extend([609, 709]) + message.packed_sfixed64.extend([610, 710]) + message.packed_float.extend([611.0, 711.0]) + message.packed_double.extend([612.0, 712.0]) + message.packed_bool.extend([True, False]) + message.packed_enum.extend([unittest_pb2.FOREIGN_BAR, + unittest_pb2.FOREIGN_BAZ]) + + +def SetAllPackedExtensions(message): + """Sets every extension in the message to a unique value. + + Args: + message: A unittest_pb2.TestPackedExtensions instance. + """ + extensions = message.Extensions + pb2 = unittest_pb2 + + extensions[pb2.packed_int32_extension].extend([601, 701]) + extensions[pb2.packed_int64_extension].extend([602, 702]) + extensions[pb2.packed_uint32_extension].extend([603, 703]) + extensions[pb2.packed_uint64_extension].extend([604, 704]) + extensions[pb2.packed_sint32_extension].extend([605, 705]) + extensions[pb2.packed_sint64_extension].extend([606, 706]) + extensions[pb2.packed_fixed32_extension].extend([607, 707]) + extensions[pb2.packed_fixed64_extension].extend([608, 708]) + extensions[pb2.packed_sfixed32_extension].extend([609, 709]) + extensions[pb2.packed_sfixed64_extension].extend([610, 710]) + extensions[pb2.packed_float_extension].extend([611.0, 711.0]) + extensions[pb2.packed_double_extension].extend([612.0, 712.0]) + extensions[pb2.packed_bool_extension].extend([True, False]) + extensions[pb2.packed_enum_extension].extend([unittest_pb2.FOREIGN_BAR, + unittest_pb2.FOREIGN_BAZ]) + + +def SetAllUnpackedFields(message): + """Sets every field in the message to a unique value. + + Args: + message: A unittest_pb2.TestUnpackedTypes instance. + """ + message.unpacked_int32.extend([601, 701]) + message.unpacked_int64.extend([602, 702]) + message.unpacked_uint32.extend([603, 703]) + message.unpacked_uint64.extend([604, 704]) + message.unpacked_sint32.extend([605, 705]) + message.unpacked_sint64.extend([606, 706]) + message.unpacked_fixed32.extend([607, 707]) + message.unpacked_fixed64.extend([608, 708]) + message.unpacked_sfixed32.extend([609, 709]) + message.unpacked_sfixed64.extend([610, 710]) + message.unpacked_float.extend([611.0, 711.0]) + message.unpacked_double.extend([612.0, 712.0]) + message.unpacked_bool.extend([True, False]) + message.unpacked_enum.extend([unittest_pb2.FOREIGN_BAR, + unittest_pb2.FOREIGN_BAZ]) + + +class NonStandardInteger(numbers.Integral): + """An integer object that does not subclass int. + + This is used to verify that both C++ and regular proto systems can handle + integer others than int and long and that they handle them in predictable + ways. + + NonStandardInteger is the minimal legal specification for a custom Integral. + As such, it does not support 0 < x < 5 and it is not hashable. + + Note: This is added here instead of relying on numpy or a similar library + with custom integers to limit dependencies. + """ + + def __init__(self, val, error_string_on_conversion=None): + assert isinstance(val, numbers.Integral) + if isinstance(val, NonStandardInteger): + val = val.val + self.val = val + self.error_string_on_conversion = error_string_on_conversion + + def __long__(self): + if self.error_string_on_conversion: + raise RuntimeError(self.error_string_on_conversion) + return long(self.val) + + def __abs__(self): + return NonStandardInteger(operator.abs(self.val)) + + def __add__(self, y): + return NonStandardInteger(operator.add(self.val, y)) + + def __div__(self, y): + return NonStandardInteger(operator.div(self.val, y)) + + def __eq__(self, y): + return operator.eq(self.val, y) + + def __floordiv__(self, y): + return NonStandardInteger(operator.floordiv(self.val, y)) + + def __truediv__(self, y): + return NonStandardInteger(operator.truediv(self.val, y)) + + def __invert__(self): + return NonStandardInteger(operator.invert(self.val)) + + def __mod__(self, y): + return NonStandardInteger(operator.mod(self.val, y)) + + def __mul__(self, y): + return NonStandardInteger(operator.mul(self.val, y)) + + def __neg__(self): + return NonStandardInteger(operator.neg(self.val)) + + def __pos__(self): + return NonStandardInteger(operator.pos(self.val)) + + def __pow__(self, y): + return NonStandardInteger(operator.pow(self.val, y)) + + def __trunc__(self): + return int(self.val) + + def __radd__(self, y): + return NonStandardInteger(operator.add(y, self.val)) + + def __rdiv__(self, y): + return NonStandardInteger(operator.div(y, self.val)) + + def __rmod__(self, y): + return NonStandardInteger(operator.mod(y, self.val)) + + def __rmul__(self, y): + return NonStandardInteger(operator.mul(y, self.val)) + + def __rpow__(self, y): + return NonStandardInteger(operator.pow(y, self.val)) + + def __rfloordiv__(self, y): + return NonStandardInteger(operator.floordiv(y, self.val)) + + def __rtruediv__(self, y): + return NonStandardInteger(operator.truediv(y, self.val)) + + def __lshift__(self, y): + return NonStandardInteger(operator.lshift(self.val, y)) + + def __rshift__(self, y): + return NonStandardInteger(operator.rshift(self.val, y)) + + def __rlshift__(self, y): + return NonStandardInteger(operator.lshift(y, self.val)) + + def __rrshift__(self, y): + return NonStandardInteger(operator.rshift(y, self.val)) + + def __le__(self, y): + if isinstance(y, NonStandardInteger): + y = y.val + return operator.le(self.val, y) + + def __lt__(self, y): + if isinstance(y, NonStandardInteger): + y = y.val + return operator.lt(self.val, y) + + def __and__(self, y): + return NonStandardInteger(operator.and_(self.val, y)) + + def __or__(self, y): + return NonStandardInteger(operator.or_(self.val, y)) + + def __xor__(self, y): + return NonStandardInteger(operator.xor(self.val, y)) + + def __rand__(self, y): + return NonStandardInteger(operator.and_(y, self.val)) + + def __ror__(self, y): + return NonStandardInteger(operator.or_(y, self.val)) + + def __rxor__(self, y): + return NonStandardInteger(operator.xor(y, self.val)) + + def __bool__(self): + return self.val + + def __nonzero__(self): + return self.val + + def __ceil__(self): + return self + + def __floor__(self): + return self + + def __int__(self): + if self.error_string_on_conversion: + raise RuntimeError(self.error_string_on_conversion) + return int(self.val) + + def __round__(self): + return self + + def __repr__(self): + return 'NonStandardInteger(%s)' % self.val diff --git a/virt/lib/python3.9/site-packages/google/protobuf/internal/type_checkers 3.py b/virt/lib/python3.9/site-packages/google/protobuf/internal/type_checkers 3.py new file mode 100644 index 00000000..5f8e5406 --- /dev/null +++ b/virt/lib/python3.9/site-packages/google/protobuf/internal/type_checkers 3.py @@ -0,0 +1,435 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# 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 Google 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 +# OWNER 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. + +"""Provides type checking routines. + +This module defines type checking utilities in the forms of dictionaries: + +VALUE_CHECKERS: A dictionary of field types and a value validation object. +TYPE_TO_BYTE_SIZE_FN: A dictionary with field types and a size computing + function. +TYPE_TO_SERIALIZE_METHOD: A dictionary with field types and serialization + function. +FIELD_TYPE_TO_WIRE_TYPE: A dictionary with field typed and their + corresponding wire types. +TYPE_TO_DESERIALIZE_METHOD: A dictionary with field types and deserialization + function. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import ctypes +import numbers + +from google.protobuf.internal import decoder +from google.protobuf.internal import encoder +from google.protobuf.internal import wire_format +from google.protobuf import descriptor + +_FieldDescriptor = descriptor.FieldDescriptor + + +def TruncateToFourByteFloat(original): + return ctypes.c_float(original).value + + +def ToShortestFloat(original): + """Returns the shortest float that has same value in wire.""" + # All 4 byte floats have between 6 and 9 significant digits, so we + # start with 6 as the lower bound. + # It has to be iterative because use '.9g' directly can not get rid + # of the noises for most values. For example if set a float_field=0.9 + # use '.9g' will print 0.899999976. + precision = 6 + rounded = float('{0:.{1}g}'.format(original, precision)) + while TruncateToFourByteFloat(rounded) != original: + precision += 1 + rounded = float('{0:.{1}g}'.format(original, precision)) + return rounded + + +def SupportsOpenEnums(field_descriptor): + return field_descriptor.containing_type.syntax == 'proto3' + + +def GetTypeChecker(field): + """Returns a type checker for a message field of the specified types. + + Args: + field: FieldDescriptor object for this field. + + Returns: + An instance of TypeChecker which can be used to verify the types + of values assigned to a field of the specified type. + """ + if (field.cpp_type == _FieldDescriptor.CPPTYPE_STRING and + field.type == _FieldDescriptor.TYPE_STRING): + return UnicodeValueChecker() + if field.cpp_type == _FieldDescriptor.CPPTYPE_ENUM: + if SupportsOpenEnums(field): + # When open enums are supported, any int32 can be assigned. + return _VALUE_CHECKERS[_FieldDescriptor.CPPTYPE_INT32] + else: + return EnumValueChecker(field.enum_type) + return _VALUE_CHECKERS[field.cpp_type] + + +# None of the typecheckers below make any attempt to guard against people +# subclassing builtin types and doing weird things. We're not trying to +# protect against malicious clients here, just people accidentally shooting +# themselves in the foot in obvious ways. +class TypeChecker(object): + + """Type checker used to catch type errors as early as possible + when the client is setting scalar fields in protocol messages. + """ + + def __init__(self, *acceptable_types): + self._acceptable_types = acceptable_types + + def CheckValue(self, proposed_value): + """Type check the provided value and return it. + + The returned value might have been normalized to another type. + """ + if not isinstance(proposed_value, self._acceptable_types): + message = ('%.1024r has type %s, but expected one of: %s' % + (proposed_value, type(proposed_value), self._acceptable_types)) + raise TypeError(message) + return proposed_value + + +class TypeCheckerWithDefault(TypeChecker): + + def __init__(self, default_value, *acceptable_types): + TypeChecker.__init__(self, *acceptable_types) + self._default_value = default_value + + def DefaultValue(self): + return self._default_value + + +class BoolValueChecker(object): + """Type checker used for bool fields.""" + + def CheckValue(self, proposed_value): + if not hasattr(proposed_value, '__index__') or ( + type(proposed_value).__module__ == 'numpy' and + type(proposed_value).__name__ == 'ndarray'): + message = ('%.1024r has type %s, but expected one of: %s' % + (proposed_value, type(proposed_value), (bool, int))) + raise TypeError(message) + return bool(proposed_value) + + def DefaultValue(self): + return False + + +# IntValueChecker and its subclasses perform integer type-checks +# and bounds-checks. +class IntValueChecker(object): + + """Checker used for integer fields. Performs type-check and range check.""" + + def CheckValue(self, proposed_value): + if not hasattr(proposed_value, '__index__') or ( + type(proposed_value).__module__ == 'numpy' and + type(proposed_value).__name__ == 'ndarray'): + message = ('%.1024r has type %s, but expected one of: %s' % + (proposed_value, type(proposed_value), (int,))) + raise TypeError(message) + + if not self._MIN <= int(proposed_value) <= self._MAX: + raise ValueError('Value out of range: %d' % proposed_value) + # We force all values to int to make alternate implementations where the + # distinction is more significant (e.g. the C++ implementation) simpler. + proposed_value = int(proposed_value) + return proposed_value + + def DefaultValue(self): + return 0 + + +class EnumValueChecker(object): + + """Checker used for enum fields. Performs type-check and range check.""" + + def __init__(self, enum_type): + self._enum_type = enum_type + + def CheckValue(self, proposed_value): + if not isinstance(proposed_value, numbers.Integral): + message = ('%.1024r has type %s, but expected one of: %s' % + (proposed_value, type(proposed_value), (int,))) + raise TypeError(message) + if int(proposed_value) not in self._enum_type.values_by_number: + raise ValueError('Unknown enum value: %d' % proposed_value) + return proposed_value + + def DefaultValue(self): + return self._enum_type.values[0].number + + +class UnicodeValueChecker(object): + + """Checker used for string fields. + + Always returns a unicode value, even if the input is of type str. + """ + + def CheckValue(self, proposed_value): + if not isinstance(proposed_value, (bytes, str)): + message = ('%.1024r has type %s, but expected one of: %s' % + (proposed_value, type(proposed_value), (bytes, str))) + raise TypeError(message) + + # If the value is of type 'bytes' make sure that it is valid UTF-8 data. + if isinstance(proposed_value, bytes): + try: + proposed_value = proposed_value.decode('utf-8') + except UnicodeDecodeError: + raise ValueError('%.1024r has type bytes, but isn\'t valid UTF-8 ' + 'encoding. Non-UTF-8 strings must be converted to ' + 'unicode objects before being added.' % + (proposed_value)) + else: + try: + proposed_value.encode('utf8') + except UnicodeEncodeError: + raise ValueError('%.1024r isn\'t a valid unicode string and ' + 'can\'t be encoded in UTF-8.'% + (proposed_value)) + + return proposed_value + + def DefaultValue(self): + return u"" + + +class Int32ValueChecker(IntValueChecker): + # We're sure to use ints instead of longs here since comparison may be more + # efficient. + _MIN = -2147483648 + _MAX = 2147483647 + + +class Uint32ValueChecker(IntValueChecker): + _MIN = 0 + _MAX = (1 << 32) - 1 + + +class Int64ValueChecker(IntValueChecker): + _MIN = -(1 << 63) + _MAX = (1 << 63) - 1 + + +class Uint64ValueChecker(IntValueChecker): + _MIN = 0 + _MAX = (1 << 64) - 1 + + +# The max 4 bytes float is about 3.4028234663852886e+38 +_FLOAT_MAX = float.fromhex('0x1.fffffep+127') +_FLOAT_MIN = -_FLOAT_MAX +_INF = float('inf') +_NEG_INF = float('-inf') + + +class DoubleValueChecker(object): + """Checker used for double fields. + + Performs type-check and range check. + """ + + def CheckValue(self, proposed_value): + """Check and convert proposed_value to float.""" + if (not hasattr(proposed_value, '__float__') and + not hasattr(proposed_value, '__index__')) or ( + type(proposed_value).__module__ == 'numpy' and + type(proposed_value).__name__ == 'ndarray'): + message = ('%.1024r has type %s, but expected one of: int, float' % + (proposed_value, type(proposed_value))) + raise TypeError(message) + return float(proposed_value) + + def DefaultValue(self): + return 0.0 + + +class FloatValueChecker(DoubleValueChecker): + """Checker used for float fields. + + Performs type-check and range check. + + Values exceeding a 32-bit float will be converted to inf/-inf. + """ + + def CheckValue(self, proposed_value): + """Check and convert proposed_value to float.""" + converted_value = super().CheckValue(proposed_value) + # This inf rounding matches the C++ proto SafeDoubleToFloat logic. + if converted_value > _FLOAT_MAX: + return _INF + if converted_value < _FLOAT_MIN: + return _NEG_INF + + return TruncateToFourByteFloat(converted_value) + +# Type-checkers for all scalar CPPTYPEs. +_VALUE_CHECKERS = { + _FieldDescriptor.CPPTYPE_INT32: Int32ValueChecker(), + _FieldDescriptor.CPPTYPE_INT64: Int64ValueChecker(), + _FieldDescriptor.CPPTYPE_UINT32: Uint32ValueChecker(), + _FieldDescriptor.CPPTYPE_UINT64: Uint64ValueChecker(), + _FieldDescriptor.CPPTYPE_DOUBLE: DoubleValueChecker(), + _FieldDescriptor.CPPTYPE_FLOAT: FloatValueChecker(), + _FieldDescriptor.CPPTYPE_BOOL: BoolValueChecker(), + _FieldDescriptor.CPPTYPE_STRING: TypeCheckerWithDefault(b'', bytes), +} + + +# Map from field type to a function F, such that F(field_num, value) +# gives the total byte size for a value of the given type. This +# byte size includes tag information and any other additional space +# associated with serializing "value". +TYPE_TO_BYTE_SIZE_FN = { + _FieldDescriptor.TYPE_DOUBLE: wire_format.DoubleByteSize, + _FieldDescriptor.TYPE_FLOAT: wire_format.FloatByteSize, + _FieldDescriptor.TYPE_INT64: wire_format.Int64ByteSize, + _FieldDescriptor.TYPE_UINT64: wire_format.UInt64ByteSize, + _FieldDescriptor.TYPE_INT32: wire_format.Int32ByteSize, + _FieldDescriptor.TYPE_FIXED64: wire_format.Fixed64ByteSize, + _FieldDescriptor.TYPE_FIXED32: wire_format.Fixed32ByteSize, + _FieldDescriptor.TYPE_BOOL: wire_format.BoolByteSize, + _FieldDescriptor.TYPE_STRING: wire_format.StringByteSize, + _FieldDescriptor.TYPE_GROUP: wire_format.GroupByteSize, + _FieldDescriptor.TYPE_MESSAGE: wire_format.MessageByteSize, + _FieldDescriptor.TYPE_BYTES: wire_format.BytesByteSize, + _FieldDescriptor.TYPE_UINT32: wire_format.UInt32ByteSize, + _FieldDescriptor.TYPE_ENUM: wire_format.EnumByteSize, + _FieldDescriptor.TYPE_SFIXED32: wire_format.SFixed32ByteSize, + _FieldDescriptor.TYPE_SFIXED64: wire_format.SFixed64ByteSize, + _FieldDescriptor.TYPE_SINT32: wire_format.SInt32ByteSize, + _FieldDescriptor.TYPE_SINT64: wire_format.SInt64ByteSize + } + + +# Maps from field types to encoder constructors. +TYPE_TO_ENCODER = { + _FieldDescriptor.TYPE_DOUBLE: encoder.DoubleEncoder, + _FieldDescriptor.TYPE_FLOAT: encoder.FloatEncoder, + _FieldDescriptor.TYPE_INT64: encoder.Int64Encoder, + _FieldDescriptor.TYPE_UINT64: encoder.UInt64Encoder, + _FieldDescriptor.TYPE_INT32: encoder.Int32Encoder, + _FieldDescriptor.TYPE_FIXED64: encoder.Fixed64Encoder, + _FieldDescriptor.TYPE_FIXED32: encoder.Fixed32Encoder, + _FieldDescriptor.TYPE_BOOL: encoder.BoolEncoder, + _FieldDescriptor.TYPE_STRING: encoder.StringEncoder, + _FieldDescriptor.TYPE_GROUP: encoder.GroupEncoder, + _FieldDescriptor.TYPE_MESSAGE: encoder.MessageEncoder, + _FieldDescriptor.TYPE_BYTES: encoder.BytesEncoder, + _FieldDescriptor.TYPE_UINT32: encoder.UInt32Encoder, + _FieldDescriptor.TYPE_ENUM: encoder.EnumEncoder, + _FieldDescriptor.TYPE_SFIXED32: encoder.SFixed32Encoder, + _FieldDescriptor.TYPE_SFIXED64: encoder.SFixed64Encoder, + _FieldDescriptor.TYPE_SINT32: encoder.SInt32Encoder, + _FieldDescriptor.TYPE_SINT64: encoder.SInt64Encoder, + } + + +# Maps from field types to sizer constructors. +TYPE_TO_SIZER = { + _FieldDescriptor.TYPE_DOUBLE: encoder.DoubleSizer, + _FieldDescriptor.TYPE_FLOAT: encoder.FloatSizer, + _FieldDescriptor.TYPE_INT64: encoder.Int64Sizer, + _FieldDescriptor.TYPE_UINT64: encoder.UInt64Sizer, + _FieldDescriptor.TYPE_INT32: encoder.Int32Sizer, + _FieldDescriptor.TYPE_FIXED64: encoder.Fixed64Sizer, + _FieldDescriptor.TYPE_FIXED32: encoder.Fixed32Sizer, + _FieldDescriptor.TYPE_BOOL: encoder.BoolSizer, + _FieldDescriptor.TYPE_STRING: encoder.StringSizer, + _FieldDescriptor.TYPE_GROUP: encoder.GroupSizer, + _FieldDescriptor.TYPE_MESSAGE: encoder.MessageSizer, + _FieldDescriptor.TYPE_BYTES: encoder.BytesSizer, + _FieldDescriptor.TYPE_UINT32: encoder.UInt32Sizer, + _FieldDescriptor.TYPE_ENUM: encoder.EnumSizer, + _FieldDescriptor.TYPE_SFIXED32: encoder.SFixed32Sizer, + _FieldDescriptor.TYPE_SFIXED64: encoder.SFixed64Sizer, + _FieldDescriptor.TYPE_SINT32: encoder.SInt32Sizer, + _FieldDescriptor.TYPE_SINT64: encoder.SInt64Sizer, + } + + +# Maps from field type to a decoder constructor. +TYPE_TO_DECODER = { + _FieldDescriptor.TYPE_DOUBLE: decoder.DoubleDecoder, + _FieldDescriptor.TYPE_FLOAT: decoder.FloatDecoder, + _FieldDescriptor.TYPE_INT64: decoder.Int64Decoder, + _FieldDescriptor.TYPE_UINT64: decoder.UInt64Decoder, + _FieldDescriptor.TYPE_INT32: decoder.Int32Decoder, + _FieldDescriptor.TYPE_FIXED64: decoder.Fixed64Decoder, + _FieldDescriptor.TYPE_FIXED32: decoder.Fixed32Decoder, + _FieldDescriptor.TYPE_BOOL: decoder.BoolDecoder, + _FieldDescriptor.TYPE_STRING: decoder.StringDecoder, + _FieldDescriptor.TYPE_GROUP: decoder.GroupDecoder, + _FieldDescriptor.TYPE_MESSAGE: decoder.MessageDecoder, + _FieldDescriptor.TYPE_BYTES: decoder.BytesDecoder, + _FieldDescriptor.TYPE_UINT32: decoder.UInt32Decoder, + _FieldDescriptor.TYPE_ENUM: decoder.EnumDecoder, + _FieldDescriptor.TYPE_SFIXED32: decoder.SFixed32Decoder, + _FieldDescriptor.TYPE_SFIXED64: decoder.SFixed64Decoder, + _FieldDescriptor.TYPE_SINT32: decoder.SInt32Decoder, + _FieldDescriptor.TYPE_SINT64: decoder.SInt64Decoder, + } + +# Maps from field type to expected wiretype. +FIELD_TYPE_TO_WIRE_TYPE = { + _FieldDescriptor.TYPE_DOUBLE: wire_format.WIRETYPE_FIXED64, + _FieldDescriptor.TYPE_FLOAT: wire_format.WIRETYPE_FIXED32, + _FieldDescriptor.TYPE_INT64: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_UINT64: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_INT32: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_FIXED64: wire_format.WIRETYPE_FIXED64, + _FieldDescriptor.TYPE_FIXED32: wire_format.WIRETYPE_FIXED32, + _FieldDescriptor.TYPE_BOOL: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_STRING: + wire_format.WIRETYPE_LENGTH_DELIMITED, + _FieldDescriptor.TYPE_GROUP: wire_format.WIRETYPE_START_GROUP, + _FieldDescriptor.TYPE_MESSAGE: + wire_format.WIRETYPE_LENGTH_DELIMITED, + _FieldDescriptor.TYPE_BYTES: + wire_format.WIRETYPE_LENGTH_DELIMITED, + _FieldDescriptor.TYPE_UINT32: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_ENUM: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_SFIXED32: wire_format.WIRETYPE_FIXED32, + _FieldDescriptor.TYPE_SFIXED64: wire_format.WIRETYPE_FIXED64, + _FieldDescriptor.TYPE_SINT32: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_SINT64: wire_format.WIRETYPE_VARINT, + } diff --git a/virt/lib/python3.9/site-packages/google/protobuf/internal/unknown_fields_test 3.py b/virt/lib/python3.9/site-packages/google/protobuf/internal/unknown_fields_test 3.py new file mode 100644 index 00000000..c06370ee --- /dev/null +++ b/virt/lib/python3.9/site-packages/google/protobuf/internal/unknown_fields_test 3.py @@ -0,0 +1,461 @@ +# -*- coding: utf-8 -*- +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# 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 Google 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 +# OWNER 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. + +"""Test for preservation of unknown fields in the pure Python implementation.""" + +__author__ = 'bohdank@google.com (Bohdan Koval)' + +import sys +import unittest + +from google.protobuf import map_unittest_pb2 +from google.protobuf import unittest_mset_pb2 +from google.protobuf import unittest_pb2 +from google.protobuf import unittest_proto3_arena_pb2 +from google.protobuf.internal import api_implementation +from google.protobuf.internal import encoder +from google.protobuf.internal import message_set_extensions_pb2 +from google.protobuf.internal import missing_enum_values_pb2 +from google.protobuf.internal import test_util +from google.protobuf.internal import testing_refleaks +from google.protobuf.internal import type_checkers +from google.protobuf.internal import wire_format +from google.protobuf import descriptor +from google.protobuf import unknown_fields +try: + import tracemalloc # pylint: disable=g-import-not-at-top +except ImportError: + # Requires python 3.4+ + pass + + +@testing_refleaks.TestCase +class UnknownFieldsTest(unittest.TestCase): + + def setUp(self): + self.descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR + self.all_fields = unittest_pb2.TestAllTypes() + test_util.SetAllFields(self.all_fields) + self.all_fields_data = self.all_fields.SerializeToString() + self.empty_message = unittest_pb2.TestEmptyMessage() + self.empty_message.ParseFromString(self.all_fields_data) + + def testSerialize(self): + data = self.empty_message.SerializeToString() + + # Don't use assertEqual because we don't want to dump raw binary data to + # stdout. + self.assertTrue(data == self.all_fields_data) + + def testSerializeProto3(self): + # Verify proto3 unknown fields behavior. + message = unittest_proto3_arena_pb2.TestEmptyMessage() + message.ParseFromString(self.all_fields_data) + self.assertEqual(self.all_fields_data, message.SerializeToString()) + + def testByteSize(self): + self.assertEqual(self.all_fields.ByteSize(), self.empty_message.ByteSize()) + + def testListFields(self): + # Make sure ListFields doesn't return unknown fields. + self.assertEqual(0, len(self.empty_message.ListFields())) + + def testSerializeMessageSetWireFormatUnknownExtension(self): + # Create a message using the message set wire format with an unknown + # message. + raw = unittest_mset_pb2.RawMessageSet() + + # Add an unknown extension. + item = raw.item.add() + item.type_id = 98218603 + message1 = message_set_extensions_pb2.TestMessageSetExtension1() + message1.i = 12345 + item.message = message1.SerializeToString() + + serialized = raw.SerializeToString() + + # Parse message using the message set wire format. + proto = message_set_extensions_pb2.TestMessageSet() + proto.MergeFromString(serialized) + + unknown_field_set = unknown_fields.UnknownFieldSet(proto) + self.assertEqual(len(unknown_field_set), 1) + # Unknown field should have wire format data which can be parsed back to + # original message. + self.assertEqual(unknown_field_set[0].field_number, item.type_id) + self.assertEqual(unknown_field_set[0].wire_type, + wire_format.WIRETYPE_LENGTH_DELIMITED) + d = unknown_field_set[0].data + message_new = message_set_extensions_pb2.TestMessageSetExtension1() + message_new.ParseFromString(d) + self.assertEqual(message1, message_new) + + # Verify that the unknown extension is serialized unchanged + reserialized = proto.SerializeToString() + new_raw = unittest_mset_pb2.RawMessageSet() + new_raw.MergeFromString(reserialized) + self.assertEqual(raw, new_raw) + + def testEquals(self): + message = unittest_pb2.TestEmptyMessage() + message.ParseFromString(self.all_fields_data) + self.assertEqual(self.empty_message, message) + + self.all_fields.ClearField('optional_string') + message.ParseFromString(self.all_fields.SerializeToString()) + self.assertNotEqual(self.empty_message, message) + + def testDiscardUnknownFields(self): + self.empty_message.DiscardUnknownFields() + self.assertEqual(b'', self.empty_message.SerializeToString()) + # Test message field and repeated message field. + message = unittest_pb2.TestAllTypes() + other_message = unittest_pb2.TestAllTypes() + other_message.optional_string = 'discard' + message.optional_nested_message.ParseFromString( + other_message.SerializeToString()) + message.repeated_nested_message.add().ParseFromString( + other_message.SerializeToString()) + self.assertNotEqual( + b'', message.optional_nested_message.SerializeToString()) + self.assertNotEqual( + b'', message.repeated_nested_message[0].SerializeToString()) + message.DiscardUnknownFields() + self.assertEqual(b'', message.optional_nested_message.SerializeToString()) + self.assertEqual( + b'', message.repeated_nested_message[0].SerializeToString()) + + msg = map_unittest_pb2.TestMap() + msg.map_int32_all_types[1].optional_nested_message.ParseFromString( + other_message.SerializeToString()) + msg.map_string_string['1'] = 'test' + self.assertNotEqual( + b'', + msg.map_int32_all_types[1].optional_nested_message.SerializeToString()) + msg.DiscardUnknownFields() + self.assertEqual( + b'', + msg.map_int32_all_types[1].optional_nested_message.SerializeToString()) + + +@testing_refleaks.TestCase +class UnknownFieldsAccessorsTest(unittest.TestCase): + + def setUp(self): + self.descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR + self.all_fields = unittest_pb2.TestAllTypes() + test_util.SetAllFields(self.all_fields) + self.all_fields_data = self.all_fields.SerializeToString() + self.empty_message = unittest_pb2.TestEmptyMessage() + self.empty_message.ParseFromString(self.all_fields_data) + + # InternalCheckUnknownField() is an additional Pure Python check which checks + # a detail of unknown fields. It cannot be used by the C++ + # implementation because some protect members are called. + # The test is added for historical reasons. It is not necessary as + # serialized string is checked. + # TODO(jieluo): Remove message._unknown_fields. + def InternalCheckUnknownField(self, name, expected_value): + if api_implementation.Type() != 'python': + return + field_descriptor = self.descriptor.fields_by_name[name] + wire_type = type_checkers.FIELD_TYPE_TO_WIRE_TYPE[field_descriptor.type] + field_tag = encoder.TagBytes(field_descriptor.number, wire_type) + result_dict = {} + for tag_bytes, value in self.empty_message._unknown_fields: + if tag_bytes == field_tag: + decoder = unittest_pb2.TestAllTypes._decoders_by_tag[tag_bytes][0] + decoder(memoryview(value), 0, len(value), self.all_fields, result_dict) + self.assertEqual(expected_value, result_dict[field_descriptor]) + + def CheckUnknownField(self, name, unknown_field_set, expected_value): + field_descriptor = self.descriptor.fields_by_name[name] + expected_type = type_checkers.FIELD_TYPE_TO_WIRE_TYPE[ + field_descriptor.type] + for unknown_field in unknown_field_set: + if unknown_field.field_number == field_descriptor.number: + self.assertEqual(expected_type, unknown_field.wire_type) + if expected_type == 3: + # Check group + self.assertEqual(expected_value[0], + unknown_field.data[0].field_number) + self.assertEqual(expected_value[1], unknown_field.data[0].wire_type) + self.assertEqual(expected_value[2], unknown_field.data[0].data) + continue + if expected_type == wire_format.WIRETYPE_LENGTH_DELIMITED: + self.assertIn(type(unknown_field.data), (str, bytes)) + if field_descriptor.label == descriptor.FieldDescriptor.LABEL_REPEATED: + self.assertIn(unknown_field.data, expected_value) + else: + self.assertEqual(expected_value, unknown_field.data) + + def testCheckUnknownFieldValue(self): + unknown_field_set = unknown_fields.UnknownFieldSet(self.empty_message) + # Test enum. + self.CheckUnknownField('optional_nested_enum', + unknown_field_set, + self.all_fields.optional_nested_enum) + self.InternalCheckUnknownField('optional_nested_enum', + self.all_fields.optional_nested_enum) + + # Test repeated enum. + self.CheckUnknownField('repeated_nested_enum', + unknown_field_set, + self.all_fields.repeated_nested_enum) + self.InternalCheckUnknownField('repeated_nested_enum', + self.all_fields.repeated_nested_enum) + + # Test varint. + self.CheckUnknownField('optional_int32', + unknown_field_set, + self.all_fields.optional_int32) + self.InternalCheckUnknownField('optional_int32', + self.all_fields.optional_int32) + + # Test fixed32. + self.CheckUnknownField('optional_fixed32', + unknown_field_set, + self.all_fields.optional_fixed32) + self.InternalCheckUnknownField('optional_fixed32', + self.all_fields.optional_fixed32) + + # Test fixed64. + self.CheckUnknownField('optional_fixed64', + unknown_field_set, + self.all_fields.optional_fixed64) + self.InternalCheckUnknownField('optional_fixed64', + self.all_fields.optional_fixed64) + + # Test length delimited. + self.CheckUnknownField('optional_string', + unknown_field_set, + self.all_fields.optional_string.encode('utf-8')) + self.InternalCheckUnknownField('optional_string', + self.all_fields.optional_string) + + # Test group. + self.CheckUnknownField('optionalgroup', + unknown_field_set, + (17, 0, 117)) + self.InternalCheckUnknownField('optionalgroup', + self.all_fields.optionalgroup) + + self.assertEqual(98, len(unknown_field_set)) + + def testCopyFrom(self): + message = unittest_pb2.TestEmptyMessage() + message.CopyFrom(self.empty_message) + self.assertEqual(message.SerializeToString(), self.all_fields_data) + + def testMergeFrom(self): + message = unittest_pb2.TestAllTypes() + message.optional_int32 = 1 + message.optional_uint32 = 2 + source = unittest_pb2.TestEmptyMessage() + source.ParseFromString(message.SerializeToString()) + + message.ClearField('optional_int32') + message.optional_int64 = 3 + message.optional_uint32 = 4 + destination = unittest_pb2.TestEmptyMessage() + unknown_field_set = unknown_fields.UnknownFieldSet(destination) + self.assertEqual(0, len(unknown_field_set)) + destination.ParseFromString(message.SerializeToString()) + self.assertEqual(0, len(unknown_field_set)) + unknown_field_set = unknown_fields.UnknownFieldSet(destination) + self.assertEqual(2, len(unknown_field_set)) + destination.MergeFrom(source) + self.assertEqual(2, len(unknown_field_set)) + # Check that the fields where correctly merged, even stored in the unknown + # fields set. + message.ParseFromString(destination.SerializeToString()) + self.assertEqual(message.optional_int32, 1) + self.assertEqual(message.optional_uint32, 2) + self.assertEqual(message.optional_int64, 3) + + def testClear(self): + unknown_field_set = unknown_fields.UnknownFieldSet(self.empty_message) + self.empty_message.Clear() + # All cleared, even unknown fields. + self.assertEqual(self.empty_message.SerializeToString(), b'') + self.assertEqual(len(unknown_field_set), 98) + + @unittest.skipIf((sys.version_info.major, sys.version_info.minor) < (3, 4), + 'tracemalloc requires python 3.4+') + def testUnknownFieldsNoMemoryLeak(self): + # Call to UnknownFields must not leak memory + nb_leaks = 1234 + + def leaking_function(): + for _ in range(nb_leaks): + unknown_fields.UnknownFieldSet(self.empty_message) + + tracemalloc.start() + snapshot1 = tracemalloc.take_snapshot() + leaking_function() + snapshot2 = tracemalloc.take_snapshot() + top_stats = snapshot2.compare_to(snapshot1, 'lineno') + tracemalloc.stop() + # There's no easy way to look for a precise leak source. + # Rely on a "marker" count value while checking allocated memory. + self.assertEqual([], [x for x in top_stats if x.count_diff == nb_leaks]) + + def testSubUnknownFields(self): + message = unittest_pb2.TestAllTypes() + message.optionalgroup.a = 123 + destination = unittest_pb2.TestEmptyMessage() + destination.ParseFromString(message.SerializeToString()) + sub_unknown_fields = unknown_fields.UnknownFieldSet(destination)[0].data + self.assertEqual(1, len(sub_unknown_fields)) + self.assertEqual(sub_unknown_fields[0].data, 123) + destination.Clear() + self.assertEqual(1, len(sub_unknown_fields)) + self.assertEqual(sub_unknown_fields[0].data, 123) + message.Clear() + message.optional_uint32 = 456 + nested_message = unittest_pb2.NestedTestAllTypes() + nested_message.payload.optional_nested_message.ParseFromString( + message.SerializeToString()) + unknown_field_set = unknown_fields.UnknownFieldSet( + nested_message.payload.optional_nested_message) + self.assertEqual(unknown_field_set[0].data, 456) + nested_message.ClearField('payload') + self.assertEqual(unknown_field_set[0].data, 456) + unknown_field_set = unknown_fields.UnknownFieldSet( + nested_message.payload.optional_nested_message) + self.assertEqual(0, len(unknown_field_set)) + + def testUnknownField(self): + message = unittest_pb2.TestAllTypes() + message.optional_int32 = 123 + destination = unittest_pb2.TestEmptyMessage() + destination.ParseFromString(message.SerializeToString()) + unknown_field = unknown_fields.UnknownFieldSet(destination)[0] + destination.Clear() + self.assertEqual(unknown_field.data, 123) + + def testUnknownExtensions(self): + message = unittest_pb2.TestEmptyMessageWithExtensions() + message.ParseFromString(self.all_fields_data) + self.assertEqual(len(unknown_fields.UnknownFieldSet(message)), 98) + self.assertEqual(message.SerializeToString(), self.all_fields_data) + + +@testing_refleaks.TestCase +class UnknownEnumValuesTest(unittest.TestCase): + + def setUp(self): + self.descriptor = missing_enum_values_pb2.TestEnumValues.DESCRIPTOR + + self.message = missing_enum_values_pb2.TestEnumValues() + # TestEnumValues.ZERO = 0, but does not exist in the other NestedEnum. + self.message.optional_nested_enum = ( + missing_enum_values_pb2.TestEnumValues.ZERO) + self.message.repeated_nested_enum.extend([ + missing_enum_values_pb2.TestEnumValues.ZERO, + missing_enum_values_pb2.TestEnumValues.ONE, + ]) + self.message.packed_nested_enum.extend([ + missing_enum_values_pb2.TestEnumValues.ZERO, + missing_enum_values_pb2.TestEnumValues.ONE, + ]) + self.message_data = self.message.SerializeToString() + self.missing_message = missing_enum_values_pb2.TestMissingEnumValues() + self.missing_message.ParseFromString(self.message_data) + + # CheckUnknownField() is an additional Pure Python check which checks + # a detail of unknown fields. It cannot be used by the C++ + # implementation because some protect members are called. + # The test is added for historical reasons. It is not necessary as + # serialized string is checked. + + def CheckUnknownField(self, name, expected_value): + field_descriptor = self.descriptor.fields_by_name[name] + unknown_field_set = unknown_fields.UnknownFieldSet(self.missing_message) + self.assertIsInstance(unknown_field_set, unknown_fields.UnknownFieldSet) + count = 0 + for field in unknown_field_set: + if field.field_number == field_descriptor.number: + count += 1 + if field_descriptor.label == descriptor.FieldDescriptor.LABEL_REPEATED: + self.assertIn(field.data, expected_value) + else: + self.assertEqual(expected_value, field.data) + if field_descriptor.label == descriptor.FieldDescriptor.LABEL_REPEATED: + self.assertEqual(count, len(expected_value)) + else: + self.assertEqual(count, 1) + + def testUnknownParseMismatchEnumValue(self): + just_string = missing_enum_values_pb2.JustString() + just_string.dummy = 'blah' + + missing = missing_enum_values_pb2.TestEnumValues() + # The parse is invalid, storing the string proto into the set of + # unknown fields. + missing.ParseFromString(just_string.SerializeToString()) + + # Fetching the enum field shouldn't crash, instead returning the + # default value. + self.assertEqual(missing.optional_nested_enum, 0) + + def testUnknownEnumValue(self): + self.assertFalse(self.missing_message.HasField('optional_nested_enum')) + self.assertEqual(self.missing_message.optional_nested_enum, 2) + # Clear does not do anything. + serialized = self.missing_message.SerializeToString() + self.missing_message.ClearField('optional_nested_enum') + self.assertEqual(self.missing_message.SerializeToString(), serialized) + + def testUnknownRepeatedEnumValue(self): + self.assertEqual([], self.missing_message.repeated_nested_enum) + + def testUnknownPackedEnumValue(self): + self.assertEqual([], self.missing_message.packed_nested_enum) + + def testCheckUnknownFieldValueForEnum(self): + unknown_field_set = unknown_fields.UnknownFieldSet(self.missing_message) + self.assertEqual(len(unknown_field_set), 5) + self.CheckUnknownField('optional_nested_enum', + self.message.optional_nested_enum) + self.CheckUnknownField('repeated_nested_enum', + self.message.repeated_nested_enum) + self.CheckUnknownField('packed_nested_enum', + self.message.packed_nested_enum) + + def testRoundTrip(self): + new_message = missing_enum_values_pb2.TestEnumValues() + new_message.ParseFromString(self.missing_message.SerializeToString()) + self.assertEqual(self.message, new_message) + + +if __name__ == '__main__': + unittest.main() diff --git a/virt/lib/python3.9/site-packages/google/protobuf/internal/well_known_types_test 3.py b/virt/lib/python3.9/site-packages/google/protobuf/internal/well_known_types_test 3.py new file mode 100644 index 00000000..aba75d3a --- /dev/null +++ b/virt/lib/python3.9/site-packages/google/protobuf/internal/well_known_types_test 3.py @@ -0,0 +1,1013 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# 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 Google 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 +# OWNER 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. + +"""Test for google.protobuf.internal.well_known_types.""" + +__author__ = 'jieluo@google.com (Jie Luo)' + +import collections.abc as collections_abc +import datetime +import unittest + +from google.protobuf import any_pb2 +from google.protobuf import duration_pb2 +from google.protobuf import field_mask_pb2 +from google.protobuf import struct_pb2 +from google.protobuf import timestamp_pb2 +from google.protobuf import map_unittest_pb2 +from google.protobuf import unittest_pb2 +from google.protobuf.internal import any_test_pb2 +from google.protobuf.internal import test_util +from google.protobuf.internal import well_known_types +from google.protobuf import descriptor +from google.protobuf import text_format +from google.protobuf.internal import _parameterized + +try: + # New module in Python 3.9: + import zoneinfo # pylint:disable=g-import-not-at-top + _TZ_JAPAN = zoneinfo.ZoneInfo('Japan') + _TZ_PACIFIC = zoneinfo.ZoneInfo('US/Pacific') +except ImportError: + _TZ_JAPAN = datetime.timezone(datetime.timedelta(hours=9), 'Japan') + _TZ_PACIFIC = datetime.timezone(datetime.timedelta(hours=-8), 'US/Pacific') + + +class TimeUtilTestBase(_parameterized.TestCase): + + def CheckTimestampConversion(self, message, text): + self.assertEqual(text, message.ToJsonString()) + parsed_message = timestamp_pb2.Timestamp() + parsed_message.FromJsonString(text) + self.assertEqual(message, parsed_message) + + def CheckDurationConversion(self, message, text): + self.assertEqual(text, message.ToJsonString()) + parsed_message = duration_pb2.Duration() + parsed_message.FromJsonString(text) + self.assertEqual(message, parsed_message) + + +class TimeUtilTest(TimeUtilTestBase): + + def testTimestampSerializeAndParse(self): + message = timestamp_pb2.Timestamp() + # Generated output should contain 3, 6, or 9 fractional digits. + message.seconds = 0 + message.nanos = 0 + self.CheckTimestampConversion(message, '1970-01-01T00:00:00Z') + message.nanos = 10000000 + self.CheckTimestampConversion(message, '1970-01-01T00:00:00.010Z') + message.nanos = 10000 + self.CheckTimestampConversion(message, '1970-01-01T00:00:00.000010Z') + message.nanos = 10 + self.CheckTimestampConversion(message, '1970-01-01T00:00:00.000000010Z') + # Test min timestamps. + message.seconds = -62135596800 + message.nanos = 0 + self.CheckTimestampConversion(message, '0001-01-01T00:00:00Z') + # Test max timestamps. + message.seconds = 253402300799 + message.nanos = 999999999 + self.CheckTimestampConversion(message, '9999-12-31T23:59:59.999999999Z') + # Test negative timestamps. + message.seconds = -1 + self.CheckTimestampConversion(message, '1969-12-31T23:59:59.999999999Z') + + # Parsing accepts an fractional digits as long as they fit into nano + # precision. + message.FromJsonString('1970-01-01T00:00:00.1Z') + self.assertEqual(0, message.seconds) + self.assertEqual(100000000, message.nanos) + # Parsing accepts offsets. + message.FromJsonString('1970-01-01T00:00:00-08:00') + self.assertEqual(8 * 3600, message.seconds) + self.assertEqual(0, message.nanos) + + # It is not easy to check with current time. For test coverage only. + message.GetCurrentTime() + self.assertNotEqual(8 * 3600, message.seconds) + + def testDurationSerializeAndParse(self): + message = duration_pb2.Duration() + # Generated output should contain 3, 6, or 9 fractional digits. + message.seconds = 0 + message.nanos = 0 + self.CheckDurationConversion(message, '0s') + message.nanos = 10000000 + self.CheckDurationConversion(message, '0.010s') + message.nanos = 10000 + self.CheckDurationConversion(message, '0.000010s') + message.nanos = 10 + self.CheckDurationConversion(message, '0.000000010s') + + # Test min and max + message.seconds = 315576000000 + message.nanos = 999999999 + self.CheckDurationConversion(message, '315576000000.999999999s') + message.seconds = -315576000000 + message.nanos = -999999999 + self.CheckDurationConversion(message, '-315576000000.999999999s') + + # Parsing accepts an fractional digits as long as they fit into nano + # precision. + message.FromJsonString('0.1s') + self.assertEqual(100000000, message.nanos) + message.FromJsonString('0.0000001s') + self.assertEqual(100, message.nanos) + + def testTimestampIntegerConversion(self): + message = timestamp_pb2.Timestamp() + message.FromNanoseconds(1) + self.assertEqual('1970-01-01T00:00:00.000000001Z', + message.ToJsonString()) + self.assertEqual(1, message.ToNanoseconds()) + + message.FromNanoseconds(-1) + self.assertEqual('1969-12-31T23:59:59.999999999Z', + message.ToJsonString()) + self.assertEqual(-1, message.ToNanoseconds()) + + message.FromMicroseconds(1) + self.assertEqual('1970-01-01T00:00:00.000001Z', + message.ToJsonString()) + self.assertEqual(1, message.ToMicroseconds()) + + message.FromMicroseconds(-1) + self.assertEqual('1969-12-31T23:59:59.999999Z', + message.ToJsonString()) + self.assertEqual(-1, message.ToMicroseconds()) + + message.FromMilliseconds(1) + self.assertEqual('1970-01-01T00:00:00.001Z', + message.ToJsonString()) + self.assertEqual(1, message.ToMilliseconds()) + + message.FromMilliseconds(-1) + self.assertEqual('1969-12-31T23:59:59.999Z', + message.ToJsonString()) + self.assertEqual(-1, message.ToMilliseconds()) + + message.FromSeconds(1) + self.assertEqual('1970-01-01T00:00:01Z', + message.ToJsonString()) + self.assertEqual(1, message.ToSeconds()) + + message.FromSeconds(-1) + self.assertEqual('1969-12-31T23:59:59Z', + message.ToJsonString()) + self.assertEqual(-1, message.ToSeconds()) + + message.FromNanoseconds(1999) + self.assertEqual(1, message.ToMicroseconds()) + # For negative values, Timestamp will be rounded down. + # For example, "1969-12-31T23:59:59.5Z" (i.e., -0.5s) rounded to seconds + # will be "1969-12-31T23:59:59Z" (i.e., -1s) rather than + # "1970-01-01T00:00:00Z" (i.e., 0s). + message.FromNanoseconds(-1999) + self.assertEqual(-2, message.ToMicroseconds()) + + def testDurationIntegerConversion(self): + message = duration_pb2.Duration() + message.FromNanoseconds(1) + self.assertEqual('0.000000001s', + message.ToJsonString()) + self.assertEqual(1, message.ToNanoseconds()) + + message.FromNanoseconds(-1) + self.assertEqual('-0.000000001s', + message.ToJsonString()) + self.assertEqual(-1, message.ToNanoseconds()) + + message.FromMicroseconds(1) + self.assertEqual('0.000001s', + message.ToJsonString()) + self.assertEqual(1, message.ToMicroseconds()) + + message.FromMicroseconds(-1) + self.assertEqual('-0.000001s', + message.ToJsonString()) + self.assertEqual(-1, message.ToMicroseconds()) + + message.FromMilliseconds(1) + self.assertEqual('0.001s', + message.ToJsonString()) + self.assertEqual(1, message.ToMilliseconds()) + + message.FromMilliseconds(-1) + self.assertEqual('-0.001s', + message.ToJsonString()) + self.assertEqual(-1, message.ToMilliseconds()) + + message.FromSeconds(1) + self.assertEqual('1s', message.ToJsonString()) + self.assertEqual(1, message.ToSeconds()) + + message.FromSeconds(-1) + self.assertEqual('-1s', + message.ToJsonString()) + self.assertEqual(-1, message.ToSeconds()) + + # Test truncation behavior. + message.FromNanoseconds(1999) + self.assertEqual(1, message.ToMicroseconds()) + + # For negative values, Duration will be rounded towards 0. + message.FromNanoseconds(-1999) + self.assertEqual(-1, message.ToMicroseconds()) + + def testTimezoneNaiveDatetimeConversion(self): + message = timestamp_pb2.Timestamp() + naive_utc_epoch = datetime.datetime(1970, 1, 1) + message.FromDatetime(naive_utc_epoch) + self.assertEqual(0, message.seconds) + self.assertEqual(0, message.nanos) + + self.assertEqual(naive_utc_epoch, message.ToDatetime()) + + naive_epoch_morning = datetime.datetime(1970, 1, 1, 8, 0, 0, 1) + message.FromDatetime(naive_epoch_morning) + self.assertEqual(8 * 3600, message.seconds) + self.assertEqual(1000, message.nanos) + + self.assertEqual(naive_epoch_morning, message.ToDatetime()) + + message.FromMilliseconds(1999) + self.assertEqual(1, message.seconds) + self.assertEqual(999_000_000, message.nanos) + + self.assertEqual(datetime.datetime(1970, 1, 1, 0, 0, 1, 999000), + message.ToDatetime()) + + naive_future = datetime.datetime(2555, 2, 22, 1, 2, 3, 456789) + message.FromDatetime(naive_future) + self.assertEqual(naive_future, message.ToDatetime()) + + naive_end_of_time = datetime.datetime.max + message.FromDatetime(naive_end_of_time) + self.assertEqual(naive_end_of_time, message.ToDatetime()) + + # Two hours after the Unix Epoch, around the world. + @_parameterized.named_parameters( + ('London', [1970, 1, 1, 2], datetime.timezone.utc), + ('Tokyo', [1970, 1, 1, 11], _TZ_JAPAN), + ('LA', [1969, 12, 31, 18], _TZ_PACIFIC), + ) + def testTimezoneAwareDatetimeConversion(self, date_parts, tzinfo): + original_datetime = datetime.datetime(*date_parts, tzinfo=tzinfo) # pylint:disable=g-tzinfo-datetime + + message = timestamp_pb2.Timestamp() + message.FromDatetime(original_datetime) + self.assertEqual(7200, message.seconds) + self.assertEqual(0, message.nanos) + + # ToDatetime() with no parameters produces a naive UTC datetime, i.e. it not + # only loses the original timezone information (e.g. US/Pacific) as it's + # "normalised" to UTC, but also drops the information that the datetime + # represents a UTC one. + naive_datetime = message.ToDatetime() + self.assertEqual(datetime.datetime(1970, 1, 1, 2), naive_datetime) + self.assertIsNone(naive_datetime.tzinfo) + self.assertNotEqual(original_datetime, naive_datetime) # not even for UTC! + + # In contrast, ToDatetime(tzinfo=) produces an aware datetime in the given + # timezone. + aware_datetime = message.ToDatetime(tzinfo=tzinfo) + self.assertEqual(original_datetime, aware_datetime) + self.assertEqual( + datetime.datetime(1970, 1, 1, 2, tzinfo=datetime.timezone.utc), + aware_datetime) + self.assertEqual(tzinfo, aware_datetime.tzinfo) + + def testTimedeltaConversion(self): + message = duration_pb2.Duration() + message.FromNanoseconds(1999999999) + td = message.ToTimedelta() + self.assertEqual(1, td.seconds) + self.assertEqual(999999, td.microseconds) + + message.FromNanoseconds(-1999999999) + td = message.ToTimedelta() + self.assertEqual(-1, td.days) + self.assertEqual(86398, td.seconds) + self.assertEqual(1, td.microseconds) + + message.FromMicroseconds(-1) + td = message.ToTimedelta() + self.assertEqual(-1, td.days) + self.assertEqual(86399, td.seconds) + self.assertEqual(999999, td.microseconds) + converted_message = duration_pb2.Duration() + converted_message.FromTimedelta(td) + self.assertEqual(message, converted_message) + + def testInvalidTimestamp(self): + message = timestamp_pb2.Timestamp() + self.assertRaisesRegex( + ValueError, 'Failed to parse timestamp: missing valid timezone offset.', + message.FromJsonString, '') + self.assertRaisesRegex( + ValueError, 'Failed to parse timestamp: invalid trailing data ' + '1970-01-01T00:00:01Ztrail.', message.FromJsonString, + '1970-01-01T00:00:01Ztrail') + self.assertRaisesRegex( + ValueError, 'time data \'10000-01-01T00:00:00\' does not match' + ' format \'%Y-%m-%dT%H:%M:%S\'', message.FromJsonString, + '10000-01-01T00:00:00.00Z') + self.assertRaisesRegex( + ValueError, 'nanos 0123456789012 more than 9 fractional digits.', + message.FromJsonString, '1970-01-01T00:00:00.0123456789012Z') + self.assertRaisesRegex( + ValueError, + (r'Invalid timezone offset value: \+08.'), + message.FromJsonString, + '1972-01-01T01:00:00.01+08', + ) + self.assertRaisesRegex(ValueError, 'year (0 )?is out of range', + message.FromJsonString, '0000-01-01T00:00:00Z') + message.seconds = 253402300800 + self.assertRaisesRegex(OverflowError, 'date value out of range', + message.ToJsonString) + + def testInvalidDuration(self): + message = duration_pb2.Duration() + self.assertRaisesRegex(ValueError, 'Duration must end with letter "s": 1.', + message.FromJsonString, '1') + self.assertRaisesRegex(ValueError, 'Couldn\'t parse duration: 1...2s.', + message.FromJsonString, '1...2s') + text = '-315576000001.000000000s' + self.assertRaisesRegex( + ValueError, + r'Duration is not valid\: Seconds -315576000001 must be in range' + r' \[-315576000000\, 315576000000\].', message.FromJsonString, text) + text = '315576000001.000000000s' + self.assertRaisesRegex( + ValueError, + r'Duration is not valid\: Seconds 315576000001 must be in range' + r' \[-315576000000\, 315576000000\].', message.FromJsonString, text) + message.seconds = -315576000001 + message.nanos = 0 + self.assertRaisesRegex( + ValueError, + r'Duration is not valid\: Seconds -315576000001 must be in range' + r' \[-315576000000\, 315576000000\].', message.ToJsonString) + message.seconds = 0 + message.nanos = 999999999 + 1 + self.assertRaisesRegex( + ValueError, r'Duration is not valid\: Nanos 1000000000 must be in range' + r' \[-999999999\, 999999999\].', message.ToJsonString) + message.seconds = -1 + message.nanos = 1 + self.assertRaisesRegex(ValueError, + r'Duration is not valid\: Sign mismatch.', + message.ToJsonString) + + +class FieldMaskTest(unittest.TestCase): + + def testStringFormat(self): + mask = field_mask_pb2.FieldMask() + self.assertEqual('', mask.ToJsonString()) + mask.paths.append('foo') + self.assertEqual('foo', mask.ToJsonString()) + mask.paths.append('bar') + self.assertEqual('foo,bar', mask.ToJsonString()) + + mask.FromJsonString('') + self.assertEqual('', mask.ToJsonString()) + mask.FromJsonString('foo') + self.assertEqual(['foo'], mask.paths) + mask.FromJsonString('foo,bar') + self.assertEqual(['foo', 'bar'], mask.paths) + + # Test camel case + mask.Clear() + mask.paths.append('foo_bar') + self.assertEqual('fooBar', mask.ToJsonString()) + mask.paths.append('bar_quz') + self.assertEqual('fooBar,barQuz', mask.ToJsonString()) + + mask.FromJsonString('') + self.assertEqual('', mask.ToJsonString()) + self.assertEqual([], mask.paths) + mask.FromJsonString('fooBar') + self.assertEqual(['foo_bar'], mask.paths) + mask.FromJsonString('fooBar,barQuz') + self.assertEqual(['foo_bar', 'bar_quz'], mask.paths) + + def testDescriptorToFieldMask(self): + mask = field_mask_pb2.FieldMask() + msg_descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR + mask.AllFieldsFromDescriptor(msg_descriptor) + self.assertEqual(76, len(mask.paths)) + self.assertTrue(mask.IsValidForDescriptor(msg_descriptor)) + for field in msg_descriptor.fields: + self.assertTrue(field.name in mask.paths) + + def testIsValidForDescriptor(self): + msg_descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR + # Empty mask + mask = field_mask_pb2.FieldMask() + self.assertTrue(mask.IsValidForDescriptor(msg_descriptor)) + # All fields from descriptor + mask.AllFieldsFromDescriptor(msg_descriptor) + self.assertTrue(mask.IsValidForDescriptor(msg_descriptor)) + # Child under optional message + mask.paths.append('optional_nested_message.bb') + self.assertTrue(mask.IsValidForDescriptor(msg_descriptor)) + # Repeated field is only allowed in the last position of path + mask.paths.append('repeated_nested_message.bb') + self.assertFalse(mask.IsValidForDescriptor(msg_descriptor)) + # Invalid top level field + mask = field_mask_pb2.FieldMask() + mask.paths.append('xxx') + self.assertFalse(mask.IsValidForDescriptor(msg_descriptor)) + # Invalid field in root + mask = field_mask_pb2.FieldMask() + mask.paths.append('xxx.zzz') + self.assertFalse(mask.IsValidForDescriptor(msg_descriptor)) + # Invalid field in internal node + mask = field_mask_pb2.FieldMask() + mask.paths.append('optional_nested_message.xxx.zzz') + self.assertFalse(mask.IsValidForDescriptor(msg_descriptor)) + # Invalid field in leaf + mask = field_mask_pb2.FieldMask() + mask.paths.append('optional_nested_message.xxx') + self.assertFalse(mask.IsValidForDescriptor(msg_descriptor)) + + def testCanonicalFrom(self): + mask = field_mask_pb2.FieldMask() + out_mask = field_mask_pb2.FieldMask() + # Paths will be sorted. + mask.FromJsonString('baz.quz,bar,foo') + out_mask.CanonicalFormFromMask(mask) + self.assertEqual('bar,baz.quz,foo', out_mask.ToJsonString()) + # Duplicated paths will be removed. + mask.FromJsonString('foo,bar,foo') + out_mask.CanonicalFormFromMask(mask) + self.assertEqual('bar,foo', out_mask.ToJsonString()) + # Sub-paths of other paths will be removed. + mask.FromJsonString('foo.b1,bar.b1,foo.b2,bar') + out_mask.CanonicalFormFromMask(mask) + self.assertEqual('bar,foo.b1,foo.b2', out_mask.ToJsonString()) + + # Test more deeply nested cases. + mask.FromJsonString( + 'foo.bar.baz1,foo.bar.baz2.quz,foo.bar.baz2') + out_mask.CanonicalFormFromMask(mask) + self.assertEqual('foo.bar.baz1,foo.bar.baz2', + out_mask.ToJsonString()) + mask.FromJsonString( + 'foo.bar.baz1,foo.bar.baz2,foo.bar.baz2.quz') + out_mask.CanonicalFormFromMask(mask) + self.assertEqual('foo.bar.baz1,foo.bar.baz2', + out_mask.ToJsonString()) + mask.FromJsonString( + 'foo.bar.baz1,foo.bar.baz2,foo.bar.baz2.quz,foo.bar') + out_mask.CanonicalFormFromMask(mask) + self.assertEqual('foo.bar', out_mask.ToJsonString()) + mask.FromJsonString( + 'foo.bar.baz1,foo.bar.baz2,foo.bar.baz2.quz,foo') + out_mask.CanonicalFormFromMask(mask) + self.assertEqual('foo', out_mask.ToJsonString()) + + def testUnion(self): + mask1 = field_mask_pb2.FieldMask() + mask2 = field_mask_pb2.FieldMask() + out_mask = field_mask_pb2.FieldMask() + mask1.FromJsonString('foo,baz') + mask2.FromJsonString('bar,quz') + out_mask.Union(mask1, mask2) + self.assertEqual('bar,baz,foo,quz', out_mask.ToJsonString()) + # Overlap with duplicated paths. + mask1.FromJsonString('foo,baz.bb') + mask2.FromJsonString('baz.bb,quz') + out_mask.Union(mask1, mask2) + self.assertEqual('baz.bb,foo,quz', out_mask.ToJsonString()) + # Overlap with paths covering some other paths. + mask1.FromJsonString('foo.bar.baz,quz') + mask2.FromJsonString('foo.bar,bar') + out_mask.Union(mask1, mask2) + self.assertEqual('bar,foo.bar,quz', out_mask.ToJsonString()) + src = unittest_pb2.TestAllTypes() + with self.assertRaises(ValueError): + out_mask.Union(src, mask2) + + def testIntersect(self): + mask1 = field_mask_pb2.FieldMask() + mask2 = field_mask_pb2.FieldMask() + out_mask = field_mask_pb2.FieldMask() + # Test cases without overlapping. + mask1.FromJsonString('foo,baz') + mask2.FromJsonString('bar,quz') + out_mask.Intersect(mask1, mask2) + self.assertEqual('', out_mask.ToJsonString()) + self.assertEqual(len(out_mask.paths), 0) + self.assertEqual(out_mask.paths, []) + # Overlap with duplicated paths. + mask1.FromJsonString('foo,baz.bb') + mask2.FromJsonString('baz.bb,quz') + out_mask.Intersect(mask1, mask2) + self.assertEqual('baz.bb', out_mask.ToJsonString()) + # Overlap with paths covering some other paths. + mask1.FromJsonString('foo.bar.baz,quz') + mask2.FromJsonString('foo.bar,bar') + out_mask.Intersect(mask1, mask2) + self.assertEqual('foo.bar.baz', out_mask.ToJsonString()) + mask1.FromJsonString('foo.bar,bar') + mask2.FromJsonString('foo.bar.baz,quz') + out_mask.Intersect(mask1, mask2) + self.assertEqual('foo.bar.baz', out_mask.ToJsonString()) + # Intersect '' with '' + mask1.Clear() + mask2.Clear() + mask1.paths.append('') + mask2.paths.append('') + self.assertEqual(mask1.paths, ['']) + self.assertEqual('', mask1.ToJsonString()) + out_mask.Intersect(mask1, mask2) + self.assertEqual(out_mask.paths, []) + + def testMergeMessageWithoutMapFields(self): + # Test merge one field. + src = unittest_pb2.TestAllTypes() + test_util.SetAllFields(src) + for field in src.DESCRIPTOR.fields: + if field.containing_oneof: + continue + field_name = field.name + dst = unittest_pb2.TestAllTypes() + # Only set one path to mask. + mask = field_mask_pb2.FieldMask() + mask.paths.append(field_name) + mask.MergeMessage(src, dst) + # The expected result message. + msg = unittest_pb2.TestAllTypes() + if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: + repeated_src = getattr(src, field_name) + repeated_msg = getattr(msg, field_name) + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + for item in repeated_src: + repeated_msg.add().CopyFrom(item) + else: + repeated_msg.extend(repeated_src) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + getattr(msg, field_name).CopyFrom(getattr(src, field_name)) + else: + setattr(msg, field_name, getattr(src, field_name)) + # Only field specified in mask is merged. + self.assertEqual(msg, dst) + + # Test merge nested fields. + nested_src = unittest_pb2.NestedTestAllTypes() + nested_dst = unittest_pb2.NestedTestAllTypes() + nested_src.child.payload.optional_int32 = 1234 + nested_src.child.child.payload.optional_int32 = 5678 + mask = field_mask_pb2.FieldMask() + mask.FromJsonString('child.payload') + mask.MergeMessage(nested_src, nested_dst) + self.assertEqual(1234, nested_dst.child.payload.optional_int32) + self.assertEqual(0, nested_dst.child.child.payload.optional_int32) + + mask.FromJsonString('child.child.payload') + mask.MergeMessage(nested_src, nested_dst) + self.assertEqual(1234, nested_dst.child.payload.optional_int32) + self.assertEqual(5678, nested_dst.child.child.payload.optional_int32) + + nested_dst.Clear() + mask.FromJsonString('child.child.payload') + mask.MergeMessage(nested_src, nested_dst) + self.assertEqual(0, nested_dst.child.payload.optional_int32) + self.assertEqual(5678, nested_dst.child.child.payload.optional_int32) + + nested_dst.Clear() + mask.FromJsonString('child') + mask.MergeMessage(nested_src, nested_dst) + self.assertEqual(1234, nested_dst.child.payload.optional_int32) + self.assertEqual(5678, nested_dst.child.child.payload.optional_int32) + + # Test MergeOptions. + nested_dst.Clear() + nested_dst.child.payload.optional_int64 = 4321 + # Message fields will be merged by default. + mask.FromJsonString('child.payload') + mask.MergeMessage(nested_src, nested_dst) + self.assertEqual(1234, nested_dst.child.payload.optional_int32) + self.assertEqual(4321, nested_dst.child.payload.optional_int64) + # Change the behavior to replace message fields. + mask.FromJsonString('child.payload') + mask.MergeMessage(nested_src, nested_dst, True, False) + self.assertEqual(1234, nested_dst.child.payload.optional_int32) + self.assertEqual(0, nested_dst.child.payload.optional_int64) + + # By default, fields missing in source are not cleared in destination. + nested_dst.payload.optional_int32 = 1234 + self.assertTrue(nested_dst.HasField('payload')) + mask.FromJsonString('payload') + mask.MergeMessage(nested_src, nested_dst) + self.assertTrue(nested_dst.HasField('payload')) + # But they are cleared when replacing message fields. + nested_dst.Clear() + nested_dst.payload.optional_int32 = 1234 + mask.FromJsonString('payload') + mask.MergeMessage(nested_src, nested_dst, True, False) + self.assertFalse(nested_dst.HasField('payload')) + + nested_src.payload.repeated_int32.append(1234) + nested_dst.payload.repeated_int32.append(5678) + # Repeated fields will be appended by default. + mask.FromJsonString('payload.repeatedInt32') + mask.MergeMessage(nested_src, nested_dst) + self.assertEqual(2, len(nested_dst.payload.repeated_int32)) + self.assertEqual(5678, nested_dst.payload.repeated_int32[0]) + self.assertEqual(1234, nested_dst.payload.repeated_int32[1]) + # Change the behavior to replace repeated fields. + mask.FromJsonString('payload.repeatedInt32') + mask.MergeMessage(nested_src, nested_dst, False, True) + self.assertEqual(1, len(nested_dst.payload.repeated_int32)) + self.assertEqual(1234, nested_dst.payload.repeated_int32[0]) + + # Test Merge oneof field. + new_msg = unittest_pb2.TestOneof2() + dst = unittest_pb2.TestOneof2() + dst.foo_message.moo_int = 1 + mask = field_mask_pb2.FieldMask() + mask.FromJsonString('fooMessage,fooLazyMessage.mooInt') + mask.MergeMessage(new_msg, dst) + self.assertTrue(dst.HasField('foo_message')) + self.assertFalse(dst.HasField('foo_lazy_message')) + + def testMergeMessageWithMapField(self): + empty_map = map_unittest_pb2.TestRecursiveMapMessage() + src_level_2 = map_unittest_pb2.TestRecursiveMapMessage() + src_level_2.a['src level 2'].CopyFrom(empty_map) + src = map_unittest_pb2.TestRecursiveMapMessage() + src.a['common key'].CopyFrom(src_level_2) + src.a['src level 1'].CopyFrom(src_level_2) + + dst_level_2 = map_unittest_pb2.TestRecursiveMapMessage() + dst_level_2.a['dst level 2'].CopyFrom(empty_map) + dst = map_unittest_pb2.TestRecursiveMapMessage() + dst.a['common key'].CopyFrom(dst_level_2) + dst.a['dst level 1'].CopyFrom(empty_map) + + mask = field_mask_pb2.FieldMask() + mask.FromJsonString('a') + mask.MergeMessage(src, dst) + + # map from dst is replaced with map from src. + self.assertEqual(dst.a['common key'], src_level_2) + self.assertEqual(dst.a['src level 1'], src_level_2) + self.assertEqual(dst.a['dst level 1'], empty_map) + + def testMergeErrors(self): + src = unittest_pb2.TestAllTypes() + dst = unittest_pb2.TestAllTypes() + mask = field_mask_pb2.FieldMask() + test_util.SetAllFields(src) + mask.FromJsonString('optionalInt32.field') + with self.assertRaises(ValueError) as e: + mask.MergeMessage(src, dst) + self.assertEqual('Error: Field optional_int32 in message ' + 'protobuf_unittest.TestAllTypes is not a singular ' + 'message field and cannot have sub-fields.', + str(e.exception)) + + def testSnakeCaseToCamelCase(self): + self.assertEqual('fooBar', + well_known_types._SnakeCaseToCamelCase('foo_bar')) + self.assertEqual('FooBar', + well_known_types._SnakeCaseToCamelCase('_foo_bar')) + self.assertEqual('foo3Bar', + well_known_types._SnakeCaseToCamelCase('foo3_bar')) + + # No uppercase letter is allowed. + self.assertRaisesRegex( + ValueError, + 'Fail to print FieldMask to Json string: Path name Foo must ' + 'not contain uppercase letters.', + well_known_types._SnakeCaseToCamelCase, 'Foo') + # Any character after a "_" must be a lowercase letter. + # 1. "_" cannot be followed by another "_". + # 2. "_" cannot be followed by a digit. + # 3. "_" cannot appear as the last character. + self.assertRaisesRegex( + ValueError, + 'Fail to print FieldMask to Json string: The character after a ' + '"_" must be a lowercase letter in path name foo__bar.', + well_known_types._SnakeCaseToCamelCase, 'foo__bar') + self.assertRaisesRegex( + ValueError, + 'Fail to print FieldMask to Json string: The character after a ' + '"_" must be a lowercase letter in path name foo_3bar.', + well_known_types._SnakeCaseToCamelCase, 'foo_3bar') + self.assertRaisesRegex( + ValueError, + 'Fail to print FieldMask to Json string: Trailing "_" in path ' + 'name foo_bar_.', well_known_types._SnakeCaseToCamelCase, 'foo_bar_') + + def testCamelCaseToSnakeCase(self): + self.assertEqual('foo_bar', + well_known_types._CamelCaseToSnakeCase('fooBar')) + self.assertEqual('_foo_bar', + well_known_types._CamelCaseToSnakeCase('FooBar')) + self.assertEqual('foo3_bar', + well_known_types._CamelCaseToSnakeCase('foo3Bar')) + self.assertRaisesRegex( + ValueError, + 'Fail to parse FieldMask: Path name foo_bar must not contain "_"s.', + well_known_types._CamelCaseToSnakeCase, 'foo_bar') + + +class StructTest(unittest.TestCase): + + def testStruct(self): + struct = struct_pb2.Struct() + self.assertIsInstance(struct, collections_abc.Mapping) + self.assertEqual(0, len(struct)) + struct_class = struct.__class__ + + struct['key1'] = 5 + struct['key2'] = 'abc' + struct['key3'] = True + struct.get_or_create_struct('key4')['subkey'] = 11.0 + struct_list = struct.get_or_create_list('key5') + self.assertIsInstance(struct_list, collections_abc.Sequence) + struct_list.extend([6, 'seven', True, False, None]) + struct_list.add_struct()['subkey2'] = 9 + struct['key6'] = {'subkey': {}} + struct['key7'] = [2, False] + + self.assertEqual(7, len(struct)) + self.assertTrue(isinstance(struct, well_known_types.Struct)) + self.assertEqual(5, struct['key1']) + self.assertEqual('abc', struct['key2']) + self.assertIs(True, struct['key3']) + self.assertEqual(11, struct['key4']['subkey']) + inner_struct = struct_class() + inner_struct['subkey2'] = 9 + self.assertEqual([6, 'seven', True, False, None, inner_struct], + list(struct['key5'].items())) + self.assertEqual({}, dict(struct['key6']['subkey'].fields)) + self.assertEqual([2, False], list(struct['key7'].items())) + + serialized = struct.SerializeToString() + struct2 = struct_pb2.Struct() + struct2.ParseFromString(serialized) + + self.assertEqual(struct, struct2) + for key, value in struct.items(): + self.assertIn(key, struct) + self.assertIn(key, struct2) + self.assertEqual(value, struct2[key]) + + self.assertEqual(7, len(struct.keys())) + self.assertEqual(7, len(struct.values())) + for key in struct.keys(): + self.assertIn(key, struct) + self.assertIn(key, struct2) + self.assertEqual(struct[key], struct2[key]) + + item = (next(iter(struct.keys())), next(iter(struct.values()))) + self.assertEqual(item, next(iter(struct.items()))) + + self.assertTrue(isinstance(struct2, well_known_types.Struct)) + self.assertEqual(5, struct2['key1']) + self.assertEqual('abc', struct2['key2']) + self.assertIs(True, struct2['key3']) + self.assertEqual(11, struct2['key4']['subkey']) + self.assertEqual([6, 'seven', True, False, None, inner_struct], + list(struct2['key5'].items())) + + struct_list = struct2['key5'] + self.assertEqual(6, struct_list[0]) + self.assertEqual('seven', struct_list[1]) + self.assertEqual(True, struct_list[2]) + self.assertEqual(False, struct_list[3]) + self.assertEqual(None, struct_list[4]) + self.assertEqual(inner_struct, struct_list[5]) + + struct_list[1] = 7 + self.assertEqual(7, struct_list[1]) + + struct_list.add_list().extend([1, 'two', True, False, None]) + self.assertEqual([1, 'two', True, False, None], + list(struct_list[6].items())) + struct_list.extend([{'nested_struct': 30}, ['nested_list', 99], {}, []]) + self.assertEqual(11, len(struct_list.values)) + self.assertEqual(30, struct_list[7]['nested_struct']) + self.assertEqual('nested_list', struct_list[8][0]) + self.assertEqual(99, struct_list[8][1]) + self.assertEqual({}, dict(struct_list[9].fields)) + self.assertEqual([], list(struct_list[10].items())) + struct_list[0] = {'replace': 'set'} + struct_list[1] = ['replace', 'set'] + self.assertEqual('set', struct_list[0]['replace']) + self.assertEqual(['replace', 'set'], list(struct_list[1].items())) + + text_serialized = str(struct) + struct3 = struct_pb2.Struct() + text_format.Merge(text_serialized, struct3) + self.assertEqual(struct, struct3) + + struct.get_or_create_struct('key3')['replace'] = 12 + self.assertEqual(12, struct['key3']['replace']) + + # Tests empty list. + struct.get_or_create_list('empty_list') + empty_list = struct['empty_list'] + self.assertEqual([], list(empty_list.items())) + list2 = struct_pb2.ListValue() + list2.add_list() + empty_list = list2[0] + self.assertEqual([], list(empty_list.items())) + + # Tests empty struct. + struct.get_or_create_struct('empty_struct') + empty_struct = struct['empty_struct'] + self.assertEqual({}, dict(empty_struct.fields)) + list2.add_struct() + empty_struct = list2[1] + self.assertEqual({}, dict(empty_struct.fields)) + + self.assertEqual(9, len(struct)) + del struct['key3'] + del struct['key4'] + self.assertEqual(7, len(struct)) + self.assertEqual(6, len(struct['key5'])) + del struct['key5'][1] + self.assertEqual(5, len(struct['key5'])) + self.assertEqual([6, True, False, None, inner_struct], + list(struct['key5'].items())) + + def testStructAssignment(self): + # Tests struct assignment from another struct + s1 = struct_pb2.Struct() + s2 = struct_pb2.Struct() + for value in [1, 'a', [1], ['a'], {'a': 'b'}]: + s1['x'] = value + s2['x'] = s1['x'] + self.assertEqual(s1['x'], s2['x']) + + def testMergeFrom(self): + struct = struct_pb2.Struct() + struct_class = struct.__class__ + + dictionary = { + 'key1': 5, + 'key2': 'abc', + 'key3': True, + 'key4': {'subkey': 11.0}, + 'key5': [6, 'seven', True, False, None, {'subkey2': 9}], + 'key6': [['nested_list', True]], + 'empty_struct': {}, + 'empty_list': [] + } + struct.update(dictionary) + self.assertEqual(5, struct['key1']) + self.assertEqual('abc', struct['key2']) + self.assertIs(True, struct['key3']) + self.assertEqual(11, struct['key4']['subkey']) + inner_struct = struct_class() + inner_struct['subkey2'] = 9 + self.assertEqual([6, 'seven', True, False, None, inner_struct], + list(struct['key5'].items())) + self.assertEqual(2, len(struct['key6'][0].values)) + self.assertEqual('nested_list', struct['key6'][0][0]) + self.assertEqual(True, struct['key6'][0][1]) + empty_list = struct['empty_list'] + self.assertEqual([], list(empty_list.items())) + empty_struct = struct['empty_struct'] + self.assertEqual({}, dict(empty_struct.fields)) + + # According to documentation: "When parsing from the wire or when merging, + # if there are duplicate map keys the last key seen is used". + duplicate = { + 'key4': {'replace': 20}, + 'key5': [[False, 5]] + } + struct.update(duplicate) + self.assertEqual(1, len(struct['key4'].fields)) + self.assertEqual(20, struct['key4']['replace']) + self.assertEqual(1, len(struct['key5'].values)) + self.assertEqual(False, struct['key5'][0][0]) + self.assertEqual(5, struct['key5'][0][1]) + + +class AnyTest(unittest.TestCase): + + def testAnyMessage(self): + # Creates and sets message. + msg = any_test_pb2.TestAny() + msg_descriptor = msg.DESCRIPTOR + all_types = unittest_pb2.TestAllTypes() + all_descriptor = all_types.DESCRIPTOR + all_types.repeated_string.append(u'\u00fc\ua71f') + # Packs to Any. + msg.value.Pack(all_types) + self.assertEqual(msg.value.type_url, + 'type.googleapis.com/%s' % all_descriptor.full_name) + self.assertEqual(msg.value.value, + all_types.SerializeToString()) + # Tests Is() method. + self.assertTrue(msg.value.Is(all_descriptor)) + self.assertFalse(msg.value.Is(msg_descriptor)) + # Unpacks Any. + unpacked_message = unittest_pb2.TestAllTypes() + self.assertTrue(msg.value.Unpack(unpacked_message)) + self.assertEqual(all_types, unpacked_message) + # Unpacks to different type. + self.assertFalse(msg.value.Unpack(msg)) + # Only Any messages have Pack method. + try: + msg.Pack(all_types) + except AttributeError: + pass + else: + raise AttributeError('%s should not have Pack method.' % + msg_descriptor.full_name) + + def testUnpackWithNoSlashInTypeUrl(self): + msg = any_test_pb2.TestAny() + all_types = unittest_pb2.TestAllTypes() + all_descriptor = all_types.DESCRIPTOR + msg.value.Pack(all_types) + # Reset type_url to part of type_url after '/' + msg.value.type_url = msg.value.TypeName() + self.assertFalse(msg.value.Is(all_descriptor)) + unpacked_message = unittest_pb2.TestAllTypes() + self.assertFalse(msg.value.Unpack(unpacked_message)) + + def testMessageName(self): + # Creates and sets message. + submessage = any_test_pb2.TestAny() + submessage.int_value = 12345 + msg = any_pb2.Any() + msg.Pack(submessage) + self.assertEqual(msg.TypeName(), 'google.protobuf.internal.TestAny') + + def testPackWithCustomTypeUrl(self): + submessage = any_test_pb2.TestAny() + submessage.int_value = 12345 + msg = any_pb2.Any() + # Pack with a custom type URL prefix. + msg.Pack(submessage, 'type.myservice.com') + self.assertEqual(msg.type_url, + 'type.myservice.com/%s' % submessage.DESCRIPTOR.full_name) + # Pack with a custom type URL prefix ending with '/'. + msg.Pack(submessage, 'type.myservice.com/') + self.assertEqual(msg.type_url, + 'type.myservice.com/%s' % submessage.DESCRIPTOR.full_name) + # Pack with an empty type URL prefix. + msg.Pack(submessage, '') + self.assertEqual(msg.type_url, + '/%s' % submessage.DESCRIPTOR.full_name) + # Test unpacking the type. + unpacked_message = any_test_pb2.TestAny() + self.assertTrue(msg.Unpack(unpacked_message)) + self.assertEqual(submessage, unpacked_message) + + def testPackDeterministic(self): + submessage = any_test_pb2.TestAny() + for i in range(10): + submessage.map_value[str(i)] = i * 2 + msg = any_pb2.Any() + msg.Pack(submessage, deterministic=True) + serialized = msg.SerializeToString(deterministic=True) + golden = (b'\n4type.googleapis.com/google.protobuf.internal.TestAny\x12F' + b'\x1a\x05\n\x010\x10\x00\x1a\x05\n\x011\x10\x02\x1a\x05\n\x01' + b'2\x10\x04\x1a\x05\n\x013\x10\x06\x1a\x05\n\x014\x10\x08\x1a' + b'\x05\n\x015\x10\n\x1a\x05\n\x016\x10\x0c\x1a\x05\n\x017\x10' + b'\x0e\x1a\x05\n\x018\x10\x10\x1a\x05\n\x019\x10\x12') + self.assertEqual(golden, serialized) + + +if __name__ == '__main__': + unittest.main() diff --git a/virt/lib/python3.9/site-packages/google/protobuf/json_format 3.py b/virt/lib/python3.9/site-packages/google/protobuf/json_format 3.py new file mode 100644 index 00000000..4204778e --- /dev/null +++ b/virt/lib/python3.9/site-packages/google/protobuf/json_format 3.py @@ -0,0 +1,912 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# 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 Google 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 +# OWNER 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. + +"""Contains routines for printing protocol messages in JSON format. + +Simple usage example: + + # Create a proto object and serialize it to a json format string. + message = my_proto_pb2.MyMessage(foo='bar') + json_string = json_format.MessageToJson(message) + + # Parse a json format string to proto object. + message = json_format.Parse(json_string, my_proto_pb2.MyMessage()) +""" + +__author__ = 'jieluo@google.com (Jie Luo)' + + +import base64 +from collections import OrderedDict +import json +import math +from operator import methodcaller +import re +import sys + +from google.protobuf.internal import type_checkers +from google.protobuf import descriptor +from google.protobuf import symbol_database + + +_TIMESTAMPFOMAT = '%Y-%m-%dT%H:%M:%S' +_INT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT32, + descriptor.FieldDescriptor.CPPTYPE_UINT32, + descriptor.FieldDescriptor.CPPTYPE_INT64, + descriptor.FieldDescriptor.CPPTYPE_UINT64]) +_INT64_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT64, + descriptor.FieldDescriptor.CPPTYPE_UINT64]) +_FLOAT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_FLOAT, + descriptor.FieldDescriptor.CPPTYPE_DOUBLE]) +_INFINITY = 'Infinity' +_NEG_INFINITY = '-Infinity' +_NAN = 'NaN' + +_UNPAIRED_SURROGATE_PATTERN = re.compile( + u'[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]') + +_VALID_EXTENSION_NAME = re.compile(r'\[[a-zA-Z0-9\._]*\]$') + + +class Error(Exception): + """Top-level module error for json_format.""" + + +class SerializeToJsonError(Error): + """Thrown if serialization to JSON fails.""" + + +class ParseError(Error): + """Thrown in case of parsing error.""" + + +def MessageToJson( + message, + including_default_value_fields=False, + preserving_proto_field_name=False, + indent=2, + sort_keys=False, + use_integers_for_enums=False, + descriptor_pool=None, + float_precision=None, + ensure_ascii=True): + """Converts protobuf message to JSON format. + + Args: + message: The protocol buffers message instance to serialize. + including_default_value_fields: If True, singular primitive fields, + repeated fields, and map fields will always be serialized. If + False, only serialize non-empty fields. Singular message fields + and oneof fields are not affected by this option. + preserving_proto_field_name: If True, use the original proto field + names as defined in the .proto file. If False, convert the field + names to lowerCamelCase. + indent: The JSON object will be pretty-printed with this indent level. + An indent level of 0 or negative will only insert newlines. + sort_keys: If True, then the output will be sorted by field names. + use_integers_for_enums: If true, print integers instead of enum names. + descriptor_pool: A Descriptor Pool for resolving types. If None use the + default. + float_precision: If set, use this to specify float field valid digits. + ensure_ascii: If True, strings with non-ASCII characters are escaped. + If False, Unicode strings are returned unchanged. + + Returns: + A string containing the JSON formatted protocol buffer message. + """ + printer = _Printer( + including_default_value_fields, + preserving_proto_field_name, + use_integers_for_enums, + descriptor_pool, + float_precision=float_precision) + return printer.ToJsonString(message, indent, sort_keys, ensure_ascii) + + +def MessageToDict( + message, + including_default_value_fields=False, + preserving_proto_field_name=False, + use_integers_for_enums=False, + descriptor_pool=None, + float_precision=None): + """Converts protobuf message to a dictionary. + + When the dictionary is encoded to JSON, it conforms to proto3 JSON spec. + + Args: + message: The protocol buffers message instance to serialize. + including_default_value_fields: If True, singular primitive fields, + repeated fields, and map fields will always be serialized. If + False, only serialize non-empty fields. Singular message fields + and oneof fields are not affected by this option. + preserving_proto_field_name: If True, use the original proto field + names as defined in the .proto file. If False, convert the field + names to lowerCamelCase. + use_integers_for_enums: If true, print integers instead of enum names. + descriptor_pool: A Descriptor Pool for resolving types. If None use the + default. + float_precision: If set, use this to specify float field valid digits. + + Returns: + A dict representation of the protocol buffer message. + """ + printer = _Printer( + including_default_value_fields, + preserving_proto_field_name, + use_integers_for_enums, + descriptor_pool, + float_precision=float_precision) + # pylint: disable=protected-access + return printer._MessageToJsonObject(message) + + +def _IsMapEntry(field): + return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and + field.message_type.has_options and + field.message_type.GetOptions().map_entry) + + +class _Printer(object): + """JSON format printer for protocol message.""" + + def __init__( + self, + including_default_value_fields=False, + preserving_proto_field_name=False, + use_integers_for_enums=False, + descriptor_pool=None, + float_precision=None): + self.including_default_value_fields = including_default_value_fields + self.preserving_proto_field_name = preserving_proto_field_name + self.use_integers_for_enums = use_integers_for_enums + self.descriptor_pool = descriptor_pool + if float_precision: + self.float_format = '.{}g'.format(float_precision) + else: + self.float_format = None + + def ToJsonString(self, message, indent, sort_keys, ensure_ascii): + js = self._MessageToJsonObject(message) + return json.dumps( + js, indent=indent, sort_keys=sort_keys, ensure_ascii=ensure_ascii) + + def _MessageToJsonObject(self, message): + """Converts message to an object according to Proto3 JSON Specification.""" + message_descriptor = message.DESCRIPTOR + full_name = message_descriptor.full_name + if _IsWrapperMessage(message_descriptor): + return self._WrapperMessageToJsonObject(message) + if full_name in _WKTJSONMETHODS: + return methodcaller(_WKTJSONMETHODS[full_name][0], message)(self) + js = {} + return self._RegularMessageToJsonObject(message, js) + + def _RegularMessageToJsonObject(self, message, js): + """Converts normal message according to Proto3 JSON Specification.""" + fields = message.ListFields() + + try: + for field, value in fields: + if self.preserving_proto_field_name: + name = field.name + else: + name = field.json_name + if _IsMapEntry(field): + # Convert a map field. + v_field = field.message_type.fields_by_name['value'] + js_map = {} + for key in value: + if isinstance(key, bool): + if key: + recorded_key = 'true' + else: + recorded_key = 'false' + else: + recorded_key = str(key) + js_map[recorded_key] = self._FieldToJsonObject( + v_field, value[key]) + js[name] = js_map + elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: + # Convert a repeated field. + js[name] = [self._FieldToJsonObject(field, k) + for k in value] + elif field.is_extension: + name = '[%s]' % field.full_name + js[name] = self._FieldToJsonObject(field, value) + else: + js[name] = self._FieldToJsonObject(field, value) + + # Serialize default value if including_default_value_fields is True. + if self.including_default_value_fields: + message_descriptor = message.DESCRIPTOR + for field in message_descriptor.fields: + # Singular message fields and oneof fields will not be affected. + if ((field.label != descriptor.FieldDescriptor.LABEL_REPEATED and + field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE) or + field.containing_oneof): + continue + if self.preserving_proto_field_name: + name = field.name + else: + name = field.json_name + if name in js: + # Skip the field which has been serialized already. + continue + if _IsMapEntry(field): + js[name] = {} + elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: + js[name] = [] + else: + js[name] = self._FieldToJsonObject(field, field.default_value) + + except ValueError as e: + raise SerializeToJsonError( + 'Failed to serialize {0} field: {1}.'.format(field.name, e)) + + return js + + def _FieldToJsonObject(self, field, value): + """Converts field value according to Proto3 JSON Specification.""" + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + return self._MessageToJsonObject(value) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: + if self.use_integers_for_enums: + return value + if field.enum_type.full_name == 'google.protobuf.NullValue': + return None + enum_value = field.enum_type.values_by_number.get(value, None) + if enum_value is not None: + return enum_value.name + else: + if field.file.syntax == 'proto3': + return value + raise SerializeToJsonError('Enum field contains an integer value ' + 'which can not mapped to an enum value.') + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: + if field.type == descriptor.FieldDescriptor.TYPE_BYTES: + # Use base64 Data encoding for bytes + return base64.b64encode(value).decode('utf-8') + else: + return value + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: + return bool(value) + elif field.cpp_type in _INT64_TYPES: + return str(value) + elif field.cpp_type in _FLOAT_TYPES: + if math.isinf(value): + if value < 0.0: + return _NEG_INFINITY + else: + return _INFINITY + if math.isnan(value): + return _NAN + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT: + if self.float_format: + return float(format(value, self.float_format)) + else: + return type_checkers.ToShortestFloat(value) + + return value + + def _AnyMessageToJsonObject(self, message): + """Converts Any message according to Proto3 JSON Specification.""" + if not message.ListFields(): + return {} + # Must print @type first, use OrderedDict instead of {} + js = OrderedDict() + type_url = message.type_url + js['@type'] = type_url + sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) + sub_message.ParseFromString(message.value) + message_descriptor = sub_message.DESCRIPTOR + full_name = message_descriptor.full_name + if _IsWrapperMessage(message_descriptor): + js['value'] = self._WrapperMessageToJsonObject(sub_message) + return js + if full_name in _WKTJSONMETHODS: + js['value'] = methodcaller(_WKTJSONMETHODS[full_name][0], + sub_message)(self) + return js + return self._RegularMessageToJsonObject(sub_message, js) + + def _GenericMessageToJsonObject(self, message): + """Converts message according to Proto3 JSON Specification.""" + # Duration, Timestamp and FieldMask have ToJsonString method to do the + # convert. Users can also call the method directly. + return message.ToJsonString() + + def _ValueMessageToJsonObject(self, message): + """Converts Value message according to Proto3 JSON Specification.""" + which = message.WhichOneof('kind') + # If the Value message is not set treat as null_value when serialize + # to JSON. The parse back result will be different from original message. + if which is None or which == 'null_value': + return None + if which == 'list_value': + return self._ListValueMessageToJsonObject(message.list_value) + if which == 'struct_value': + value = message.struct_value + else: + value = getattr(message, which) + oneof_descriptor = message.DESCRIPTOR.fields_by_name[which] + return self._FieldToJsonObject(oneof_descriptor, value) + + def _ListValueMessageToJsonObject(self, message): + """Converts ListValue message according to Proto3 JSON Specification.""" + return [self._ValueMessageToJsonObject(value) + for value in message.values] + + def _StructMessageToJsonObject(self, message): + """Converts Struct message according to Proto3 JSON Specification.""" + fields = message.fields + ret = {} + for key in fields: + ret[key] = self._ValueMessageToJsonObject(fields[key]) + return ret + + def _WrapperMessageToJsonObject(self, message): + return self._FieldToJsonObject( + message.DESCRIPTOR.fields_by_name['value'], message.value) + + +def _IsWrapperMessage(message_descriptor): + return message_descriptor.file.name == 'google/protobuf/wrappers.proto' + + +def _DuplicateChecker(js): + result = {} + for name, value in js: + if name in result: + raise ParseError('Failed to load JSON: duplicate key {0}.'.format(name)) + result[name] = value + return result + + +def _CreateMessageFromTypeUrl(type_url, descriptor_pool): + """Creates a message from a type URL.""" + db = symbol_database.Default() + pool = db.pool if descriptor_pool is None else descriptor_pool + type_name = type_url.split('/')[-1] + try: + message_descriptor = pool.FindMessageTypeByName(type_name) + except KeyError: + raise TypeError( + 'Can not find message descriptor by type_url: {0}'.format(type_url)) + message_class = db.GetPrototype(message_descriptor) + return message_class() + + +def Parse(text, + message, + ignore_unknown_fields=False, + descriptor_pool=None, + max_recursion_depth=100): + """Parses a JSON representation of a protocol message into a message. + + Args: + text: Message JSON representation. + message: A protocol buffer message to merge into. + ignore_unknown_fields: If True, do not raise errors for unknown fields. + descriptor_pool: A Descriptor Pool for resolving types. If None use the + default. + max_recursion_depth: max recursion depth of JSON message to be + deserialized. JSON messages over this depth will fail to be + deserialized. Default value is 100. + + Returns: + The same message passed as argument. + + Raises:: + ParseError: On JSON parsing problems. + """ + if not isinstance(text, str): + text = text.decode('utf-8') + try: + js = json.loads(text, object_pairs_hook=_DuplicateChecker) + except ValueError as e: + raise ParseError('Failed to load JSON: {0}.'.format(str(e))) + return ParseDict(js, message, ignore_unknown_fields, descriptor_pool, + max_recursion_depth) + + +def ParseDict(js_dict, + message, + ignore_unknown_fields=False, + descriptor_pool=None, + max_recursion_depth=100): + """Parses a JSON dictionary representation into a message. + + Args: + js_dict: Dict representation of a JSON message. + message: A protocol buffer message to merge into. + ignore_unknown_fields: If True, do not raise errors for unknown fields. + descriptor_pool: A Descriptor Pool for resolving types. If None use the + default. + max_recursion_depth: max recursion depth of JSON message to be + deserialized. JSON messages over this depth will fail to be + deserialized. Default value is 100. + + Returns: + The same message passed as argument. + """ + parser = _Parser(ignore_unknown_fields, descriptor_pool, max_recursion_depth) + parser.ConvertMessage(js_dict, message, '') + return message + + +_INT_OR_FLOAT = (int, float) + + +class _Parser(object): + """JSON format parser for protocol message.""" + + def __init__(self, ignore_unknown_fields, descriptor_pool, + max_recursion_depth): + self.ignore_unknown_fields = ignore_unknown_fields + self.descriptor_pool = descriptor_pool + self.max_recursion_depth = max_recursion_depth + self.recursion_depth = 0 + + def ConvertMessage(self, value, message, path): + """Convert a JSON object into a message. + + Args: + value: A JSON object. + message: A WKT or regular protocol message to record the data. + path: parent path to log parse error info. + + Raises: + ParseError: In case of convert problems. + """ + self.recursion_depth += 1 + if self.recursion_depth > self.max_recursion_depth: + raise ParseError('Message too deep. Max recursion depth is {0}'.format( + self.max_recursion_depth)) + message_descriptor = message.DESCRIPTOR + full_name = message_descriptor.full_name + if not path: + path = message_descriptor.name + if _IsWrapperMessage(message_descriptor): + self._ConvertWrapperMessage(value, message, path) + elif full_name in _WKTJSONMETHODS: + methodcaller(_WKTJSONMETHODS[full_name][1], value, message, path)(self) + else: + self._ConvertFieldValuePair(value, message, path) + self.recursion_depth -= 1 + + def _ConvertFieldValuePair(self, js, message, path): + """Convert field value pairs into regular message. + + Args: + js: A JSON object to convert the field value pairs. + message: A regular protocol message to record the data. + path: parent path to log parse error info. + + Raises: + ParseError: In case of problems converting. + """ + names = [] + message_descriptor = message.DESCRIPTOR + fields_by_json_name = dict((f.json_name, f) + for f in message_descriptor.fields) + for name in js: + try: + field = fields_by_json_name.get(name, None) + if not field: + field = message_descriptor.fields_by_name.get(name, None) + if not field and _VALID_EXTENSION_NAME.match(name): + if not message_descriptor.is_extendable: + raise ParseError( + 'Message type {0} does not have extensions at {1}'.format( + message_descriptor.full_name, path)) + identifier = name[1:-1] # strip [] brackets + # pylint: disable=protected-access + field = message.Extensions._FindExtensionByName(identifier) + # pylint: enable=protected-access + if not field: + # Try looking for extension by the message type name, dropping the + # field name following the final . separator in full_name. + identifier = '.'.join(identifier.split('.')[:-1]) + # pylint: disable=protected-access + field = message.Extensions._FindExtensionByName(identifier) + # pylint: enable=protected-access + if not field: + if self.ignore_unknown_fields: + continue + raise ParseError( + ('Message type "{0}" has no field named "{1}" at "{2}".\n' + ' Available Fields(except extensions): "{3}"').format( + message_descriptor.full_name, name, path, + [f.json_name for f in message_descriptor.fields])) + if name in names: + raise ParseError('Message type "{0}" should not have multiple ' + '"{1}" fields at "{2}".'.format( + message.DESCRIPTOR.full_name, name, path)) + names.append(name) + value = js[name] + # Check no other oneof field is parsed. + if field.containing_oneof is not None and value is not None: + oneof_name = field.containing_oneof.name + if oneof_name in names: + raise ParseError('Message type "{0}" should not have multiple ' + '"{1}" oneof fields at "{2}".'.format( + message.DESCRIPTOR.full_name, oneof_name, + path)) + names.append(oneof_name) + + if value is None: + if (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE + and field.message_type.full_name == 'google.protobuf.Value'): + sub_message = getattr(message, field.name) + sub_message.null_value = 0 + elif (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM + and field.enum_type.full_name == 'google.protobuf.NullValue'): + setattr(message, field.name, 0) + else: + message.ClearField(field.name) + continue + + # Parse field value. + if _IsMapEntry(field): + message.ClearField(field.name) + self._ConvertMapFieldValue(value, message, field, + '{0}.{1}'.format(path, name)) + elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: + message.ClearField(field.name) + if not isinstance(value, list): + raise ParseError('repeated field {0} must be in [] which is ' + '{1} at {2}'.format(name, value, path)) + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + # Repeated message field. + for index, item in enumerate(value): + sub_message = getattr(message, field.name).add() + # None is a null_value in Value. + if (item is None and + sub_message.DESCRIPTOR.full_name != 'google.protobuf.Value'): + raise ParseError('null is not allowed to be used as an element' + ' in a repeated field at {0}.{1}[{2}]'.format( + path, name, index)) + self.ConvertMessage(item, sub_message, + '{0}.{1}[{2}]'.format(path, name, index)) + else: + # Repeated scalar field. + for index, item in enumerate(value): + if item is None: + raise ParseError('null is not allowed to be used as an element' + ' in a repeated field at {0}.{1}[{2}]'.format( + path, name, index)) + getattr(message, field.name).append( + _ConvertScalarFieldValue( + item, field, '{0}.{1}[{2}]'.format(path, name, index))) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + if field.is_extension: + sub_message = message.Extensions[field] + else: + sub_message = getattr(message, field.name) + sub_message.SetInParent() + self.ConvertMessage(value, sub_message, '{0}.{1}'.format(path, name)) + else: + if field.is_extension: + message.Extensions[field] = _ConvertScalarFieldValue( + value, field, '{0}.{1}'.format(path, name)) + else: + setattr( + message, field.name, + _ConvertScalarFieldValue(value, field, + '{0}.{1}'.format(path, name))) + except ParseError as e: + if field and field.containing_oneof is None: + raise ParseError('Failed to parse {0} field: {1}.'.format(name, e)) + else: + raise ParseError(str(e)) + except ValueError as e: + raise ParseError('Failed to parse {0} field: {1}.'.format(name, e)) + except TypeError as e: + raise ParseError('Failed to parse {0} field: {1}.'.format(name, e)) + + def _ConvertAnyMessage(self, value, message, path): + """Convert a JSON representation into Any message.""" + if isinstance(value, dict) and not value: + return + try: + type_url = value['@type'] + except KeyError: + raise ParseError( + '@type is missing when parsing any message at {0}'.format(path)) + + try: + sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) + except TypeError as e: + raise ParseError('{0} at {1}'.format(e, path)) + message_descriptor = sub_message.DESCRIPTOR + full_name = message_descriptor.full_name + if _IsWrapperMessage(message_descriptor): + self._ConvertWrapperMessage(value['value'], sub_message, + '{0}.value'.format(path)) + elif full_name in _WKTJSONMETHODS: + methodcaller(_WKTJSONMETHODS[full_name][1], value['value'], sub_message, + '{0}.value'.format(path))( + self) + else: + del value['@type'] + self._ConvertFieldValuePair(value, sub_message, path) + value['@type'] = type_url + # Sets Any message + message.value = sub_message.SerializeToString() + message.type_url = type_url + + def _ConvertGenericMessage(self, value, message, path): + """Convert a JSON representation into message with FromJsonString.""" + # Duration, Timestamp, FieldMask have a FromJsonString method to do the + # conversion. Users can also call the method directly. + try: + message.FromJsonString(value) + except ValueError as e: + raise ParseError('{0} at {1}'.format(e, path)) + + def _ConvertValueMessage(self, value, message, path): + """Convert a JSON representation into Value message.""" + if isinstance(value, dict): + self._ConvertStructMessage(value, message.struct_value, path) + elif isinstance(value, list): + self._ConvertListValueMessage(value, message.list_value, path) + elif value is None: + message.null_value = 0 + elif isinstance(value, bool): + message.bool_value = value + elif isinstance(value, str): + message.string_value = value + elif isinstance(value, _INT_OR_FLOAT): + message.number_value = value + else: + raise ParseError('Value {0} has unexpected type {1} at {2}'.format( + value, type(value), path)) + + def _ConvertListValueMessage(self, value, message, path): + """Convert a JSON representation into ListValue message.""" + if not isinstance(value, list): + raise ParseError('ListValue must be in [] which is {0} at {1}'.format( + value, path)) + message.ClearField('values') + for index, item in enumerate(value): + self._ConvertValueMessage(item, message.values.add(), + '{0}[{1}]'.format(path, index)) + + def _ConvertStructMessage(self, value, message, path): + """Convert a JSON representation into Struct message.""" + if not isinstance(value, dict): + raise ParseError('Struct must be in a dict which is {0} at {1}'.format( + value, path)) + # Clear will mark the struct as modified so it will be created even if + # there are no values. + message.Clear() + for key in value: + self._ConvertValueMessage(value[key], message.fields[key], + '{0}.{1}'.format(path, key)) + return + + def _ConvertWrapperMessage(self, value, message, path): + """Convert a JSON representation into Wrapper message.""" + field = message.DESCRIPTOR.fields_by_name['value'] + setattr( + message, 'value', + _ConvertScalarFieldValue(value, field, path='{0}.value'.format(path))) + + def _ConvertMapFieldValue(self, value, message, field, path): + """Convert map field value for a message map field. + + Args: + value: A JSON object to convert the map field value. + message: A protocol message to record the converted data. + field: The descriptor of the map field to be converted. + path: parent path to log parse error info. + + Raises: + ParseError: In case of convert problems. + """ + if not isinstance(value, dict): + raise ParseError( + 'Map field {0} must be in a dict which is {1} at {2}'.format( + field.name, value, path)) + key_field = field.message_type.fields_by_name['key'] + value_field = field.message_type.fields_by_name['value'] + for key in value: + key_value = _ConvertScalarFieldValue(key, key_field, + '{0}.key'.format(path), True) + if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + self.ConvertMessage(value[key], + getattr(message, field.name)[key_value], + '{0}[{1}]'.format(path, key_value)) + else: + getattr(message, field.name)[key_value] = _ConvertScalarFieldValue( + value[key], value_field, path='{0}[{1}]'.format(path, key_value)) + + +def _ConvertScalarFieldValue(value, field, path, require_str=False): + """Convert a single scalar field value. + + Args: + value: A scalar value to convert the scalar field value. + field: The descriptor of the field to convert. + path: parent path to log parse error info. + require_str: If True, the field value must be a str. + + Returns: + The converted scalar field value + + Raises: + ParseError: In case of convert problems. + """ + try: + if field.cpp_type in _INT_TYPES: + return _ConvertInteger(value) + elif field.cpp_type in _FLOAT_TYPES: + return _ConvertFloat(value, field) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: + return _ConvertBool(value, require_str) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: + if field.type == descriptor.FieldDescriptor.TYPE_BYTES: + if isinstance(value, str): + encoded = value.encode('utf-8') + else: + encoded = value + # Add extra padding '=' + padded_value = encoded + b'=' * (4 - len(encoded) % 4) + return base64.urlsafe_b64decode(padded_value) + else: + # Checking for unpaired surrogates appears to be unreliable, + # depending on the specific Python version, so we check manually. + if _UNPAIRED_SURROGATE_PATTERN.search(value): + raise ParseError('Unpaired surrogate') + return value + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: + # Convert an enum value. + enum_value = field.enum_type.values_by_name.get(value, None) + if enum_value is None: + try: + number = int(value) + enum_value = field.enum_type.values_by_number.get(number, None) + except ValueError: + raise ParseError('Invalid enum value {0} for enum type {1}'.format( + value, field.enum_type.full_name)) + if enum_value is None: + if field.file.syntax == 'proto3': + # Proto3 accepts unknown enums. + return number + raise ParseError('Invalid enum value {0} for enum type {1}'.format( + value, field.enum_type.full_name)) + return enum_value.number + except ParseError as e: + raise ParseError('{0} at {1}'.format(e, path)) + + +def _ConvertInteger(value): + """Convert an integer. + + Args: + value: A scalar value to convert. + + Returns: + The integer value. + + Raises: + ParseError: If an integer couldn't be consumed. + """ + if isinstance(value, float) and not value.is_integer(): + raise ParseError('Couldn\'t parse integer: {0}'.format(value)) + + if isinstance(value, str) and value.find(' ') != -1: + raise ParseError('Couldn\'t parse integer: "{0}"'.format(value)) + + if isinstance(value, bool): + raise ParseError('Bool value {0} is not acceptable for ' + 'integer field'.format(value)) + + return int(value) + + +def _ConvertFloat(value, field): + """Convert an floating point number.""" + if isinstance(value, float): + if math.isnan(value): + raise ParseError('Couldn\'t parse NaN, use quoted "NaN" instead') + if math.isinf(value): + if value > 0: + raise ParseError('Couldn\'t parse Infinity or value too large, ' + 'use quoted "Infinity" instead') + else: + raise ParseError('Couldn\'t parse -Infinity or value too small, ' + 'use quoted "-Infinity" instead') + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT: + # pylint: disable=protected-access + if value > type_checkers._FLOAT_MAX: + raise ParseError('Float value too large') + # pylint: disable=protected-access + if value < type_checkers._FLOAT_MIN: + raise ParseError('Float value too small') + if value == 'nan': + raise ParseError('Couldn\'t parse float "nan", use "NaN" instead') + try: + # Assume Python compatible syntax. + return float(value) + except ValueError: + # Check alternative spellings. + if value == _NEG_INFINITY: + return float('-inf') + elif value == _INFINITY: + return float('inf') + elif value == _NAN: + return float('nan') + else: + raise ParseError('Couldn\'t parse float: {0}'.format(value)) + + +def _ConvertBool(value, require_str): + """Convert a boolean value. + + Args: + value: A scalar value to convert. + require_str: If True, value must be a str. + + Returns: + The bool parsed. + + Raises: + ParseError: If a boolean value couldn't be consumed. + """ + if require_str: + if value == 'true': + return True + elif value == 'false': + return False + else: + raise ParseError('Expected "true" or "false", not {0}'.format(value)) + + if not isinstance(value, bool): + raise ParseError('Expected true or false without quotes') + return value + +_WKTJSONMETHODS = { + 'google.protobuf.Any': ['_AnyMessageToJsonObject', + '_ConvertAnyMessage'], + 'google.protobuf.Duration': ['_GenericMessageToJsonObject', + '_ConvertGenericMessage'], + 'google.protobuf.FieldMask': ['_GenericMessageToJsonObject', + '_ConvertGenericMessage'], + 'google.protobuf.ListValue': ['_ListValueMessageToJsonObject', + '_ConvertListValueMessage'], + 'google.protobuf.Struct': ['_StructMessageToJsonObject', + '_ConvertStructMessage'], + 'google.protobuf.Timestamp': ['_GenericMessageToJsonObject', + '_ConvertGenericMessage'], + 'google.protobuf.Value': ['_ValueMessageToJsonObject', + '_ConvertValueMessage'] +} diff --git a/virt/lib/python3.9/site-packages/google/protobuf/text_format 3.py b/virt/lib/python3.9/site-packages/google/protobuf/text_format 3.py new file mode 100644 index 00000000..8e6f1fa5 --- /dev/null +++ b/virt/lib/python3.9/site-packages/google/protobuf/text_format 3.py @@ -0,0 +1,1842 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# 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 Google 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 +# OWNER 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. + +"""Contains routines for printing protocol messages in text format. + +Simple usage example:: + + # Create a proto object and serialize it to a text proto string. + message = my_proto_pb2.MyMessage(foo='bar') + text_proto = text_format.MessageToString(message) + + # Parse a text proto string. + message = text_format.Parse(text_proto, my_proto_pb2.MyMessage()) +""" + +__author__ = 'kenton@google.com (Kenton Varda)' + +# TODO(b/129989314) Import thread contention leads to test failures. +import encodings.raw_unicode_escape # pylint: disable=unused-import +import encodings.unicode_escape # pylint: disable=unused-import +import io +import math +import re + +from google.protobuf.internal import decoder +from google.protobuf.internal import type_checkers +from google.protobuf import descriptor +from google.protobuf import text_encoding +from google.protobuf import unknown_fields + +# pylint: disable=g-import-not-at-top +__all__ = ['MessageToString', 'Parse', 'PrintMessage', 'PrintField', + 'PrintFieldValue', 'Merge', 'MessageToBytes'] + +_INTEGER_CHECKERS = (type_checkers.Uint32ValueChecker(), + type_checkers.Int32ValueChecker(), + type_checkers.Uint64ValueChecker(), + type_checkers.Int64ValueChecker()) +_FLOAT_INFINITY = re.compile('-?inf(?:inity)?f?$', re.IGNORECASE) +_FLOAT_NAN = re.compile('nanf?$', re.IGNORECASE) +_QUOTES = frozenset(("'", '"')) +_ANY_FULL_TYPE_NAME = 'google.protobuf.Any' + + +class Error(Exception): + """Top-level module error for text_format.""" + + +class ParseError(Error): + """Thrown in case of text parsing or tokenizing error.""" + + def __init__(self, message=None, line=None, column=None): + if message is not None and line is not None: + loc = str(line) + if column is not None: + loc += ':{0}'.format(column) + message = '{0} : {1}'.format(loc, message) + if message is not None: + super(ParseError, self).__init__(message) + else: + super(ParseError, self).__init__() + self._line = line + self._column = column + + def GetLine(self): + return self._line + + def GetColumn(self): + return self._column + + +class TextWriter(object): + + def __init__(self, as_utf8): + self._writer = io.StringIO() + + def write(self, val): + return self._writer.write(val) + + def close(self): + return self._writer.close() + + def getvalue(self): + return self._writer.getvalue() + + +def MessageToString( + message, + as_utf8=False, + as_one_line=False, + use_short_repeated_primitives=False, + pointy_brackets=False, + use_index_order=False, + float_format=None, + double_format=None, + use_field_number=False, + descriptor_pool=None, + indent=0, + message_formatter=None, + print_unknown_fields=False, + force_colon=False): + # type: (...) -> str + """Convert protobuf message to text format. + + Double values can be formatted compactly with 15 digits of + precision (which is the most that IEEE 754 "double" can guarantee) + using double_format='.15g'. To ensure that converting to text and back to a + proto will result in an identical value, double_format='.17g' should be used. + + Args: + message: The protocol buffers message. + as_utf8: Return unescaped Unicode for non-ASCII characters. + as_one_line: Don't introduce newlines between fields. + use_short_repeated_primitives: Use short repeated format for primitives. + pointy_brackets: If True, use angle brackets instead of curly braces for + nesting. + use_index_order: If True, fields of a proto message will be printed using + the order defined in source code instead of the field number, extensions + will be printed at the end of the message and their relative order is + determined by the extension number. By default, use the field number + order. + float_format (str): If set, use this to specify float field formatting + (per the "Format Specification Mini-Language"); otherwise, shortest float + that has same value in wire will be printed. Also affect double field + if double_format is not set but float_format is set. + double_format (str): If set, use this to specify double field formatting + (per the "Format Specification Mini-Language"); if it is not set but + float_format is set, use float_format. Otherwise, use ``str()`` + use_field_number: If True, print field numbers instead of names. + descriptor_pool (DescriptorPool): Descriptor pool used to resolve Any types. + indent (int): The initial indent level, in terms of spaces, for pretty + print. + message_formatter (function(message, indent, as_one_line) -> unicode|None): + Custom formatter for selected sub-messages (usually based on message + type). Use to pretty print parts of the protobuf for easier diffing. + print_unknown_fields: If True, unknown fields will be printed. + force_colon: If set, a colon will be added after the field name even if the + field is a proto message. + + Returns: + str: A string of the text formatted protocol buffer message. + """ + out = TextWriter(as_utf8) + printer = _Printer( + out, + indent, + as_utf8, + as_one_line, + use_short_repeated_primitives, + pointy_brackets, + use_index_order, + float_format, + double_format, + use_field_number, + descriptor_pool, + message_formatter, + print_unknown_fields=print_unknown_fields, + force_colon=force_colon) + printer.PrintMessage(message) + result = out.getvalue() + out.close() + if as_one_line: + return result.rstrip() + return result + + +def MessageToBytes(message, **kwargs): + # type: (...) -> bytes + """Convert protobuf message to encoded text format. See MessageToString.""" + text = MessageToString(message, **kwargs) + if isinstance(text, bytes): + return text + codec = 'utf-8' if kwargs.get('as_utf8') else 'ascii' + return text.encode(codec) + + +def _IsMapEntry(field): + return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and + field.message_type.has_options and + field.message_type.GetOptions().map_entry) + + +def PrintMessage(message, + out, + indent=0, + as_utf8=False, + as_one_line=False, + use_short_repeated_primitives=False, + pointy_brackets=False, + use_index_order=False, + float_format=None, + double_format=None, + use_field_number=False, + descriptor_pool=None, + message_formatter=None, + print_unknown_fields=False, + force_colon=False): + """Convert the message to text format and write it to the out stream. + + Args: + message: The Message object to convert to text format. + out: A file handle to write the message to. + indent: The initial indent level for pretty print. + as_utf8: Return unescaped Unicode for non-ASCII characters. + as_one_line: Don't introduce newlines between fields. + use_short_repeated_primitives: Use short repeated format for primitives. + pointy_brackets: If True, use angle brackets instead of curly braces for + nesting. + use_index_order: If True, print fields of a proto message using the order + defined in source code instead of the field number. By default, use the + field number order. + float_format: If set, use this to specify float field formatting + (per the "Format Specification Mini-Language"); otherwise, shortest + float that has same value in wire will be printed. Also affect double + field if double_format is not set but float_format is set. + double_format: If set, use this to specify double field formatting + (per the "Format Specification Mini-Language"); if it is not set but + float_format is set, use float_format. Otherwise, str() is used. + use_field_number: If True, print field numbers instead of names. + descriptor_pool: A DescriptorPool used to resolve Any types. + message_formatter: A function(message, indent, as_one_line): unicode|None + to custom format selected sub-messages (usually based on message type). + Use to pretty print parts of the protobuf for easier diffing. + print_unknown_fields: If True, unknown fields will be printed. + force_colon: If set, a colon will be added after the field name even if + the field is a proto message. + """ + printer = _Printer( + out=out, indent=indent, as_utf8=as_utf8, + as_one_line=as_one_line, + use_short_repeated_primitives=use_short_repeated_primitives, + pointy_brackets=pointy_brackets, + use_index_order=use_index_order, + float_format=float_format, + double_format=double_format, + use_field_number=use_field_number, + descriptor_pool=descriptor_pool, + message_formatter=message_formatter, + print_unknown_fields=print_unknown_fields, + force_colon=force_colon) + printer.PrintMessage(message) + + +def PrintField(field, + value, + out, + indent=0, + as_utf8=False, + as_one_line=False, + use_short_repeated_primitives=False, + pointy_brackets=False, + use_index_order=False, + float_format=None, + double_format=None, + message_formatter=None, + print_unknown_fields=False, + force_colon=False): + """Print a single field name/value pair.""" + printer = _Printer(out, indent, as_utf8, as_one_line, + use_short_repeated_primitives, pointy_brackets, + use_index_order, float_format, double_format, + message_formatter=message_formatter, + print_unknown_fields=print_unknown_fields, + force_colon=force_colon) + printer.PrintField(field, value) + + +def PrintFieldValue(field, + value, + out, + indent=0, + as_utf8=False, + as_one_line=False, + use_short_repeated_primitives=False, + pointy_brackets=False, + use_index_order=False, + float_format=None, + double_format=None, + message_formatter=None, + print_unknown_fields=False, + force_colon=False): + """Print a single field value (not including name).""" + printer = _Printer(out, indent, as_utf8, as_one_line, + use_short_repeated_primitives, pointy_brackets, + use_index_order, float_format, double_format, + message_formatter=message_formatter, + print_unknown_fields=print_unknown_fields, + force_colon=force_colon) + printer.PrintFieldValue(field, value) + + +def _BuildMessageFromTypeName(type_name, descriptor_pool): + """Returns a protobuf message instance. + + Args: + type_name: Fully-qualified protobuf message type name string. + descriptor_pool: DescriptorPool instance. + + Returns: + A Message instance of type matching type_name, or None if the a Descriptor + wasn't found matching type_name. + """ + # pylint: disable=g-import-not-at-top + if descriptor_pool is None: + from google.protobuf import descriptor_pool as pool_mod + descriptor_pool = pool_mod.Default() + from google.protobuf import symbol_database + database = symbol_database.Default() + try: + message_descriptor = descriptor_pool.FindMessageTypeByName(type_name) + except KeyError: + return None + message_type = database.GetPrototype(message_descriptor) + return message_type() + + +# These values must match WireType enum in google/protobuf/wire_format.h. +WIRETYPE_LENGTH_DELIMITED = 2 +WIRETYPE_START_GROUP = 3 + + +class _Printer(object): + """Text format printer for protocol message.""" + + def __init__( + self, + out, + indent=0, + as_utf8=False, + as_one_line=False, + use_short_repeated_primitives=False, + pointy_brackets=False, + use_index_order=False, + float_format=None, + double_format=None, + use_field_number=False, + descriptor_pool=None, + message_formatter=None, + print_unknown_fields=False, + force_colon=False): + """Initialize the Printer. + + Double values can be formatted compactly with 15 digits of precision + (which is the most that IEEE 754 "double" can guarantee) using + double_format='.15g'. To ensure that converting to text and back to a proto + will result in an identical value, double_format='.17g' should be used. + + Args: + out: To record the text format result. + indent: The initial indent level for pretty print. + as_utf8: Return unescaped Unicode for non-ASCII characters. + as_one_line: Don't introduce newlines between fields. + use_short_repeated_primitives: Use short repeated format for primitives. + pointy_brackets: If True, use angle brackets instead of curly braces for + nesting. + use_index_order: If True, print fields of a proto message using the order + defined in source code instead of the field number. By default, use the + field number order. + float_format: If set, use this to specify float field formatting + (per the "Format Specification Mini-Language"); otherwise, shortest + float that has same value in wire will be printed. Also affect double + field if double_format is not set but float_format is set. + double_format: If set, use this to specify double field formatting + (per the "Format Specification Mini-Language"); if it is not set but + float_format is set, use float_format. Otherwise, str() is used. + use_field_number: If True, print field numbers instead of names. + descriptor_pool: A DescriptorPool used to resolve Any types. + message_formatter: A function(message, indent, as_one_line): unicode|None + to custom format selected sub-messages (usually based on message type). + Use to pretty print parts of the protobuf for easier diffing. + print_unknown_fields: If True, unknown fields will be printed. + force_colon: If set, a colon will be added after the field name even if + the field is a proto message. + """ + self.out = out + self.indent = indent + self.as_utf8 = as_utf8 + self.as_one_line = as_one_line + self.use_short_repeated_primitives = use_short_repeated_primitives + self.pointy_brackets = pointy_brackets + self.use_index_order = use_index_order + self.float_format = float_format + if double_format is not None: + self.double_format = double_format + else: + self.double_format = float_format + self.use_field_number = use_field_number + self.descriptor_pool = descriptor_pool + self.message_formatter = message_formatter + self.print_unknown_fields = print_unknown_fields + self.force_colon = force_colon + + def _TryPrintAsAnyMessage(self, message): + """Serializes if message is a google.protobuf.Any field.""" + if '/' not in message.type_url: + return False + packed_message = _BuildMessageFromTypeName(message.TypeName(), + self.descriptor_pool) + if packed_message: + packed_message.MergeFromString(message.value) + colon = ':' if self.force_colon else '' + self.out.write('%s[%s]%s ' % (self.indent * ' ', message.type_url, colon)) + self._PrintMessageFieldValue(packed_message) + self.out.write(' ' if self.as_one_line else '\n') + return True + else: + return False + + def _TryCustomFormatMessage(self, message): + formatted = self.message_formatter(message, self.indent, self.as_one_line) + if formatted is None: + return False + + out = self.out + out.write(' ' * self.indent) + out.write(formatted) + out.write(' ' if self.as_one_line else '\n') + return True + + def PrintMessage(self, message): + """Convert protobuf message to text format. + + Args: + message: The protocol buffers message. + """ + if self.message_formatter and self._TryCustomFormatMessage(message): + return + if (message.DESCRIPTOR.full_name == _ANY_FULL_TYPE_NAME and + self._TryPrintAsAnyMessage(message)): + return + fields = message.ListFields() + if self.use_index_order: + fields.sort( + key=lambda x: x[0].number if x[0].is_extension else x[0].index) + for field, value in fields: + if _IsMapEntry(field): + for key in sorted(value): + # This is slow for maps with submessage entries because it copies the + # entire tree. Unfortunately this would take significant refactoring + # of this file to work around. + # + # TODO(haberman): refactor and optimize if this becomes an issue. + entry_submsg = value.GetEntryClass()(key=key, value=value[key]) + self.PrintField(field, entry_submsg) + elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: + if (self.use_short_repeated_primitives + and field.cpp_type != descriptor.FieldDescriptor.CPPTYPE_MESSAGE + and field.cpp_type != descriptor.FieldDescriptor.CPPTYPE_STRING): + self._PrintShortRepeatedPrimitivesValue(field, value) + else: + for element in value: + self.PrintField(field, element) + else: + self.PrintField(field, value) + + if self.print_unknown_fields: + self._PrintUnknownFields(unknown_fields.UnknownFieldSet(message)) + + def _PrintUnknownFields(self, unknown_field_set): + """Print unknown fields.""" + out = self.out + for field in unknown_field_set: + out.write(' ' * self.indent) + out.write(str(field.field_number)) + if field.wire_type == WIRETYPE_START_GROUP: + if self.as_one_line: + out.write(' { ') + else: + out.write(' {\n') + self.indent += 2 + + self._PrintUnknownFields(field.data) + + if self.as_one_line: + out.write('} ') + else: + self.indent -= 2 + out.write(' ' * self.indent + '}\n') + elif field.wire_type == WIRETYPE_LENGTH_DELIMITED: + try: + # If this field is parseable as a Message, it is probably + # an embedded message. + # pylint: disable=protected-access + (embedded_unknown_message, pos) = decoder._DecodeUnknownFieldSet( + memoryview(field.data), 0, len(field.data)) + except Exception: # pylint: disable=broad-except + pos = 0 + + if pos == len(field.data): + if self.as_one_line: + out.write(' { ') + else: + out.write(' {\n') + self.indent += 2 + + self._PrintUnknownFields(embedded_unknown_message) + + if self.as_one_line: + out.write('} ') + else: + self.indent -= 2 + out.write(' ' * self.indent + '}\n') + else: + # A string or bytes field. self.as_utf8 may not work. + out.write(': \"') + out.write(text_encoding.CEscape(field.data, False)) + out.write('\" ' if self.as_one_line else '\"\n') + else: + # varint, fixed32, fixed64 + out.write(': ') + out.write(str(field.data)) + out.write(' ' if self.as_one_line else '\n') + + def _PrintFieldName(self, field): + """Print field name.""" + out = self.out + out.write(' ' * self.indent) + if self.use_field_number: + out.write(str(field.number)) + else: + if field.is_extension: + out.write('[') + if (field.containing_type.GetOptions().message_set_wire_format and + field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and + field.label == descriptor.FieldDescriptor.LABEL_OPTIONAL): + out.write(field.message_type.full_name) + else: + out.write(field.full_name) + out.write(']') + elif field.type == descriptor.FieldDescriptor.TYPE_GROUP: + # For groups, use the capitalized name. + out.write(field.message_type.name) + else: + out.write(field.name) + + if (self.force_colon or + field.cpp_type != descriptor.FieldDescriptor.CPPTYPE_MESSAGE): + # The colon is optional in this case, but our cross-language golden files + # don't include it. Here, the colon is only included if force_colon is + # set to True + out.write(':') + + def PrintField(self, field, value): + """Print a single field name/value pair.""" + self._PrintFieldName(field) + self.out.write(' ') + self.PrintFieldValue(field, value) + self.out.write(' ' if self.as_one_line else '\n') + + def _PrintShortRepeatedPrimitivesValue(self, field, value): + """"Prints short repeated primitives value.""" + # Note: this is called only when value has at least one element. + self._PrintFieldName(field) + self.out.write(' [') + for i in range(len(value) - 1): + self.PrintFieldValue(field, value[i]) + self.out.write(', ') + self.PrintFieldValue(field, value[-1]) + self.out.write(']') + self.out.write(' ' if self.as_one_line else '\n') + + def _PrintMessageFieldValue(self, value): + if self.pointy_brackets: + openb = '<' + closeb = '>' + else: + openb = '{' + closeb = '}' + + if self.as_one_line: + self.out.write('%s ' % openb) + self.PrintMessage(value) + self.out.write(closeb) + else: + self.out.write('%s\n' % openb) + self.indent += 2 + self.PrintMessage(value) + self.indent -= 2 + self.out.write(' ' * self.indent + closeb) + + def PrintFieldValue(self, field, value): + """Print a single field value (not including name). + + For repeated fields, the value should be a single element. + + Args: + field: The descriptor of the field to be printed. + value: The value of the field. + """ + out = self.out + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + self._PrintMessageFieldValue(value) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: + enum_value = field.enum_type.values_by_number.get(value, None) + if enum_value is not None: + out.write(enum_value.name) + else: + out.write(str(value)) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: + out.write('\"') + if isinstance(value, str) and not self.as_utf8: + out_value = value.encode('utf-8') + else: + out_value = value + if field.type == descriptor.FieldDescriptor.TYPE_BYTES: + # We always need to escape all binary data in TYPE_BYTES fields. + out_as_utf8 = False + else: + out_as_utf8 = self.as_utf8 + out.write(text_encoding.CEscape(out_value, out_as_utf8)) + out.write('\"') + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: + if value: + out.write('true') + else: + out.write('false') + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT: + if self.float_format is not None: + out.write('{1:{0}}'.format(self.float_format, value)) + else: + if math.isnan(value): + out.write(str(value)) + else: + out.write(str(type_checkers.ToShortestFloat(value))) + elif (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_DOUBLE and + self.double_format is not None): + out.write('{1:{0}}'.format(self.double_format, value)) + else: + out.write(str(value)) + + +def Parse(text, + message, + allow_unknown_extension=False, + allow_field_number=False, + descriptor_pool=None, + allow_unknown_field=False): + """Parses a text representation of a protocol message into a message. + + NOTE: for historical reasons this function does not clear the input + message. This is different from what the binary msg.ParseFrom(...) does. + If text contains a field already set in message, the value is appended if the + field is repeated. Otherwise, an error is raised. + + Example:: + + a = MyProto() + a.repeated_field.append('test') + b = MyProto() + + # Repeated fields are combined + text_format.Parse(repr(a), b) + text_format.Parse(repr(a), b) # repeated_field contains ["test", "test"] + + # Non-repeated fields cannot be overwritten + a.singular_field = 1 + b.singular_field = 2 + text_format.Parse(repr(a), b) # ParseError + + # Binary version: + b.ParseFromString(a.SerializeToString()) # repeated_field is now "test" + + Caller is responsible for clearing the message as needed. + + Args: + text (str): Message text representation. + message (Message): A protocol buffer message to merge into. + allow_unknown_extension: if True, skip over missing extensions and keep + parsing + allow_field_number: if True, both field number and field name are allowed. + descriptor_pool (DescriptorPool): Descriptor pool used to resolve Any types. + allow_unknown_field: if True, skip over unknown field and keep + parsing. Avoid to use this option if possible. It may hide some + errors (e.g. spelling error on field name) + + Returns: + Message: The same message passed as argument. + + Raises: + ParseError: On text parsing problems. + """ + return ParseLines(text.split(b'\n' if isinstance(text, bytes) else u'\n'), + message, + allow_unknown_extension, + allow_field_number, + descriptor_pool=descriptor_pool, + allow_unknown_field=allow_unknown_field) + + +def Merge(text, + message, + allow_unknown_extension=False, + allow_field_number=False, + descriptor_pool=None, + allow_unknown_field=False): + """Parses a text representation of a protocol message into a message. + + Like Parse(), but allows repeated values for a non-repeated field, and uses + the last one. This means any non-repeated, top-level fields specified in text + replace those in the message. + + Args: + text (str): Message text representation. + message (Message): A protocol buffer message to merge into. + allow_unknown_extension: if True, skip over missing extensions and keep + parsing + allow_field_number: if True, both field number and field name are allowed. + descriptor_pool (DescriptorPool): Descriptor pool used to resolve Any types. + allow_unknown_field: if True, skip over unknown field and keep + parsing. Avoid to use this option if possible. It may hide some + errors (e.g. spelling error on field name) + + Returns: + Message: The same message passed as argument. + + Raises: + ParseError: On text parsing problems. + """ + return MergeLines( + text.split(b'\n' if isinstance(text, bytes) else u'\n'), + message, + allow_unknown_extension, + allow_field_number, + descriptor_pool=descriptor_pool, + allow_unknown_field=allow_unknown_field) + + +def ParseLines(lines, + message, + allow_unknown_extension=False, + allow_field_number=False, + descriptor_pool=None, + allow_unknown_field=False): + """Parses a text representation of a protocol message into a message. + + See Parse() for caveats. + + Args: + lines: An iterable of lines of a message's text representation. + message: A protocol buffer message to merge into. + allow_unknown_extension: if True, skip over missing extensions and keep + parsing + allow_field_number: if True, both field number and field name are allowed. + descriptor_pool: A DescriptorPool used to resolve Any types. + allow_unknown_field: if True, skip over unknown field and keep + parsing. Avoid to use this option if possible. It may hide some + errors (e.g. spelling error on field name) + + Returns: + The same message passed as argument. + + Raises: + ParseError: On text parsing problems. + """ + parser = _Parser(allow_unknown_extension, + allow_field_number, + descriptor_pool=descriptor_pool, + allow_unknown_field=allow_unknown_field) + return parser.ParseLines(lines, message) + + +def MergeLines(lines, + message, + allow_unknown_extension=False, + allow_field_number=False, + descriptor_pool=None, + allow_unknown_field=False): + """Parses a text representation of a protocol message into a message. + + See Merge() for more details. + + Args: + lines: An iterable of lines of a message's text representation. + message: A protocol buffer message to merge into. + allow_unknown_extension: if True, skip over missing extensions and keep + parsing + allow_field_number: if True, both field number and field name are allowed. + descriptor_pool: A DescriptorPool used to resolve Any types. + allow_unknown_field: if True, skip over unknown field and keep + parsing. Avoid to use this option if possible. It may hide some + errors (e.g. spelling error on field name) + + Returns: + The same message passed as argument. + + Raises: + ParseError: On text parsing problems. + """ + parser = _Parser(allow_unknown_extension, + allow_field_number, + descriptor_pool=descriptor_pool, + allow_unknown_field=allow_unknown_field) + return parser.MergeLines(lines, message) + + +class _Parser(object): + """Text format parser for protocol message.""" + + def __init__(self, + allow_unknown_extension=False, + allow_field_number=False, + descriptor_pool=None, + allow_unknown_field=False): + self.allow_unknown_extension = allow_unknown_extension + self.allow_field_number = allow_field_number + self.descriptor_pool = descriptor_pool + self.allow_unknown_field = allow_unknown_field + + def ParseLines(self, lines, message): + """Parses a text representation of a protocol message into a message.""" + self._allow_multiple_scalars = False + self._ParseOrMerge(lines, message) + return message + + def MergeLines(self, lines, message): + """Merges a text representation of a protocol message into a message.""" + self._allow_multiple_scalars = True + self._ParseOrMerge(lines, message) + return message + + def _ParseOrMerge(self, lines, message): + """Converts a text representation of a protocol message into a message. + + Args: + lines: Lines of a message's text representation. + message: A protocol buffer message to merge into. + + Raises: + ParseError: On text parsing problems. + """ + # Tokenize expects native str lines. + str_lines = ( + line if isinstance(line, str) else line.decode('utf-8') + for line in lines) + tokenizer = Tokenizer(str_lines) + while not tokenizer.AtEnd(): + self._MergeField(tokenizer, message) + + def _MergeField(self, tokenizer, message): + """Merges a single protocol message field into a message. + + Args: + tokenizer: A tokenizer to parse the field name and values. + message: A protocol message to record the data. + + Raises: + ParseError: In case of text parsing problems. + """ + message_descriptor = message.DESCRIPTOR + if (message_descriptor.full_name == _ANY_FULL_TYPE_NAME and + tokenizer.TryConsume('[')): + type_url_prefix, packed_type_name = self._ConsumeAnyTypeUrl(tokenizer) + tokenizer.Consume(']') + tokenizer.TryConsume(':') + if tokenizer.TryConsume('<'): + expanded_any_end_token = '>' + else: + tokenizer.Consume('{') + expanded_any_end_token = '}' + expanded_any_sub_message = _BuildMessageFromTypeName(packed_type_name, + self.descriptor_pool) + # Direct comparison with None is used instead of implicit bool conversion + # to avoid false positives with falsy initial values, e.g. for + # google.protobuf.ListValue. + if expanded_any_sub_message is None: + raise ParseError('Type %s not found in descriptor pool' % + packed_type_name) + while not tokenizer.TryConsume(expanded_any_end_token): + if tokenizer.AtEnd(): + raise tokenizer.ParseErrorPreviousToken('Expected "%s".' % + (expanded_any_end_token,)) + self._MergeField(tokenizer, expanded_any_sub_message) + deterministic = False + + message.Pack(expanded_any_sub_message, + type_url_prefix=type_url_prefix, + deterministic=deterministic) + return + + if tokenizer.TryConsume('['): + name = [tokenizer.ConsumeIdentifier()] + while tokenizer.TryConsume('.'): + name.append(tokenizer.ConsumeIdentifier()) + name = '.'.join(name) + + if not message_descriptor.is_extendable: + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" does not have extensions.' % + message_descriptor.full_name) + # pylint: disable=protected-access + field = message.Extensions._FindExtensionByName(name) + # pylint: enable=protected-access + + + if not field: + if self.allow_unknown_extension: + field = None + else: + raise tokenizer.ParseErrorPreviousToken( + 'Extension "%s" not registered. ' + 'Did you import the _pb2 module which defines it? ' + 'If you are trying to place the extension in the MessageSet ' + 'field of another message that is in an Any or MessageSet field, ' + 'that message\'s _pb2 module must be imported as well' % name) + elif message_descriptor != field.containing_type: + raise tokenizer.ParseErrorPreviousToken( + 'Extension "%s" does not extend message type "%s".' % + (name, message_descriptor.full_name)) + + tokenizer.Consume(']') + + else: + name = tokenizer.ConsumeIdentifierOrNumber() + if self.allow_field_number and name.isdigit(): + number = ParseInteger(name, True, True) + field = message_descriptor.fields_by_number.get(number, None) + if not field and message_descriptor.is_extendable: + field = message.Extensions._FindExtensionByNumber(number) + else: + field = message_descriptor.fields_by_name.get(name, None) + + # Group names are expected to be capitalized as they appear in the + # .proto file, which actually matches their type names, not their field + # names. + if not field: + field = message_descriptor.fields_by_name.get(name.lower(), None) + if field and field.type != descriptor.FieldDescriptor.TYPE_GROUP: + field = None + + if (field and field.type == descriptor.FieldDescriptor.TYPE_GROUP and + field.message_type.name != name): + field = None + + if not field and not self.allow_unknown_field: + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" has no field named "%s".' % + (message_descriptor.full_name, name)) + + if field: + if not self._allow_multiple_scalars and field.containing_oneof: + # Check if there's a different field set in this oneof. + # Note that we ignore the case if the same field was set before, and we + # apply _allow_multiple_scalars to non-scalar fields as well. + which_oneof = message.WhichOneof(field.containing_oneof.name) + if which_oneof is not None and which_oneof != field.name: + raise tokenizer.ParseErrorPreviousToken( + 'Field "%s" is specified along with field "%s", another member ' + 'of oneof "%s" for message type "%s".' % + (field.name, which_oneof, field.containing_oneof.name, + message_descriptor.full_name)) + + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + tokenizer.TryConsume(':') + merger = self._MergeMessageField + else: + tokenizer.Consume(':') + merger = self._MergeScalarField + + if (field.label == descriptor.FieldDescriptor.LABEL_REPEATED and + tokenizer.TryConsume('[')): + # Short repeated format, e.g. "foo: [1, 2, 3]" + if not tokenizer.TryConsume(']'): + while True: + merger(tokenizer, message, field) + if tokenizer.TryConsume(']'): + break + tokenizer.Consume(',') + + else: + merger(tokenizer, message, field) + + else: # Proto field is unknown. + assert (self.allow_unknown_extension or self.allow_unknown_field) + _SkipFieldContents(tokenizer) + + # For historical reasons, fields may optionally be separated by commas or + # semicolons. + if not tokenizer.TryConsume(','): + tokenizer.TryConsume(';') + + + def _ConsumeAnyTypeUrl(self, tokenizer): + """Consumes a google.protobuf.Any type URL and returns the type name.""" + # Consume "type.googleapis.com/". + prefix = [tokenizer.ConsumeIdentifier()] + tokenizer.Consume('.') + prefix.append(tokenizer.ConsumeIdentifier()) + tokenizer.Consume('.') + prefix.append(tokenizer.ConsumeIdentifier()) + tokenizer.Consume('/') + # Consume the fully-qualified type name. + name = [tokenizer.ConsumeIdentifier()] + while tokenizer.TryConsume('.'): + name.append(tokenizer.ConsumeIdentifier()) + return '.'.join(prefix), '.'.join(name) + + def _MergeMessageField(self, tokenizer, message, field): + """Merges a single scalar field into a message. + + Args: + tokenizer: A tokenizer to parse the field value. + message: The message of which field is a member. + field: The descriptor of the field to be merged. + + Raises: + ParseError: In case of text parsing problems. + """ + is_map_entry = _IsMapEntry(field) + + if tokenizer.TryConsume('<'): + end_token = '>' + else: + tokenizer.Consume('{') + end_token = '}' + + if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: + if field.is_extension: + sub_message = message.Extensions[field].add() + elif is_map_entry: + sub_message = getattr(message, field.name).GetEntryClass()() + else: + sub_message = getattr(message, field.name).add() + else: + if field.is_extension: + if (not self._allow_multiple_scalars and + message.HasExtension(field)): + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" should not have multiple "%s" extensions.' % + (message.DESCRIPTOR.full_name, field.full_name)) + sub_message = message.Extensions[field] + else: + # Also apply _allow_multiple_scalars to message field. + # TODO(jieluo): Change to _allow_singular_overwrites. + if (not self._allow_multiple_scalars and + message.HasField(field.name)): + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" should not have multiple "%s" fields.' % + (message.DESCRIPTOR.full_name, field.name)) + sub_message = getattr(message, field.name) + sub_message.SetInParent() + + while not tokenizer.TryConsume(end_token): + if tokenizer.AtEnd(): + raise tokenizer.ParseErrorPreviousToken('Expected "%s".' % (end_token,)) + self._MergeField(tokenizer, sub_message) + + if is_map_entry: + value_cpptype = field.message_type.fields_by_name['value'].cpp_type + if value_cpptype == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + value = getattr(message, field.name)[sub_message.key] + value.CopyFrom(sub_message.value) + else: + getattr(message, field.name)[sub_message.key] = sub_message.value + + @staticmethod + def _IsProto3Syntax(message): + message_descriptor = message.DESCRIPTOR + return (hasattr(message_descriptor, 'syntax') and + message_descriptor.syntax == 'proto3') + + def _MergeScalarField(self, tokenizer, message, field): + """Merges a single scalar field into a message. + + Args: + tokenizer: A tokenizer to parse the field value. + message: A protocol message to record the data. + field: The descriptor of the field to be merged. + + Raises: + ParseError: In case of text parsing problems. + RuntimeError: On runtime errors. + """ + _ = self.allow_unknown_extension + value = None + + if field.type in (descriptor.FieldDescriptor.TYPE_INT32, + descriptor.FieldDescriptor.TYPE_SINT32, + descriptor.FieldDescriptor.TYPE_SFIXED32): + value = _ConsumeInt32(tokenizer) + elif field.type in (descriptor.FieldDescriptor.TYPE_INT64, + descriptor.FieldDescriptor.TYPE_SINT64, + descriptor.FieldDescriptor.TYPE_SFIXED64): + value = _ConsumeInt64(tokenizer) + elif field.type in (descriptor.FieldDescriptor.TYPE_UINT32, + descriptor.FieldDescriptor.TYPE_FIXED32): + value = _ConsumeUint32(tokenizer) + elif field.type in (descriptor.FieldDescriptor.TYPE_UINT64, + descriptor.FieldDescriptor.TYPE_FIXED64): + value = _ConsumeUint64(tokenizer) + elif field.type in (descriptor.FieldDescriptor.TYPE_FLOAT, + descriptor.FieldDescriptor.TYPE_DOUBLE): + value = tokenizer.ConsumeFloat() + elif field.type == descriptor.FieldDescriptor.TYPE_BOOL: + value = tokenizer.ConsumeBool() + elif field.type == descriptor.FieldDescriptor.TYPE_STRING: + value = tokenizer.ConsumeString() + elif field.type == descriptor.FieldDescriptor.TYPE_BYTES: + value = tokenizer.ConsumeByteString() + elif field.type == descriptor.FieldDescriptor.TYPE_ENUM: + value = tokenizer.ConsumeEnum(field) + else: + raise RuntimeError('Unknown field type %d' % field.type) + + if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: + if field.is_extension: + message.Extensions[field].append(value) + else: + getattr(message, field.name).append(value) + else: + if field.is_extension: + if (not self._allow_multiple_scalars and + not self._IsProto3Syntax(message) and + message.HasExtension(field)): + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" should not have multiple "%s" extensions.' % + (message.DESCRIPTOR.full_name, field.full_name)) + else: + message.Extensions[field] = value + else: + duplicate_error = False + if not self._allow_multiple_scalars: + if self._IsProto3Syntax(message): + # Proto3 doesn't represent presence so we try best effort to check + # multiple scalars by compare to default values. + duplicate_error = bool(getattr(message, field.name)) + else: + duplicate_error = message.HasField(field.name) + + if duplicate_error: + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" should not have multiple "%s" fields.' % + (message.DESCRIPTOR.full_name, field.name)) + else: + setattr(message, field.name, value) + + +def _SkipFieldContents(tokenizer): + """Skips over contents (value or message) of a field. + + Args: + tokenizer: A tokenizer to parse the field name and values. + """ + # Try to guess the type of this field. + # If this field is not a message, there should be a ":" between the + # field name and the field value and also the field value should not + # start with "{" or "<" which indicates the beginning of a message body. + # If there is no ":" or there is a "{" or "<" after ":", this field has + # to be a message or the input is ill-formed. + if tokenizer.TryConsume( + ':') and not tokenizer.LookingAt('{') and not tokenizer.LookingAt('<'): + if tokenizer.LookingAt('['): + _SkipRepeatedFieldValue(tokenizer) + else: + _SkipFieldValue(tokenizer) + else: + _SkipFieldMessage(tokenizer) + + +def _SkipField(tokenizer): + """Skips over a complete field (name and value/message). + + Args: + tokenizer: A tokenizer to parse the field name and values. + """ + if tokenizer.TryConsume('['): + # Consume extension name. + tokenizer.ConsumeIdentifier() + while tokenizer.TryConsume('.'): + tokenizer.ConsumeIdentifier() + tokenizer.Consume(']') + else: + tokenizer.ConsumeIdentifierOrNumber() + + _SkipFieldContents(tokenizer) + + # For historical reasons, fields may optionally be separated by commas or + # semicolons. + if not tokenizer.TryConsume(','): + tokenizer.TryConsume(';') + + +def _SkipFieldMessage(tokenizer): + """Skips over a field message. + + Args: + tokenizer: A tokenizer to parse the field name and values. + """ + + if tokenizer.TryConsume('<'): + delimiter = '>' + else: + tokenizer.Consume('{') + delimiter = '}' + + while not tokenizer.LookingAt('>') and not tokenizer.LookingAt('}'): + _SkipField(tokenizer) + + tokenizer.Consume(delimiter) + + +def _SkipFieldValue(tokenizer): + """Skips over a field value. + + Args: + tokenizer: A tokenizer to parse the field name and values. + + Raises: + ParseError: In case an invalid field value is found. + """ + # String/bytes tokens can come in multiple adjacent string literals. + # If we can consume one, consume as many as we can. + if tokenizer.TryConsumeByteString(): + while tokenizer.TryConsumeByteString(): + pass + return + + if (not tokenizer.TryConsumeIdentifier() and + not _TryConsumeInt64(tokenizer) and not _TryConsumeUint64(tokenizer) and + not tokenizer.TryConsumeFloat()): + raise ParseError('Invalid field value: ' + tokenizer.token) + + +def _SkipRepeatedFieldValue(tokenizer): + """Skips over a repeated field value. + + Args: + tokenizer: A tokenizer to parse the field value. + """ + tokenizer.Consume('[') + if not tokenizer.LookingAt(']'): + _SkipFieldValue(tokenizer) + while tokenizer.TryConsume(','): + _SkipFieldValue(tokenizer) + tokenizer.Consume(']') + + +class Tokenizer(object): + """Protocol buffer text representation tokenizer. + + This class handles the lower level string parsing by splitting it into + meaningful tokens. + + It was directly ported from the Java protocol buffer API. + """ + + _WHITESPACE = re.compile(r'\s+') + _COMMENT = re.compile(r'(\s*#.*$)', re.MULTILINE) + _WHITESPACE_OR_COMMENT = re.compile(r'(\s|(#.*$))+', re.MULTILINE) + _TOKEN = re.compile('|'.join([ + r'[a-zA-Z_][0-9a-zA-Z_+-]*', # an identifier + r'([0-9+-]|(\.[0-9]))[0-9a-zA-Z_.+-]*', # a number + ] + [ # quoted str for each quote mark + # Avoid backtracking! https://stackoverflow.com/a/844267 + r'{qt}[^{qt}\n\\]*((\\.)+[^{qt}\n\\]*)*({qt}|\\?$)'.format(qt=mark) + for mark in _QUOTES + ])) + + _IDENTIFIER = re.compile(r'[^\d\W]\w*') + _IDENTIFIER_OR_NUMBER = re.compile(r'\w+') + + def __init__(self, lines, skip_comments=True): + self._position = 0 + self._line = -1 + self._column = 0 + self._token_start = None + self.token = '' + self._lines = iter(lines) + self._current_line = '' + self._previous_line = 0 + self._previous_column = 0 + self._more_lines = True + self._skip_comments = skip_comments + self._whitespace_pattern = (skip_comments and self._WHITESPACE_OR_COMMENT + or self._WHITESPACE) + self._SkipWhitespace() + self.NextToken() + + def LookingAt(self, token): + return self.token == token + + def AtEnd(self): + """Checks the end of the text was reached. + + Returns: + True iff the end was reached. + """ + return not self.token + + def _PopLine(self): + while len(self._current_line) <= self._column: + try: + self._current_line = next(self._lines) + except StopIteration: + self._current_line = '' + self._more_lines = False + return + else: + self._line += 1 + self._column = 0 + + def _SkipWhitespace(self): + while True: + self._PopLine() + match = self._whitespace_pattern.match(self._current_line, self._column) + if not match: + break + length = len(match.group(0)) + self._column += length + + def TryConsume(self, token): + """Tries to consume a given piece of text. + + Args: + token: Text to consume. + + Returns: + True iff the text was consumed. + """ + if self.token == token: + self.NextToken() + return True + return False + + def Consume(self, token): + """Consumes a piece of text. + + Args: + token: Text to consume. + + Raises: + ParseError: If the text couldn't be consumed. + """ + if not self.TryConsume(token): + raise self.ParseError('Expected "%s".' % token) + + def ConsumeComment(self): + result = self.token + if not self._COMMENT.match(result): + raise self.ParseError('Expected comment.') + self.NextToken() + return result + + def ConsumeCommentOrTrailingComment(self): + """Consumes a comment, returns a 2-tuple (trailing bool, comment str).""" + + # Tokenizer initializes _previous_line and _previous_column to 0. As the + # tokenizer starts, it looks like there is a previous token on the line. + just_started = self._line == 0 and self._column == 0 + + before_parsing = self._previous_line + comment = self.ConsumeComment() + + # A trailing comment is a comment on the same line than the previous token. + trailing = (self._previous_line == before_parsing + and not just_started) + + return trailing, comment + + def TryConsumeIdentifier(self): + try: + self.ConsumeIdentifier() + return True + except ParseError: + return False + + def ConsumeIdentifier(self): + """Consumes protocol message field identifier. + + Returns: + Identifier string. + + Raises: + ParseError: If an identifier couldn't be consumed. + """ + result = self.token + if not self._IDENTIFIER.match(result): + raise self.ParseError('Expected identifier.') + self.NextToken() + return result + + def TryConsumeIdentifierOrNumber(self): + try: + self.ConsumeIdentifierOrNumber() + return True + except ParseError: + return False + + def ConsumeIdentifierOrNumber(self): + """Consumes protocol message field identifier. + + Returns: + Identifier string. + + Raises: + ParseError: If an identifier couldn't be consumed. + """ + result = self.token + if not self._IDENTIFIER_OR_NUMBER.match(result): + raise self.ParseError('Expected identifier or number, got %s.' % result) + self.NextToken() + return result + + def TryConsumeInteger(self): + try: + self.ConsumeInteger() + return True + except ParseError: + return False + + def ConsumeInteger(self): + """Consumes an integer number. + + Returns: + The integer parsed. + + Raises: + ParseError: If an integer couldn't be consumed. + """ + try: + result = _ParseAbstractInteger(self.token) + except ValueError as e: + raise self.ParseError(str(e)) + self.NextToken() + return result + + def TryConsumeFloat(self): + try: + self.ConsumeFloat() + return True + except ParseError: + return False + + def ConsumeFloat(self): + """Consumes an floating point number. + + Returns: + The number parsed. + + Raises: + ParseError: If a floating point number couldn't be consumed. + """ + try: + result = ParseFloat(self.token) + except ValueError as e: + raise self.ParseError(str(e)) + self.NextToken() + return result + + def ConsumeBool(self): + """Consumes a boolean value. + + Returns: + The bool parsed. + + Raises: + ParseError: If a boolean value couldn't be consumed. + """ + try: + result = ParseBool(self.token) + except ValueError as e: + raise self.ParseError(str(e)) + self.NextToken() + return result + + def TryConsumeByteString(self): + try: + self.ConsumeByteString() + return True + except ParseError: + return False + + def ConsumeString(self): + """Consumes a string value. + + Returns: + The string parsed. + + Raises: + ParseError: If a string value couldn't be consumed. + """ + the_bytes = self.ConsumeByteString() + try: + return str(the_bytes, 'utf-8') + except UnicodeDecodeError as e: + raise self._StringParseError(e) + + def ConsumeByteString(self): + """Consumes a byte array value. + + Returns: + The array parsed (as a string). + + Raises: + ParseError: If a byte array value couldn't be consumed. + """ + the_list = [self._ConsumeSingleByteString()] + while self.token and self.token[0] in _QUOTES: + the_list.append(self._ConsumeSingleByteString()) + return b''.join(the_list) + + def _ConsumeSingleByteString(self): + """Consume one token of a string literal. + + String literals (whether bytes or text) can come in multiple adjacent + tokens which are automatically concatenated, like in C or Python. This + method only consumes one token. + + Returns: + The token parsed. + Raises: + ParseError: When the wrong format data is found. + """ + text = self.token + if len(text) < 1 or text[0] not in _QUOTES: + raise self.ParseError('Expected string but found: %r' % (text,)) + + if len(text) < 2 or text[-1] != text[0]: + raise self.ParseError('String missing ending quote: %r' % (text,)) + + try: + result = text_encoding.CUnescape(text[1:-1]) + except ValueError as e: + raise self.ParseError(str(e)) + self.NextToken() + return result + + def ConsumeEnum(self, field): + try: + result = ParseEnum(field, self.token) + except ValueError as e: + raise self.ParseError(str(e)) + self.NextToken() + return result + + def ParseErrorPreviousToken(self, message): + """Creates and *returns* a ParseError for the previously read token. + + Args: + message: A message to set for the exception. + + Returns: + A ParseError instance. + """ + return ParseError(message, self._previous_line + 1, + self._previous_column + 1) + + def ParseError(self, message): + """Creates and *returns* a ParseError for the current token.""" + return ParseError('\'' + self._current_line + '\': ' + message, + self._line + 1, self._column + 1) + + def _StringParseError(self, e): + return self.ParseError('Couldn\'t parse string: ' + str(e)) + + def NextToken(self): + """Reads the next meaningful token.""" + self._previous_line = self._line + self._previous_column = self._column + + self._column += len(self.token) + self._SkipWhitespace() + + if not self._more_lines: + self.token = '' + return + + match = self._TOKEN.match(self._current_line, self._column) + if not match and not self._skip_comments: + match = self._COMMENT.match(self._current_line, self._column) + if match: + token = match.group(0) + self.token = token + else: + self.token = self._current_line[self._column] + +# Aliased so it can still be accessed by current visibility violators. +# TODO(dbarnett): Migrate violators to textformat_tokenizer. +_Tokenizer = Tokenizer # pylint: disable=invalid-name + + +def _ConsumeInt32(tokenizer): + """Consumes a signed 32bit integer number from tokenizer. + + Args: + tokenizer: A tokenizer used to parse the number. + + Returns: + The integer parsed. + + Raises: + ParseError: If a signed 32bit integer couldn't be consumed. + """ + return _ConsumeInteger(tokenizer, is_signed=True, is_long=False) + + +def _ConsumeUint32(tokenizer): + """Consumes an unsigned 32bit integer number from tokenizer. + + Args: + tokenizer: A tokenizer used to parse the number. + + Returns: + The integer parsed. + + Raises: + ParseError: If an unsigned 32bit integer couldn't be consumed. + """ + return _ConsumeInteger(tokenizer, is_signed=False, is_long=False) + + +def _TryConsumeInt64(tokenizer): + try: + _ConsumeInt64(tokenizer) + return True + except ParseError: + return False + + +def _ConsumeInt64(tokenizer): + """Consumes a signed 32bit integer number from tokenizer. + + Args: + tokenizer: A tokenizer used to parse the number. + + Returns: + The integer parsed. + + Raises: + ParseError: If a signed 32bit integer couldn't be consumed. + """ + return _ConsumeInteger(tokenizer, is_signed=True, is_long=True) + + +def _TryConsumeUint64(tokenizer): + try: + _ConsumeUint64(tokenizer) + return True + except ParseError: + return False + + +def _ConsumeUint64(tokenizer): + """Consumes an unsigned 64bit integer number from tokenizer. + + Args: + tokenizer: A tokenizer used to parse the number. + + Returns: + The integer parsed. + + Raises: + ParseError: If an unsigned 64bit integer couldn't be consumed. + """ + return _ConsumeInteger(tokenizer, is_signed=False, is_long=True) + + +def _ConsumeInteger(tokenizer, is_signed=False, is_long=False): + """Consumes an integer number from tokenizer. + + Args: + tokenizer: A tokenizer used to parse the number. + is_signed: True if a signed integer must be parsed. + is_long: True if a long integer must be parsed. + + Returns: + The integer parsed. + + Raises: + ParseError: If an integer with given characteristics couldn't be consumed. + """ + try: + result = ParseInteger(tokenizer.token, is_signed=is_signed, is_long=is_long) + except ValueError as e: + raise tokenizer.ParseError(str(e)) + tokenizer.NextToken() + return result + + +def ParseInteger(text, is_signed=False, is_long=False): + """Parses an integer. + + Args: + text: The text to parse. + is_signed: True if a signed integer must be parsed. + is_long: True if a long integer must be parsed. + + Returns: + The integer value. + + Raises: + ValueError: Thrown Iff the text is not a valid integer. + """ + # Do the actual parsing. Exception handling is propagated to caller. + result = _ParseAbstractInteger(text) + + # Check if the integer is sane. Exceptions handled by callers. + checker = _INTEGER_CHECKERS[2 * int(is_long) + int(is_signed)] + checker.CheckValue(result) + return result + + +def _ParseAbstractInteger(text): + """Parses an integer without checking size/signedness. + + Args: + text: The text to parse. + + Returns: + The integer value. + + Raises: + ValueError: Thrown Iff the text is not a valid integer. + """ + # Do the actual parsing. Exception handling is propagated to caller. + orig_text = text + c_octal_match = re.match(r'(-?)0(\d+)$', text) + if c_octal_match: + # Python 3 no longer supports 0755 octal syntax without the 'o', so + # we always use the '0o' prefix for multi-digit numbers starting with 0. + text = c_octal_match.group(1) + '0o' + c_octal_match.group(2) + try: + return int(text, 0) + except ValueError: + raise ValueError('Couldn\'t parse integer: %s' % orig_text) + + +def ParseFloat(text): + """Parse a floating point number. + + Args: + text: Text to parse. + + Returns: + The number parsed. + + Raises: + ValueError: If a floating point number couldn't be parsed. + """ + try: + # Assume Python compatible syntax. + return float(text) + except ValueError: + # Check alternative spellings. + if _FLOAT_INFINITY.match(text): + if text[0] == '-': + return float('-inf') + else: + return float('inf') + elif _FLOAT_NAN.match(text): + return float('nan') + else: + # assume '1.0f' format + try: + return float(text.rstrip('f')) + except ValueError: + raise ValueError('Couldn\'t parse float: %s' % text) + + +def ParseBool(text): + """Parse a boolean value. + + Args: + text: Text to parse. + + Returns: + Boolean values parsed + + Raises: + ValueError: If text is not a valid boolean. + """ + if text in ('true', 't', '1', 'True'): + return True + elif text in ('false', 'f', '0', 'False'): + return False + else: + raise ValueError('Expected "true" or "false".') + + +def ParseEnum(field, value): + """Parse an enum value. + + The value can be specified by a number (the enum value), or by + a string literal (the enum name). + + Args: + field: Enum field descriptor. + value: String value. + + Returns: + Enum value number. + + Raises: + ValueError: If the enum value could not be parsed. + """ + enum_descriptor = field.enum_type + try: + number = int(value, 0) + except ValueError: + # Identifier. + enum_value = enum_descriptor.values_by_name.get(value, None) + if enum_value is None: + raise ValueError('Enum type "%s" has no value named %s.' % + (enum_descriptor.full_name, value)) + else: + # Numeric value. + if hasattr(field.file, 'syntax'): + # Attribute is checked for compatibility. + if field.file.syntax == 'proto3': + # Proto3 accept numeric unknown enums. + return number + enum_value = enum_descriptor.values_by_number.get(number, None) + if enum_value is None: + raise ValueError('Enum type "%s" has no value with number %d.' % + (enum_descriptor.full_name, number)) + return enum_value.number diff --git a/virt/lib/python3.9/site-packages/mysql/connector/abstracts 3.py b/virt/lib/python3.9/site-packages/mysql/connector/abstracts 3.py new file mode 100644 index 00000000..a7ce1b22 --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysql/connector/abstracts 3.py @@ -0,0 +1,1806 @@ +# Copyright (c) 2014, 2023, Oracle and/or its affiliates. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, as +# published by the Free Software Foundation. +# +# This program is also distributed with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an +# additional permission to link the program and your derivative works +# with the separately licensed software that they have included with +# MySQL. +# +# Without limiting anything contained in the foregoing, this file, +# which is part of MySQL Connector/Python, is also subject to the +# Universal FOSS Exception, version 1.0, a copy of which can be found at +# http://oss.oracle.com/licenses/universal-foss-exception. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License, version 2.0, for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# mypy: disable-error-code="assignment,attr-defined" + +"""Module gathering all abstract base classes.""" + +from __future__ import annotations + +import importlib +import os +import re +import weakref + +from abc import ABC, abstractmethod +from datetime import date, datetime, time, timedelta +from decimal import Decimal +from inspect import signature +from time import sleep +from types import TracebackType +from typing import ( + Any, + BinaryIO, + Callable, + Dict, + Generator, + List, + Mapping, + Optional, + Sequence, + Tuple, + Type, + Union, +) + +TLS_V1_3_SUPPORTED = False +try: + import ssl + + if hasattr(ssl, "HAS_TLSv1_3") and ssl.HAS_TLSv1_3: + TLS_V1_3_SUPPORTED = True +except ImportError: + # If import fails, we don't have SSL support. + pass + +from .constants import ( + CONN_ATTRS_DN, + DEFAULT_CONFIGURATION, + DEPRECATED_TLS_VERSIONS, + OPENSSL_CS_NAMES, + TLS_CIPHER_SUITES, + TLS_VERSIONS, + CharacterSet, + ClientFlag, +) +from .conversion import MySQLConverter, MySQLConverterBase +from .errors import ( + Error, + InterfaceError, + NotSupportedError, + OperationalError, + ProgrammingError, +) +from .opentelemetry.constants import ( + CONNECTION_SPAN_NAME, + OPTION_CNX_SPAN, + OPTION_CNX_TRACER, + OTEL_ENABLED, +) + +if OTEL_ENABLED: + from .opentelemetry.instrumentation import ( + end_span, + record_exception_event, + set_connection_span_attrs, + trace, + ) + +from .optionfiles import read_option_files +from .types import ( + ConnAttrsType, + DescriptionType, + HandShakeType, + StrOrBytes, + SupportedMysqlBinaryProtocolTypes, + WarningType, +) + +NAMED_TUPLE_CACHE: weakref.WeakValueDictionary[Any, Any] = weakref.WeakValueDictionary() + +DUPLICATED_IN_LIST_ERROR = ( + "The '{list}' list must not contain repeated values, the value " + "'{value}' is duplicated." +) + +TLS_VERSION_ERROR = ( + "The given tls_version: '{}' is not recognized as a valid " + "TLS protocol version (should be one of {})." +) + +TLS_VERSION_DEPRECATED_ERROR = ( + "The given tls_version: '{}' are no longer allowed (should be one of {})." +) + +TLS_VER_NO_SUPPORTED = ( + "No supported TLS protocol version found in the 'tls-versions' list '{}'. " +) + +KRB_SERVICE_PINCIPAL_ERROR = ( + 'Option "krb_service_principal" {error}, must be a string in the form ' + '"primary/instance@realm" e.g "ldap/ldapauth@MYSQL.COM" where "@realm" ' + "is optional and if it is not given will be assumed to belong to the " + "default realm, as configured in the krb5.conf file." +) + +MYSQL_PY_TYPES = ( + Decimal, + bytes, + date, + datetime, + float, + int, + str, + time, + timedelta, +) + + +class MySQLConnectionAbstract(ABC): + """Abstract class for classes connecting to a MySQL server""" + + def __init__(self) -> None: + """Initialize""" + # opentelemetry related + self._tracer: Any = None + self._span: Any = None + self.otel_context_propagation: bool = True + + self._client_flags: int = ClientFlag.get_default() + self._charset_id: int = 45 + self._sql_mode: Optional[str] = None + self._time_zone: Optional[str] = None + self._autocommit: bool = False + self._server_version: Optional[Tuple[int, ...]] = None + self._handshake: Optional[HandShakeType] = None + self._conn_attrs: ConnAttrsType = {} + + self._user: str = "" + self._password: str = "" + self._password1: str = "" + self._password2: str = "" + self._password3: str = "" + self._database: str = "" + self._host: str = "127.0.0.1" + self._port: int = 3306 + self._unix_socket: Optional[str] = None + self._client_host: str = "" + self._client_port: int = 0 + self._ssl: Dict[str, Optional[Union[str, bool, List[str]]]] = {} + self._ssl_disabled: bool = DEFAULT_CONFIGURATION["ssl_disabled"] + self._force_ipv6: bool = False + self._oci_config_file: Optional[str] = None + self._oci_config_profile: Optional[str] = None + self._fido_callback: Optional[Union[str, Callable]] = None + self._krb_service_principal: Optional[str] = None + + self._use_unicode: bool = True + self._get_warnings: bool = False + self._raise_on_warnings: bool = False + self._connection_timeout: Optional[int] = DEFAULT_CONFIGURATION[ + "connect_timeout" + ] + self._buffered: bool = False + self._unread_result: bool = False + self._have_next_result: bool = False + self._raw: bool = False + self._in_transaction: bool = False + self._allow_local_infile: bool = DEFAULT_CONFIGURATION["allow_local_infile"] + self._allow_local_infile_in_path: Optional[str] = DEFAULT_CONFIGURATION[ + "allow_local_infile_in_path" + ] + + self._prepared_statements: Any = None + self._query_attrs: Dict[str, Any] = {} + + self._ssl_active: bool = False + self._auth_plugin: Optional[str] = None + self._auth_plugin_class: Optional[str] = None + self._pool_config_version: Any = None + self.converter: Optional[MySQLConverter] = None + self._converter_class: Optional[Type[MySQLConverter]] = None + self._converter_str_fallback: bool = False + self._compress: bool = False + + self._consume_results: bool = False + self._init_command: Optional[str] = None + + def __enter__(self) -> MySQLConnectionAbstract: + return self + + def __exit__( + self, + exc_type: Type[BaseException], + exc_value: BaseException, + traceback: TracebackType, + ) -> None: + self.close() + + def get_self(self) -> MySQLConnectionAbstract: + """Return self for weakref.proxy + + This method is used when the original object is needed when using + weakref.proxy. + """ + return self + + @property + def is_secure(self) -> bool: + """Return True if is a secure connection.""" + return self._ssl_active or ( + self._unix_socket is not None and os.name == "posix" + ) + + @property + def have_next_result(self) -> bool: + """Return if have next result.""" + return self._have_next_result + + @property + def query_attrs(self) -> List[Tuple[str, Any]]: + """Return query attributes list.""" + return list(self._query_attrs.items()) + + def query_attrs_append( + self, value: Tuple[str, SupportedMysqlBinaryProtocolTypes] + ) -> None: + """Add element to the query attributes list. + + If an element in the query attributes list already matches + the attribute name provided, the new element will NOT be added. + """ + attr_name, attr_value = value + if attr_name not in self._query_attrs: + self._query_attrs[attr_name] = attr_value + + def query_attrs_remove(self, name: str) -> Any: + """Remove element by name from the query attributes list. + + If no match, `None` is returned; else the corresponding value is returned. + """ + return self._query_attrs.pop(name, None) + + def query_attrs_clear(self) -> None: + """Clear query attributes list.""" + self._query_attrs = {} + + def _validate_tls_ciphersuites(self) -> None: + """Validates the tls_ciphersuites option.""" + tls_ciphersuites = [] + tls_cs = self._ssl["tls_ciphersuites"] + + if isinstance(tls_cs, str): + if not (tls_cs.startswith("[") and tls_cs.endswith("]")): + raise AttributeError( + f"tls_ciphersuites must be a list, found: '{tls_cs}'" + ) + tls_css = tls_cs[1:-1].split(",") + if not tls_css: + raise AttributeError( + "No valid cipher suite found in 'tls_ciphersuites' list" + ) + for _tls_cs in tls_css: + _tls_cs = tls_cs.strip().upper() + if _tls_cs: + tls_ciphersuites.append(_tls_cs) + + elif isinstance(tls_cs, (list, set)): + tls_ciphersuites = [tls_cs for tls_cs in tls_cs if tls_cs] + else: + raise AttributeError( + "tls_ciphersuites should be a list with one or more " + f"ciphersuites. Found: '{tls_cs}'" + ) + + tls_versions = ( + TLS_VERSIONS[:] + if self._ssl.get("tls_versions", None) is None + else self._ssl["tls_versions"][:] # type: ignore[index] + ) + + # A newer TLS version can use a cipher introduced on + # an older version. + tls_versions.sort(reverse=True) # type: ignore[union-attr] + newer_tls_ver = tls_versions[0] + # translated_names[0] belongs to TLSv1, TLSv1.1 and TLSv1.2 + # translated_names[1] are TLSv1.3 only + translated_names: List[List[str]] = [[], []] + iani_cipher_suites_names = {} + ossl_cipher_suites_names: List[str] = [] + + # Old ciphers can work with new TLS versions. + # Find all the ciphers introduced on previous TLS versions. + for tls_ver in TLS_VERSIONS[: TLS_VERSIONS.index(newer_tls_ver) + 1]: + iani_cipher_suites_names.update(TLS_CIPHER_SUITES[tls_ver]) + ossl_cipher_suites_names.extend(OPENSSL_CS_NAMES[tls_ver]) + + for name in tls_ciphersuites: + if "-" in name and name in ossl_cipher_suites_names: + if name in OPENSSL_CS_NAMES["TLSv1.3"]: + translated_names[1].append(name) + else: + translated_names[0].append(name) + elif name in iani_cipher_suites_names: + translated_name = iani_cipher_suites_names[name] + if translated_name in translated_names: + raise AttributeError( + DUPLICATED_IN_LIST_ERROR.format( + list="tls_ciphersuites", value=translated_name + ) + ) + if name in TLS_CIPHER_SUITES["TLSv1.3"]: + translated_names[1].append(iani_cipher_suites_names[name]) + else: + translated_names[0].append(iani_cipher_suites_names[name]) + else: + raise AttributeError( + f"The value '{name}' in tls_ciphersuites is not a valid " + "cipher suite" + ) + if not translated_names[0] and not translated_names[1]: + raise AttributeError( + "No valid cipher suite found in the 'tls_ciphersuites' list" + ) + + self._ssl["tls_ciphersuites"] = [ + ":".join(translated_names[0]), + ":".join(translated_names[1]), + ] + + def _validate_tls_versions(self) -> None: + """Validates the tls_versions option.""" + tls_versions = [] + tls_version = self._ssl["tls_versions"] + + if isinstance(tls_version, str): + if not (tls_version.startswith("[") and tls_version.endswith("]")): + raise AttributeError( + f"tls_versions must be a list, found: '{tls_version}'" + ) + tls_vers = tls_version[1:-1].split(",") + for tls_ver in tls_vers: + tls_version = tls_ver.strip() + if tls_version == "": + continue + if tls_version in tls_versions: + raise AttributeError( + DUPLICATED_IN_LIST_ERROR.format( + list="tls_versions", value=tls_version + ) + ) + tls_versions.append(tls_version) + if tls_vers == ["TLSv1.3"] and not TLS_V1_3_SUPPORTED: + raise AttributeError( + TLS_VER_NO_SUPPORTED.format(tls_version, TLS_VERSIONS) + ) + elif isinstance(tls_version, list): + if not tls_version: + raise AttributeError( + "At least one TLS protocol version must be specified in " + "'tls_versions' list" + ) + for tls_ver in tls_version: + if tls_ver in tls_versions: + raise AttributeError( + DUPLICATED_IN_LIST_ERROR.format( + list="tls_versions", value=tls_ver + ) + ) + tls_versions.append(tls_ver) + elif isinstance(tls_version, set): + for tls_ver in tls_version: + tls_versions.append(tls_ver) + else: + raise AttributeError( + "tls_versions should be a list with one or more of versions " + f"in {', '.join(TLS_VERSIONS)}. found: '{tls_versions}'" + ) + + if not tls_versions: + raise AttributeError( + "At least one TLS protocol version must be specified " + "in 'tls_versions' list when this option is given" + ) + + use_tls_versions = [] + deprecated_tls_versions = [] + invalid_tls_versions = [] + for tls_ver in tls_versions: + if tls_ver in TLS_VERSIONS: + use_tls_versions.append(tls_ver) + if tls_ver in DEPRECATED_TLS_VERSIONS: + deprecated_tls_versions.append(tls_ver) + else: + invalid_tls_versions.append(tls_ver) + + if use_tls_versions: + if use_tls_versions == ["TLSv1.3"] and not TLS_V1_3_SUPPORTED: + raise NotSupportedError( + TLS_VER_NO_SUPPORTED.format(tls_version, TLS_VERSIONS) + ) + use_tls_versions.sort() + self._ssl["tls_versions"] = use_tls_versions + elif deprecated_tls_versions: + raise NotSupportedError( + TLS_VERSION_DEPRECATED_ERROR.format( + deprecated_tls_versions, TLS_VERSIONS + ) + ) + elif invalid_tls_versions: + raise AttributeError(TLS_VERSION_ERROR.format(tls_ver, TLS_VERSIONS)) + + @property + def user(self) -> str: + """User used while connecting to MySQL""" + return self._user + + @property + def server_host(self) -> str: + """MySQL server IP address or name""" + return self._host + + @property + def server_port(self) -> int: + "MySQL server TCP/IP port" + return self._port + + @property + def unix_socket(self) -> Optional[str]: + "MySQL Unix socket file location" + return self._unix_socket + + @property + @abstractmethod + def database(self) -> str: + """Get the current database""" + + @database.setter + def database(self, value: str) -> None: + """Set the current database""" + self.cmd_query(f"USE {value}") + + @property + def can_consume_results(self) -> bool: + """Returns whether to consume results""" + return self._consume_results + + @can_consume_results.setter + def can_consume_results(self, value: bool) -> None: + """Set if can consume results.""" + assert isinstance(value, bool) + self._consume_results = value + + @property + def pool_config_version(self) -> Any: + """Return the pool configuration version""" + return self._pool_config_version + + @pool_config_version.setter + def pool_config_version(self, value: Any) -> None: + """Set the pool configuration version""" + self._pool_config_version = value + + def config(self, **kwargs: Any) -> None: + """Configure the MySQL Connection + + This method allows you to configure the MySQLConnection instance. + + Raises on errors. + """ + # opentelemetry related + self._span = kwargs.pop(OPTION_CNX_SPAN, None) + self._tracer = kwargs.pop(OPTION_CNX_TRACER, None) + + config = kwargs.copy() + if "dsn" in config: + raise NotSupportedError("Data source name is not supported") + + # Read option files + config = read_option_files(**config) + + # Configure how we handle MySQL warnings + try: + self.get_warnings = config["get_warnings"] + del config["get_warnings"] + except KeyError: + pass # Leave what was set or default + try: + self.raise_on_warnings = config["raise_on_warnings"] + del config["raise_on_warnings"] + except KeyError: + pass # Leave what was set or default + + # Configure client flags + try: + default = ClientFlag.get_default() + self.set_client_flags(config["client_flags"] or default) + del config["client_flags"] + except KeyError: + pass # Missing client_flags-argument is OK + + try: + if config["compress"]: + self._compress = True + self.set_client_flags([ClientFlag.COMPRESS]) + except KeyError: + pass # Missing compress argument is OK + + self._allow_local_infile = config.get( + "allow_local_infile", DEFAULT_CONFIGURATION["allow_local_infile"] + ) + self._allow_local_infile_in_path = config.get( + "allow_local_infile_in_path", + DEFAULT_CONFIGURATION["allow_local_infile_in_path"], + ) + infile_in_path = None + if self._allow_local_infile_in_path: + infile_in_path = os.path.abspath(self._allow_local_infile_in_path) + if ( + infile_in_path + and os.path.exists(infile_in_path) + and not os.path.isdir(infile_in_path) + or os.path.islink(infile_in_path) + ): + raise AttributeError("allow_local_infile_in_path must be a directory") + if self._allow_local_infile or self._allow_local_infile_in_path: + self.set_client_flags([ClientFlag.LOCAL_FILES]) + else: + self.set_client_flags([-ClientFlag.LOCAL_FILES]) + + try: + if not config["consume_results"]: + self._consume_results = False + else: + self._consume_results = True + except KeyError: + self._consume_results = False + + # Configure auth_plugin + try: + self._auth_plugin = config["auth_plugin"] + del config["auth_plugin"] + except KeyError: + self._auth_plugin = "" + + # Configure character set and collation + if "charset" in config or "collation" in config: + try: + charset = config["charset"] + del config["charset"] + except KeyError: + charset = None + try: + collation = config["collation"] + del config["collation"] + except KeyError: + collation = None + self._charset_id = CharacterSet.get_charset_info(charset, collation)[0] + + # Set converter class + try: + self.set_converter_class(config["converter_class"]) + except KeyError: + pass # Using default converter class + except TypeError as err: + raise AttributeError( + "Converter class should be a subclass of " + "conversion.MySQLConverterBase" + ) from err + + # Compatible configuration with other drivers + compat_map = [ + # (<other driver argument>,<translates to>) + ("db", "database"), + ("username", "user"), + ("passwd", "password"), + ("connect_timeout", "connection_timeout"), + ("read_default_file", "option_files"), + ] + for compat, translate in compat_map: + try: + if translate not in config: + config[translate] = config[compat] + del config[compat] + except KeyError: + pass # Missing compat argument is OK + + # Configure login information + if "user" in config or "password" in config: + try: + user = config["user"] + del config["user"] + except KeyError: + user = self._user + try: + password = config["password"] + del config["password"] + except KeyError: + password = self._password + self.set_login(user, password) + + # Configure host information + if "host" in config and config["host"]: + self._host = config["host"] + + # Check network locations + try: + self._port = int(config["port"]) + del config["port"] + except KeyError: + pass # Missing port argument is OK + except ValueError as err: + raise InterfaceError("TCP/IP port number should be an integer") from err + + if "ssl_disabled" in config: + self._ssl_disabled = config.pop("ssl_disabled") + + # If an init_command is set, keep it, so we can execute it in _post_connection + if "init_command" in config: + self._init_command = config["init_command"] + del config["init_command"] + + # Other configuration + set_ssl_flag = False + for key, value in config.items(): + try: + DEFAULT_CONFIGURATION[key] + except KeyError: + raise AttributeError(f"Unsupported argument '{key}'") from None + # SSL Configuration + if key.startswith("ssl_"): + set_ssl_flag = True + self._ssl.update({key.replace("ssl_", ""): value}) + elif key.startswith("tls_"): + set_ssl_flag = True + self._ssl.update({key: value}) + else: + attribute = "_" + key + try: + setattr(self, attribute, value.strip()) + except AttributeError: + setattr(self, attribute, value) + + # Disable SSL for unix socket connections + if self._unix_socket and os.name == "posix": + self._ssl_disabled = True + + if self._ssl_disabled and self._auth_plugin == "mysql_clear_password": + raise InterfaceError( + "Clear password authentication is not supported over insecure channels" + ) + + if set_ssl_flag: + if "verify_cert" not in self._ssl: + self._ssl["verify_cert"] = DEFAULT_CONFIGURATION["ssl_verify_cert"] + if "verify_identity" not in self._ssl: + self._ssl["verify_identity"] = DEFAULT_CONFIGURATION[ + "ssl_verify_identity" + ] + # Make sure both ssl_key/ssl_cert are set, or neither (XOR) + if "ca" not in self._ssl or self._ssl["ca"] is None: + self._ssl["ca"] = "" + if bool("key" in self._ssl) != bool("cert" in self._ssl): + raise AttributeError( + "ssl_key and ssl_cert need to be both specified, or neither" + ) + # Make sure key/cert are set to None + if not set(("key", "cert")) <= set(self._ssl): + self._ssl["key"] = None + self._ssl["cert"] = None + elif (self._ssl["key"] is None) != (self._ssl["cert"] is None): + raise AttributeError( + "ssl_key and ssl_cert need to be both set, or neither" + ) + if "tls_versions" in self._ssl and self._ssl["tls_versions"] is not None: + self._validate_tls_versions() + + if ( + "tls_ciphersuites" in self._ssl + and self._ssl["tls_ciphersuites"] is not None + ): + self._validate_tls_ciphersuites() + + if self._conn_attrs is None: + self._conn_attrs = {} + elif not isinstance(self._conn_attrs, dict): + raise InterfaceError("conn_attrs must be of type dict") + else: + for attr_name, attr_value in self._conn_attrs.items(): + if attr_name in CONN_ATTRS_DN: + continue + # Validate name type + if not isinstance(attr_name, str): + raise InterfaceError( + "Attribute name should be a string, found: " + f"'{attr_name}' in '{self._conn_attrs}'" + ) + # Validate attribute name limit 32 characters + if len(attr_name) > 32: + raise InterfaceError( + f"Attribute name '{attr_name}' exceeds 32 characters limit size" + ) + # Validate names in connection attributes cannot start with "_" + if attr_name.startswith("_"): + raise InterfaceError( + "Key names in connection attributes cannot start with " + "'_', found: '{attr_name}'" + ) + # Validate value type + if not isinstance(attr_value, str): + raise InterfaceError( + f"Attribute '{attr_name}' value: '{attr_value}' must " + "be a string type" + ) + # Validate attribute value limit 1024 characters + if len(attr_value) > 1024: + raise InterfaceError( + f"Attribute '{attr_name}' value: '{attr_value}' " + "exceeds 1024 characters limit size" + ) + + if self._client_flags & ClientFlag.CONNECT_ARGS: + self._add_default_conn_attrs() + + if "kerberos_auth_mode" in config and config["kerberos_auth_mode"] is not None: + if not isinstance(config["kerberos_auth_mode"], str): + raise InterfaceError("'kerberos_auth_mode' must be of type str") + kerberos_auth_mode = config["kerberos_auth_mode"].lower() + if kerberos_auth_mode == "sspi": + if os.name != "nt": + raise InterfaceError( + "'kerberos_auth_mode=SSPI' is only available on Windows" + ) + self._auth_plugin_class = "MySQLSSPIKerberosAuthPlugin" + elif kerberos_auth_mode == "gssapi": + self._auth_plugin_class = "MySQLKerberosAuthPlugin" + else: + raise InterfaceError( + "Invalid 'kerberos_auth_mode' mode. Please use 'SSPI' or 'GSSAPI'" + ) + + if ( + "krb_service_principal" in config + and config["krb_service_principal"] is not None + ): + self._krb_service_principal = config["krb_service_principal"] + if not isinstance(self._krb_service_principal, str): + raise InterfaceError( + KRB_SERVICE_PINCIPAL_ERROR.format(error="is not a string") + ) + if self._krb_service_principal == "": + raise InterfaceError( + KRB_SERVICE_PINCIPAL_ERROR.format( + error="can not be an empty string" + ) + ) + if "/" not in self._krb_service_principal: + raise InterfaceError( + KRB_SERVICE_PINCIPAL_ERROR.format(error="is incorrectly formatted") + ) + + if self._fido_callback: + # Import the callable if it's a str + if isinstance(self._fido_callback, str): + try: + module, callback = self._fido_callback.rsplit(".", 1) + except ValueError: + raise ProgrammingError( + f"No callable named '{self._fido_callback}'" + ) from None + try: + module = importlib.import_module(module) + self._fido_callback = getattr(module, callback) + except (AttributeError, ModuleNotFoundError) as err: + raise ProgrammingError(f"{err}") from err + # Check if it's a callable + if not callable(self._fido_callback): + raise ProgrammingError("Expected a callable for 'fido_callback'") + # Check the callable signature if has only 1 positional argument + params = len(signature(self._fido_callback).parameters) + if params != 1: + raise ProgrammingError( + "'fido_callback' requires 1 positional argument, but the " + f"callback provided has {params}" + ) + + def _add_default_conn_attrs(self) -> Any: + """Add the default connection attributes.""" + + @staticmethod + def _check_server_version(server_version: StrOrBytes) -> Tuple[int, ...]: + """Check the MySQL version + + This method will check the MySQL version and raise an InterfaceError + when it is not supported or invalid. It will return the version + as a tuple with major, minor and patch. + + Raises InterfaceError if invalid server version. + + Returns tuple + """ + if isinstance(server_version, (bytearray, bytes)): + server_version = server_version.decode() + + regex_ver = re.compile(r"^(\d{1,2})\.(\d{1,2})\.(\d{1,3})(.*)") + match = regex_ver.match(server_version) + if not match: + raise InterfaceError("Failed parsing MySQL version") + + version = tuple(int(v) for v in match.groups()[0:3]) + if version < (4, 1): + raise InterfaceError(f"MySQL Version '{server_version}' is not supported") + + return version + + def get_server_version(self) -> Tuple[int, ...]: + """Get the MySQL version + + This method returns the MySQL server version as a tuple. If not + previously connected, it will return None. + + Returns a tuple or None. + """ + return self._server_version + + def get_server_info(self) -> Optional[str]: + """Get the original MySQL version information + + This method returns the original MySQL server as text. If not + previously connected, it will return None. + + Returns a string or None. + """ + try: + return self._handshake["server_version_original"] # type: ignore[return-value] + except (TypeError, KeyError): + return None + + @property + @abstractmethod + def in_transaction(self) -> Any: + """MySQL session has started a transaction""" + + def set_client_flags(self, flags: Union[int, Sequence[int]]) -> int: + """Set the client flags + + The flags-argument can be either an int or a list (or tuple) of + ClientFlag-values. If it is an integer, it will set client_flags + to flags as is. + If flags is a list (or tuple), each flag will be set or unset + when it's negative. + + set_client_flags([ClientFlag.FOUND_ROWS,-ClientFlag.LONG_FLAG]) + + Raises ProgrammingError when the flags argument is not a set or + an integer bigger than 0. + + Returns self.client_flags + """ + if isinstance(flags, int) and flags > 0: + self._client_flags = flags + elif isinstance(flags, (tuple, list)): + for flag in flags: + if flag < 0: + self._client_flags &= ~abs(flag) + else: + self._client_flags |= flag + else: + raise ProgrammingError("set_client_flags expect integer (>0) or set") + return self._client_flags + + def isset_client_flag(self, flag: int) -> bool: + """Check if a client flag is set""" + if (self._client_flags & flag) > 0: + return True + return False + + @property + def time_zone(self) -> str: + """Get the current time zone""" + return self.info_query("SELECT @@session.time_zone")[0] + + @time_zone.setter + def time_zone(self, value: str) -> None: + """Set the time zone""" + self.cmd_query(f"SET @@session.time_zone = '{value}'") + self._time_zone = value + + @property + def sql_mode(self) -> str: + """Get the SQL mode""" + if self._sql_mode is None: + self._sql_mode = self.info_query("SELECT @@session.sql_mode")[0] + return self._sql_mode + + @sql_mode.setter + def sql_mode(self, value: Union[str, Sequence[int]]) -> None: + """Set the SQL mode + + This method sets the SQL Mode for the current connection. The value + argument can be either a string with comma separate mode names, or + a sequence of mode names. + + It is good practice to use the constants class SQLMode: + from mysql.connector.constants import SQLMode + cnx.sql_mode = [SQLMode.NO_ZERO_DATE, SQLMode.REAL_AS_FLOAT] + """ + if isinstance(value, (list, tuple)): + value = ",".join(value) + self.cmd_query(f"SET @@session.sql_mode = '{value}'") + self._sql_mode = value + + @abstractmethod + def info_query(self, query: Any) -> Any: + """Send a query which only returns 1 row""" + + def set_login( + self, username: Optional[str] = None, password: Optional[str] = None + ) -> None: + """Set login information for MySQL + + Set the username and/or password for the user connecting to + the MySQL Server. + """ + if username is not None: + self._user = username.strip() + else: + self._user = "" + if password is not None: + self._password = password + else: + self._password = "" + + def set_unicode(self, value: bool = True) -> None: + """Toggle unicode mode + + Set whether we return string fields as unicode or not. + Default is True. + """ + self._use_unicode = value + if self.converter: + self.converter.set_unicode(value) + + @property + def autocommit(self) -> bool: + """Get whether autocommit is on or off""" + value = self.info_query("SELECT @@session.autocommit")[0] + return value == 1 + + @autocommit.setter + def autocommit(self, value: bool) -> None: + """Toggle autocommit""" + switch = "ON" if value else "OFF" + self.cmd_query(f"SET @@session.autocommit = {switch}") + self._autocommit = value + + @property + def get_warnings(self) -> bool: + """Get whether this connection retrieves warnings automatically + + This method returns whether this connection retrieves warnings + automatically. + + Returns True, or False when warnings are not retrieved. + """ + return self._get_warnings + + @get_warnings.setter + def get_warnings(self, value: bool) -> None: + """Set whether warnings should be automatically retrieved + + The toggle-argument must be a boolean. When True, cursors for this + connection will retrieve information about warnings (if any). + + Raises ValueError on error. + """ + if not isinstance(value, bool): + raise ValueError("Expected a boolean type") + self._get_warnings = value + + @property + def raise_on_warnings(self) -> bool: + """Get whether this connection raises an error on warnings + + This method returns whether this connection will raise errors when + MySQL reports warnings. + + Returns True or False. + """ + return self._raise_on_warnings + + @raise_on_warnings.setter + def raise_on_warnings(self, value: bool) -> None: + """Set whether warnings raise an error + + The toggle-argument must be a boolean. When True, cursors for this + connection will raise an error when MySQL reports warnings. + + Raising on warnings implies retrieving warnings automatically. In + other words: warnings will be set to True. If set to False, warnings + will be also set to False. + + Raises ValueError on error. + """ + if not isinstance(value, bool): + raise ValueError("Expected a boolean type") + self._raise_on_warnings = value + # Don't disable warning retrieval if raising explicitly disabled + if value: + self._get_warnings = value + + @property + def unread_result(self) -> bool: + """Get whether there is an unread result + + This method is used by cursors to check whether another cursor still + needs to retrieve its result set. + + Returns True, or False when there is no unread result. + """ + return self._unread_result + + @unread_result.setter + def unread_result(self, value: bool) -> None: + """Set whether there is an unread result + + This method is used by cursors to let other cursors know there is + still a result set that needs to be retrieved. + + Raises ValueError on errors. + """ + if not isinstance(value, bool): + raise ValueError("Expected a boolean type") + self._unread_result = value + + @property + def charset(self) -> str: + """Returns the character set for current connection + + This property returns the character set name of the current connection. + The server is queried when the connection is active. If not connected, + the configured character set name is returned. + + Returns a string. + """ + return CharacterSet.get_info(self._charset_id)[0] + + @property + def python_charset(self) -> str: + """Returns the Python character set for current connection + + This property returns the character set name of the current connection. + Note that, unlike property charset, this checks if the previously set + character set is supported by Python and if not, it returns the + equivalent character set that Python supports. + + Returns a string. + """ + encoding = CharacterSet.get_info(self._charset_id)[0] + if encoding in ("utf8mb4", "utf8mb3", "binary"): + return "utf8" + return encoding + + def set_charset_collation( + self, charset: Optional[Union[int, str]] = None, collation: Optional[str] = None + ) -> None: + """Sets the character set and collation for the current connection + + This method sets the character set and collation to be used for + the current connection. The charset argument can be either the + name of a character set as a string, or the numerical equivalent + as defined in constants.CharacterSet. + + When the collation is not given, the default will be looked up and + used. + + For example, the following will set the collation for the latin1 + character set to latin1_general_ci: + + set_charset('latin1','latin1_general_ci') + + """ + err_msg = "{} should be either integer, string or None" + if not isinstance(charset, (int, str)) and charset is not None: + raise ValueError(err_msg.format("charset")) + if not isinstance(collation, str) and collation is not None: + raise ValueError("collation should be either string or None") + + if charset: + if isinstance(charset, int): + ( + self._charset_id, + charset_name, + collation_name, + ) = CharacterSet.get_charset_info(charset) + elif isinstance(charset, str): + ( + self._charset_id, + charset_name, + collation_name, + ) = CharacterSet.get_charset_info(charset, collation) + else: + raise ValueError(err_msg.format("charset")) + elif collation: + ( + self._charset_id, + charset_name, + collation_name, + ) = CharacterSet.get_charset_info(collation=collation) + else: + charset = DEFAULT_CONFIGURATION["charset"] + ( + self._charset_id, + charset_name, + collation_name, + ) = CharacterSet.get_charset_info(charset, collation=None) + + self._execute_query(f"SET NAMES '{charset_name}' COLLATE '{collation_name}'") + + try: + # Required for C Extension + self.set_character_set_name(charset_name) + except AttributeError: + # Not required for pure Python connection + pass + + if self.converter: + self.converter.set_charset(charset_name) + + @property + def collation(self) -> str: + """Returns the collation for current connection + + This property returns the collation name of the current connection. + The server is queried when the connection is active. If not connected, + the configured collation name is returned. + + Returns a string. + """ + return CharacterSet.get_charset_info(self._charset_id)[2] + + @abstractmethod + def _do_handshake(self) -> Any: + """Gather information of the MySQL server before authentication""" + + @abstractmethod + def _open_connection(self) -> Any: + """Open the connection to the MySQL server""" + + def _post_connection(self) -> None: + """Executes commands after connection has been established + + This method executes commands after the connection has been + established. Some setting like autocommit, character set, and SQL mode + are set using this method. + """ + self.set_charset_collation(self._charset_id) + self.autocommit = self._autocommit + if self._time_zone: + self.time_zone = self._time_zone + if self._sql_mode: + self.sql_mode = self._sql_mode + if self._init_command: + self._execute_query(self._init_command) + + @abstractmethod + def disconnect(self) -> Any: + """Disconnect from the MySQL server""" + + close: Callable[[], Any] = disconnect + + def connect(self, **kwargs: Any) -> None: + """Connect to the MySQL server + + This method sets up the connection to the MySQL server. If no + arguments are given, it will use the already configured or default + values. + """ + if kwargs: + self.config(**kwargs) + + self.disconnect() + self._open_connection() + # Server does not allow to run any other statement different from ALTER + # when user's password has been expired. + if not self._client_flags & ClientFlag.CAN_HANDLE_EXPIRED_PASSWORDS: + self._post_connection() + + def reconnect(self, attempts: int = 1, delay: int = 0) -> None: + """Attempt to reconnect to the MySQL server + + The argument attempts should be the number of times a reconnect + is tried. The delay argument is the number of seconds to wait between + each retry. + + You may want to set the number of attempts higher and use delay when + you expect the MySQL server to be down for maintenance or when you + expect the network to be temporary unavailable. + + Raises InterfaceError on errors. + """ + counter = 0 + span = None + + if self._tracer: + span = self._tracer.start_span( + name=CONNECTION_SPAN_NAME, kind=trace.SpanKind.CLIENT + ) + + try: + while counter != attempts: + counter = counter + 1 + try: + self.disconnect() + self.connect() + if self.is_connected(): + break + except (Error, IOError) as err: + if counter == attempts: + msg = ( + f"Can not reconnect to MySQL after {attempts} " + f"attempt(s): {err}" + ) + raise InterfaceError(msg) from err + if delay > 0: + sleep(delay) + except InterfaceError as interface_err: + if OTEL_ENABLED: + set_connection_span_attrs(self, span) + record_exception_event(span, interface_err) + end_span(span) + raise + + self._span = span + if OTEL_ENABLED: + set_connection_span_attrs(self, self._span) + + @abstractmethod + def is_connected(self) -> Any: + """Reports whether the connection to MySQL Server is available""" + + @abstractmethod + def ping(self, reconnect: bool = False, attempts: int = 1, delay: int = 0) -> Any: + """Check availability of the MySQL server""" + + @abstractmethod + def commit(self) -> Any: + """Commit current transaction""" + + @abstractmethod + def cursor( + self, + buffered: Optional[bool] = None, + raw: Optional[bool] = None, + prepared: Optional[bool] = None, + cursor_class: Optional[type] = None, + dictionary: Optional[bool] = None, + named_tuple: Optional[bool] = None, + ) -> "MySQLCursorAbstract": + """Instantiates and returns a cursor""" + + @abstractmethod + def _execute_query(self, query: Any) -> Any: + """Execute a query""" + + @abstractmethod + def rollback(self) -> Any: + """Rollback current transaction""" + + def start_transaction( + self, + consistent_snapshot: bool = False, + isolation_level: Optional[str] = None, + readonly: Optional[bool] = None, + ) -> None: + """Start a transaction + + This method explicitly starts a transaction sending the + START TRANSACTION statement to the MySQL server. You can optionally + set whether there should be a consistent snapshot, which + isolation level you need or which access mode i.e. READ ONLY or + READ WRITE. + + For example, to start a transaction with isolation level SERIALIZABLE, + you would do the following: + >>> cnx = mysql.connector.connect(..) + >>> cnx.start_transaction(isolation_level='SERIALIZABLE') + + Raises ProgrammingError when a transaction is already in progress + and when ValueError when isolation_level specifies an Unknown + level. + """ + if self.in_transaction: + raise ProgrammingError("Transaction already in progress") + + if isolation_level: + level = isolation_level.strip().replace("-", " ").upper() + levels = [ + "READ UNCOMMITTED", + "READ COMMITTED", + "REPEATABLE READ", + "SERIALIZABLE", + ] + + if level not in levels: + raise ValueError(f'Unknown isolation level "{isolation_level}"') + + self._execute_query(f"SET TRANSACTION ISOLATION LEVEL {level}") + + if readonly is not None: + if self._server_version < (5, 6, 5): + raise ValueError( + f"MySQL server version {self._server_version} does not " + "support this feature" + ) + + if readonly: + access_mode = "READ ONLY" + else: + access_mode = "READ WRITE" + self._execute_query(f"SET TRANSACTION {access_mode}") + + query = "START TRANSACTION" + if consistent_snapshot: + query += " WITH CONSISTENT SNAPSHOT" + self.cmd_query(query) + + def reset_session( + self, + user_variables: Optional[Dict[str, Any]] = None, + session_variables: Optional[Dict[str, Any]] = None, + ) -> None: + """Clears the current active session + + This method resets the session state, if the MySQL server is 5.7.3 + or later active session will be reset without re-authenticating. + For other server versions session will be reset by re-authenticating. + + It is possible to provide a sequence of variables and their values to + be set after clearing the session. This is possible for both user + defined variables and session variables. + This method takes two arguments user_variables and session_variables + which are dictionaries. + + Raises OperationalError if not connected, InternalError if there are + unread results and InterfaceError on errors. + """ + if not self.is_connected(): + raise OperationalError("MySQL Connection not available") + + try: + self.cmd_reset_connection() + except (NotSupportedError, NotImplementedError): + if self._compress: + raise NotSupportedError( + "Reset session is not supported with compression for " + "MySQL server version 5.7.2 or earlier" + ) from None + self.cmd_change_user( + self._user, + self._password, + self._database, + self._charset_id, + ) + + if user_variables or session_variables: + cur = self.cursor() + if user_variables: + for key, value in user_variables.items(): + cur.execute(f"SET @`{key}` = {value}") + if session_variables: + for key, value in session_variables.items(): + cur.execute(f"SET SESSION `{key}` = {value}") + cur.close() + + def set_converter_class(self, convclass: Optional[Type[MySQLConverter]]) -> None: + """ + Set the converter class to be used. This should be a class overloading + methods and members of conversion.MySQLConverter. + """ + if convclass and issubclass(convclass, MySQLConverterBase): + charset_name = CharacterSet.get_info(self._charset_id)[0] + self._converter_class = convclass + self.converter = convclass(charset_name, self._use_unicode) + self.converter.str_fallback = self._converter_str_fallback + else: + raise TypeError( + "Converter class should be a subclass of conversion.MySQLConverterBase." + ) + + @abstractmethod + def get_rows( + self, + count: Optional[int] = None, + binary: bool = False, + columns: Optional[List[DescriptionType]] = None, + raw: Optional[bool] = None, + prep_stmt: Any = None, + ) -> Tuple[List[Any], Optional[Mapping[str, Any]]]: + """Get all rows returned by the MySQL server""" + + def cmd_init_db(self, database: str) -> Optional[Mapping[str, Any]]: + """Change the current database""" + raise NotImplementedError + + def cmd_query( + self, + query: Any, + raw: bool = False, + buffered: bool = False, + raw_as_string: bool = False, + ) -> Optional[Mapping[str, Any]]: + """Send a query to the MySQL server""" + raise NotImplementedError + + def cmd_query_iter( + self, statements: Any + ) -> Generator[Mapping[str, Any], None, None]: + """Send one or more statements to the MySQL server""" + raise NotImplementedError + + def cmd_refresh(self, options: int) -> Optional[Mapping[str, Any]]: + """Send the Refresh command to the MySQL server""" + raise NotImplementedError + + def cmd_quit(self) -> Any: + """Close the current connection with the server""" + raise NotImplementedError + + def cmd_shutdown( + self, shutdown_type: Optional[int] = None + ) -> Optional[Mapping[str, Any]]: + """Shut down the MySQL Server""" + raise NotImplementedError + + def cmd_statistics(self) -> Optional[Mapping[str, Any]]: + """Send the statistics command to the MySQL Server""" + raise NotImplementedError + + @staticmethod + def cmd_process_info() -> Any: + """Get the process list of the MySQL Server + + This method is a placeholder to notify that the PROCESS_INFO command + is not supported by raising the NotSupportedError. The command + "SHOW PROCESSLIST" should be send using the cmd_query()-method or + using the INFORMATION_SCHEMA database. + + Raises NotSupportedError exception + """ + raise NotSupportedError( + "Not implemented. Use SHOW PROCESSLIST or INFORMATION_SCHEMA" + ) + + def cmd_process_kill(self, mysql_pid: int) -> Optional[Mapping[str, Any]]: + """Kill a MySQL process""" + raise NotImplementedError + + def cmd_debug(self) -> Optional[Mapping[str, Any]]: + """Send the DEBUG command""" + raise NotImplementedError + + def cmd_ping(self) -> Optional[Mapping[str, Any]]: + """Send the PING command""" + raise NotImplementedError + + def cmd_change_user( + self, + username: str = "", + password: str = "", + database: str = "", + charset: int = 45, + password1: str = "", + password2: str = "", + password3: str = "", + oci_config_file: str = "", + ) -> Optional[Mapping[str, Any]]: + """Change the current logged in user""" + raise NotImplementedError + + def cmd_stmt_prepare(self, statement: Any) -> Optional[Mapping[str, Any]]: + """Prepare a MySQL statement""" + raise NotImplementedError + + def cmd_stmt_execute( + self, + statement_id: Any, + data: Sequence[Any] = (), + parameters: Sequence[Any] = (), + flags: int = 0, + ) -> Any: + """Execute a prepared MySQL statement""" + raise NotImplementedError + + def cmd_stmt_close(self, statement_id: Any) -> Any: + """Deallocate a prepared MySQL statement""" + raise NotImplementedError + + def cmd_stmt_send_long_data( + self, statement_id: Any, param_id: int, data: BinaryIO + ) -> Any: + """Send data for a column""" + raise NotImplementedError + + def cmd_stmt_reset(self, statement_id: Any) -> Any: + """Reset data for prepared statement sent as long data""" + raise NotImplementedError + + def cmd_reset_connection(self) -> Any: + """Resets the session state without re-authenticating""" + raise NotImplementedError + + +class MySQLCursorAbstract(ABC): + """Abstract cursor class + + Abstract class defining cursor class with method and members + required by the Python Database API Specification v2.0. + """ + + def __init__(self) -> None: + """Initialization""" + self._description: Optional[List[DescriptionType]] = None + self._rowcount: int = -1 + self._last_insert_id: Optional[int] = None + self._warnings: Optional[List[WarningType]] = None + self._warning_count: int = 0 + self._executed: Optional[StrOrBytes] = None + self._executed_list: List[StrOrBytes] = [] + self._stored_results: List[Any] = [] + self.arraysize: int = 1 + + def __enter__(self) -> MySQLCursorAbstract: + return self + + def __exit__( + self, + exc_type: Type[BaseException], + exc_value: BaseException, + traceback: TracebackType, + ) -> None: + self.close() + + @abstractmethod + def callproc(self, procname: str, args: Sequence[Any] = ()) -> Any: + """Calls a stored procedure with the given arguments + + The arguments will be set during this session, meaning + they will be called like _<procname>__arg<nr> where + <nr> is an enumeration (+1) of the arguments. + + Coding Example: + 1) Defining the Stored Routine in MySQL: + CREATE PROCEDURE multiply(IN pFac1 INT, IN pFac2 INT, OUT pProd INT) + BEGIN + SET pProd := pFac1 * pFac2; + END + + 2) Executing in Python: + args = (5,5,0) # 0 is to hold pprod + cursor.callproc('multiply', args) + print(cursor.fetchone()) + + Does not return a value, but a result set will be + available when the CALL-statement execute successfully. + Raises exceptions when something is wrong. + """ + + @abstractmethod + def close(self) -> Any: + """Close the cursor.""" + + @abstractmethod + def execute( + self, + operation: str, + params: Union[Sequence[Any], Dict[str, Any]] = (), + multi: bool = False, + ) -> Any: + """Executes the given operation + + Executes the given operation substituting any markers with + the given parameters. + + For example, getting all rows where id is 5: + cursor.execute("SELECT * FROM t1 WHERE id = %s", (5,)) + + The multi argument should be set to True when executing multiple + statements in one operation. If not set and multiple results are + found, an InterfaceError will be raised. + + If warnings where generated, and connection.get_warnings is True, then + self._warnings will be a list containing these warnings. + + Returns an iterator when multi is True, otherwise None. + """ + + @abstractmethod + def executemany( + self, operation: str, seq_params: Sequence[Union[Sequence[Any], Dict[str, Any]]] + ) -> Any: + """Execute the given operation multiple times + + The executemany() method will execute the operation iterating + over the list of parameters in seq_params. + + Example: Inserting 3 new employees and their phone number + + data = [ + ('Jane','555-001'), + ('Joe', '555-001'), + ('John', '555-003') + ] + stmt = "INSERT INTO employees (name, phone) VALUES ('%s','%s')" + cursor.executemany(stmt, data) + + INSERT statements are optimized by batching the data, that is + using the MySQL multiple rows syntax. + + Results are discarded. If they are needed, consider looping over + data using the execute() method. + """ + + @abstractmethod + def fetchone(self) -> Optional[Sequence[Any]]: + """Returns next row of a query result set + + Returns a tuple or None. + """ + + @abstractmethod + def fetchmany(self, size: int = 1) -> List[Sequence[Any]]: + """Returns the next set of rows of a query result, returning a + list of tuples. When no more rows are available, it returns an + empty list. + + The number of rows returned can be specified using the size argument, + which defaults to one + """ + + @abstractmethod + def fetchall(self) -> Sequence[Any]: + """Returns all rows of a query result set + + Returns a list of tuples. + """ + + def nextset(self) -> Any: + """Not Implemented.""" + + def setinputsizes(self, sizes: Any) -> Any: + """Not Implemented.""" + + def setoutputsize(self, size: Any, column: Any = None) -> Any: + """Not Implemented.""" + + def reset(self, free: bool = True) -> Any: + """Reset the cursor to default""" + + @property + @abstractmethod + def description( + self, + ) -> Optional[List[DescriptionType]]: + """Returns description of columns in a result + + This property returns a list of tuples describing the columns in + in a result set. A tuple is described as follows:: + + (column_name, + type, + None, + None, + None, + None, + null_ok, + column_flags) # Addition to PEP-249 specs + + Returns a list of tuples. + """ + return self._description + + @property + @abstractmethod + def rowcount(self) -> int: + """Returns the number of rows produced or affected + + This property returns the number of rows produced by queries + such as a SELECT, or affected rows when executing DML statements + like INSERT or UPDATE. + + Note that for non-buffered cursors it is impossible to know the + number of rows produced before having fetched them all. For those, + the number of rows will be -1 right after execution, and + incremented when fetching rows. + + Returns an integer. + """ + return self._rowcount + + @property + def lastrowid(self) -> Optional[int]: + """Returns the value generated for an AUTO_INCREMENT column + + Returns the value generated for an AUTO_INCREMENT column by + the previous INSERT or UPDATE statement or None when there is + no such value available. + + Returns a long value or None. + """ + return self._last_insert_id + + @property + def warnings(self) -> Optional[List[WarningType]]: + """Return warnings.""" + return self._warnings + + @property + def warning_count(self) -> int: + """Returns the number of warnings + + This property returns the number of warnings generated by the + previously executed operation. + + Returns an integer value. + """ + return self._warning_count + + def fetchwarnings(self) -> Optional[List[WarningType]]: + """Returns Warnings.""" + return self._warnings + + def get_attributes(self) -> Optional[List[Tuple[str, Any]]]: + """Get the added query attributes so far.""" + if hasattr(self, "_cnx"): + return self._cnx.query_attrs + if hasattr(self, "_connection"): + return self._connection.query_attrs + return None + + def add_attribute(self, name: str, value: Any) -> None: + """Add a query attribute and his value.""" + if not isinstance(name, str): + raise ProgrammingError("Parameter `name` must be a string type") + if value is not None and not isinstance(value, MYSQL_PY_TYPES): + raise ProgrammingError( + f"Object {value} cannot be converted to a MySQL type" + ) + if hasattr(self, "_cnx"): + self._cnx.query_attrs_append((name, value)) + elif hasattr(self, "_connection"): + self._connection.query_attrs_append((name, value)) + + def remove_attribute(self, name: str) -> Any: + """Remove a query attribute by name. + + If no match, `None` is returned; else the corresponding value is returned. + """ + if not isinstance(name, str): + raise ProgrammingError("Parameter `name` must be a string type") + if hasattr(self, "_cnx"): + return self._cnx.query_attrs_remove(name) + if hasattr(self, "_connection"): + return self._connection.query_attrs_remove(name) + return None + + def clear_attributes(self) -> None: + """Remove all the query attributes.""" + if hasattr(self, "_cnx"): + self._cnx.query_attrs_clear() + elif hasattr(self, "_connection"): + self._connection.query_attrs_clear() diff --git a/virt/lib/python3.9/site-packages/mysql/connector/connection 3.py b/virt/lib/python3.9/site-packages/mysql/connector/connection 3.py new file mode 100644 index 00000000..0151cbdb --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysql/connector/connection 3.py @@ -0,0 +1,1741 @@ +# Copyright (c) 2009, 2023, Oracle and/or its affiliates. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, as +# published by the Free Software Foundation. +# +# This program is also distributed with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an +# additional permission to link the program and your derivative works +# with the separately licensed software that they have included with +# MySQL. +# +# Without limiting anything contained in the foregoing, this file, +# which is part of MySQL Connector/Python, is also subject to the +# Universal FOSS Exception, version 1.0, a copy of which can be found at +# http://oss.oracle.com/licenses/universal-foss-exception. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License, version 2.0, for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# mypy: disable-error-code="arg-type,operator,attr-defined,assignment" + +"""Implementing communication with MySQL servers.""" + +import datetime +import getpass +import os +import socket +import struct +import sys +import warnings + +from decimal import Decimal +from io import IOBase +from typing import ( + Any, + BinaryIO, + Dict, + Generator, + List, + Mapping, + Optional, + Sequence, + Tuple, + Type, + Union, +) + +from . import version +from .abstracts import MySQLConnectionAbstract +from .authentication import get_auth_plugin +from .constants import ( + CharacterSet, + ClientFlag, + FieldType, + ServerCmd, + ServerFlag, + ShutdownType, + flag_is_set, +) +from .conversion import MySQLConverter +from .cursor import ( + CursorBase, + MySQLCursor, + MySQLCursorBuffered, + MySQLCursorBufferedDict, + MySQLCursorBufferedNamedTuple, + MySQLCursorBufferedRaw, + MySQLCursorDict, + MySQLCursorNamedTuple, + MySQLCursorPrepared, + MySQLCursorPreparedDict, + MySQLCursorPreparedNamedTuple, + MySQLCursorPreparedRaw, + MySQLCursorRaw, +) +from .errors import ( + DatabaseError, + Error, + InterfaceError, + InternalError, + NotSupportedError, + OperationalError, + ProgrammingError, + get_exception, +) +from .logger import logger +from .network import MySQLSocket, MySQLTCPSocket, MySQLUnixSocket +from .opentelemetry.constants import OTEL_ENABLED +from .opentelemetry.context_propagation import with_context_propagation +from .plugins import BaseAuthPlugin +from .protocol import MySQLProtocol +from .types import ( + ConnAttrsType, + DescriptionType, + EofPacketType, + HandShakeType, + OkPacketType, + ResultType, + RowType, + StatsPacketType, + StrOrBytes, + SupportedMysqlBinaryProtocolTypes, +) +from .utils import get_platform, int1store, int4store, lc_int + +if OTEL_ENABLED: + from .opentelemetry.instrumentation import end_span, record_exception_event + + +class MySQLConnection(MySQLConnectionAbstract): + """Connection to a MySQL Server""" + + def __init__(self, **kwargs: Any) -> None: + self._protocol: Optional[MySQLProtocol] = None + self._socket: Optional[MySQLSocket] = None + self._handshake: Optional[HandShakeType] = None + super().__init__() + + self._converter_class: Type[MySQLConverter] = MySQLConverter + + self._client_flags: int = ClientFlag.get_default() + self._charset_id: int = 45 + self._sql_mode: Optional[str] = None + self._time_zone: Optional[str] = None + self._autocommit: bool = False + + self._user: str = "" + self._password: str = "" + self._database: str = "" + self._host: str = "127.0.0.1" + self._port: int = 3306 + self._unix_socket: Optional[str] = None + self._client_host: str = "" + self._client_port: int = 0 + self._ssl: Dict[str, Optional[Union[str, bool, List[str]]]] = {} + self._force_ipv6: bool = False + + self._use_unicode: bool = True + self._get_warnings: bool = False + self._raise_on_warnings: bool = False + self._buffered: bool = False + self._unread_result: bool = False + self._have_next_result: bool = False + self._raw: bool = False + self._in_transaction: bool = False + + self._prepared_statements: Any = None + + self._ssl_active: bool = False + self._auth_plugin: Optional[str] = None + self._krb_service_principal: Optional[str] = None + self._pool_config_version: Any = None + self._query_attrs_supported: int = False + + self._columns_desc: List[DescriptionType] = [] + self._mfa_nfactor: int = 1 + + if kwargs: + try: + self.connect(**kwargs) + except Exception: + # Tidy-up underlying socket on failure + self.close() + self._socket = None + raise + + def _add_default_conn_attrs(self) -> None: + """Add the default connection attributes.""" + platform = get_platform() + license_chunks = version.LICENSE.split(" ") + if license_chunks[0] == "GPLv2": + client_license = "GPL-2.0" + else: + client_license = "Commercial" + default_conn_attrs = { + "_pid": str(os.getpid()), + "_platform": platform["arch"], + "_source_host": socket.gethostname(), + "_client_name": "mysql-connector-python", + "_client_license": client_license, + "_client_version": ".".join([str(x) for x in version.VERSION[0:3]]), + "_os": platform["version"], + } + + self._conn_attrs.update((default_conn_attrs)) + + def _do_handshake(self) -> None: + """Get the handshake from the MySQL server""" + packet = self._socket.recv() + if packet[4] == 255: + raise get_exception(packet) + + self._handshake = None + handshake = self._protocol.parse_handshake(packet) + + server_version = handshake["server_version_original"] + + self._server_version = self._check_server_version( + server_version + if isinstance(server_version, (str, bytes, bytearray)) + else "Unknown" + ) + CharacterSet.set_mysql_version(self._server_version) + + if not handshake["capabilities"] & ClientFlag.SSL: + if self._auth_plugin == "mysql_clear_password" and not self.is_secure: + raise InterfaceError( + "Clear password authentication is not supported over " + "insecure channels" + ) + if self._ssl.get("verify_cert"): + raise InterfaceError( + "SSL is required but the server doesn't support it", + errno=2026, + ) + self._client_flags &= ~ClientFlag.SSL + elif not self._ssl_disabled: + self._client_flags |= ClientFlag.SSL + + if handshake["capabilities"] & ClientFlag.PLUGIN_AUTH: + self.set_client_flags([ClientFlag.PLUGIN_AUTH]) + + if handshake["capabilities"] & ClientFlag.CLIENT_QUERY_ATTRIBUTES: + self._query_attrs_supported = True + self.set_client_flags([ClientFlag.CLIENT_QUERY_ATTRIBUTES]) + + if handshake["capabilities"] & ClientFlag.MULTI_FACTOR_AUTHENTICATION: + self.set_client_flags([ClientFlag.MULTI_FACTOR_AUTHENTICATION]) + + self._handshake = handshake + + def _do_auth( + self, + username: Optional[str] = None, + password: Optional[str] = None, + database: Optional[str] = None, + client_flags: int = 0, + charset: int = 45, + ssl_options: Optional[Dict[str, Optional[Union[str, bool, List[str]]]]] = None, + conn_attrs: Optional[ConnAttrsType] = None, + ) -> bool: + """Authenticate with the MySQL server + + Authentication happens in two parts. We first send a response to the + handshake. The MySQL server will then send either an AuthSwitchRequest + or an error packet. + + Raises NotSupportedError when we get the old, insecure password + reply back. Raises any error coming from MySQL. + """ + self._ssl_active = False + if ssl_options is None: + ssl_options = {} + if not self._ssl_disabled and (client_flags & ClientFlag.SSL): + packet = self._protocol.make_auth_ssl( + charset=charset, client_flags=client_flags + ) + self._socket.send(packet) + if ssl_options.get("tls_ciphersuites") is not None: + tls_ciphersuites = ":".join(ssl_options.get("tls_ciphersuites")) + else: + tls_ciphersuites = "" + self._socket.switch_to_ssl( + ssl_options.get("ca"), + ssl_options.get("cert"), + ssl_options.get("key"), + ssl_options.get("verify_cert") or False, + ssl_options.get("verify_identity") or False, + tls_ciphersuites, + ssl_options.get("tls_versions"), + ) + self._ssl_active = True + + if self._password1 and password != self._password1: + password = self._password1 + + logger.debug("# _do_auth(): self._auth_plugin: %s", self._auth_plugin) + if ( + self._auth_plugin.startswith("authentication_oci") + or ( + self._auth_plugin.startswith("authentication_kerberos") + and os.name == "nt" + ) + ) and not username: + username = getpass.getuser() + logger.debug( + "MySQL user is empty, OS user: %s will be used for %s", + username, + self._auth_plugin, + ) + + packet = self._protocol.make_auth( + handshake=self._handshake, + username=username, + password=password, + database=database, + charset=charset, + client_flags=client_flags, + ssl_enabled=self._ssl_active, + auth_plugin=self._auth_plugin, + conn_attrs=conn_attrs, + auth_plugin_class=self._auth_plugin_class, + ) + self._socket.send(packet) + self._auth_switch_request(username, password) + + if not (client_flags & ClientFlag.CONNECT_WITH_DB) and database: + self.cmd_init_db(database) + + return True + + def _auth_switch_request( + self, username: Optional[str] = None, password: Optional[str] = None + ) -> Optional[OkPacketType]: + """Handle second part of authentication + + Raises NotSupportedError when we get the old, insecure password + reply back. Raises any error coming from MySQL. + """ + auth = None + new_auth_plugin: Optional[str] = ( + self._auth_plugin or self._handshake["auth_plugin"] + ) + logger.debug("new_auth_plugin: %s", new_auth_plugin) + packet = self._socket.recv() + if packet[4] == 254 and len(packet) == 5: + raise NotSupportedError( + "Authentication with old (insecure) passwords " + "is not supported. For more information, lookup " + "Password Hashing in the latest MySQL manual" + ) + if packet[4] == 254: + # AuthSwitchRequest + ( + new_auth_plugin, + auth_data, + ) = self._protocol.parse_auth_switch_request(packet) + auth = get_auth_plugin(new_auth_plugin, self._auth_plugin_class)( + auth_data, + username=username or self._user, + password=password, + ssl_enabled=self.is_secure, + ) + packet = self._auth_continue(auth, new_auth_plugin, auth_data) + + if packet[4] == 1: + auth_data = self._protocol.parse_auth_more_data(packet) + auth = get_auth_plugin(new_auth_plugin, self._auth_plugin_class)( + auth_data, password=password, ssl_enabled=self.is_secure + ) + if new_auth_plugin == "caching_sha2_password": + response = auth.auth_response() + if response: + self._socket.send(response) + packet = self._socket.recv() + + if packet[4] == 0: + return self._handle_ok(packet) + if packet[4] == 2: + return self._handle_mfa(packet) + if packet[4] == 255: + raise get_exception(packet) + return None + + def _handle_mfa(self, packet: bytes) -> Optional[OkPacketType]: + """Handle Multi Factor Authentication.""" + self._mfa_nfactor += 1 + if self._mfa_nfactor == 2: + password = self._password2 + elif self._mfa_nfactor == 3: + password = self._password3 + else: + raise InterfaceError( + "Failed Multi Factor Authentication (invalid N factor)" + ) + + logger.debug("# MFA N Factor #%d", self._mfa_nfactor) + + packet, auth_plugin = self._protocol.parse_auth_next_factor(packet[4:]) + auth = get_auth_plugin(auth_plugin, self._auth_plugin_class)( + None, + username=self._user, + password=password, + ssl_enabled=self.is_secure, + ) + packet = self._auth_continue(auth, auth_plugin, packet) + + if packet[4] == 1: + auth_data = self._protocol.parse_auth_more_data(packet) + auth = get_auth_plugin(auth_plugin, self._auth_plugin_class)( + auth_data, password=password, ssl_enabled=self.is_secure + ) + if auth_plugin == "caching_sha2_password": + response = auth.auth_response() + if response: + self._socket.send(response) + packet = self._socket.recv() + + if packet[4] == 0: + return self._handle_ok(packet) + if packet[4] == 2: + return self._handle_mfa(packet) + if packet[4] == 255: + raise get_exception(packet) + return None + + def _auth_continue( + self, auth: BaseAuthPlugin, auth_plugin: str, auth_data: bytes + ) -> bytes: + """Continue with the authentication.""" + if auth_plugin == "authentication_ldap_sasl_client": + logger.debug("# auth_data: %s", auth_data) + response = auth.auth_response(self._krb_service_principal) + elif auth_plugin == "authentication_kerberos_client": + logger.debug("# auth_data: %s", auth_data) + response = auth.auth_response(auth_data) + elif auth_plugin == "authentication_oci_client": + logger.debug("# oci configuration file path: %s", self._oci_config_file) + auth.oci_config_file = self._oci_config_file + auth.oci_config_profile = self._oci_config_profile + response = auth.auth_response() + else: + response = auth.auth_response() + + logger.debug("# request: %s size: %s", response, len(response)) + self._socket.send(response) + packet = self._socket.recv() + logger.debug("# server response packet: %s", packet) + if ( + auth_plugin == "authentication_ldap_sasl_client" + and len(packet) >= 6 + and packet[5] == 114 + and packet[6] == 61 + ): # 'r' and '=' + # Continue with sasl authentication + dec_response = packet[5:] + cresponse = auth.auth_continue(dec_response) + self._socket.send(cresponse) + packet = self._socket.recv() + if packet[5] == 118 and packet[6] == 61: # 'v' and '=' + if auth.auth_finalize(packet[5:]): + # receive packed OK + packet = self._socket.recv() + elif ( + auth_plugin == "authentication_ldap_sasl_client" + and auth_data == b"GSSAPI" + and packet[4] != 255 + ): + rcode_size = 5 # header size for the response status code. + logger.debug("# Continue with sasl GSSAPI authentication") + logger.debug("# response header: %s", packet[: rcode_size + 1]) + logger.debug("# response size: %s", len(packet)) + + logger.debug("# Negotiate a service request") + complete = False + tries = 0 # To avoid a infinite loop attempt no more than feedback messages + while not complete and tries < 5: + logger.debug("%s Attempt %s %s", "-" * 20, tries + 1, "-" * 20) + logger.debug("<< server response: %s", packet) + logger.debug("# response code: %s", packet[: rcode_size + 1]) + step, complete = auth.auth_continue_krb(packet[rcode_size:]) + logger.debug(" >> response to server: %s", step) + self._socket.send(step or b"") + packet = self._socket.recv() + tries += 1 + if not complete: + raise InterfaceError( + f"Unable to fulfill server request after {tries} " + f"attempts. Last server response: {packet}" + ) + logger.debug( + " last GSSAPI response from server: %s length: %d", + packet, + len(packet), + ) + last_step = auth.auth_accept_close_handshake(packet[rcode_size:]) + logger.debug( + " >> last response to server: %s length: %d", + last_step, + len(last_step), + ) + self._socket.send(last_step) + # Receive final handshake from server + packet = self._socket.recv() + logger.debug("<< final handshake from server: %s", packet) + + # receive OK packet from server. + packet = self._socket.recv() + logger.debug("<< ok packet from server: %s", packet) + elif auth_plugin == "authentication_kerberos_client" and packet[4] != 255: + rcode_size = 5 # Reader size for the response status code + logger.debug("# Continue with GSSAPI authentication") + logger.debug("# Response header: %s", packet[: rcode_size + 1]) + logger.debug("# Response size: %s", len(packet)) + logger.debug("# Negotiate a service request") + complete = False + tries = 0 + + while not complete and tries < 5: + logger.debug("%s Attempt %s %s", "-" * 20, tries + 1, "-" * 20) + logger.debug("<< Server response: %s", packet) + logger.debug("# Response code: %s", packet[: rcode_size + 1]) + token, complete = auth.auth_continue(packet[rcode_size:]) + if token: + self._socket.send(token) + if complete: + break + packet = self._socket.recv() + + logger.debug(">> Response to server: %s", token) + tries += 1 + + if not complete: + raise InterfaceError( + f"Unable to fulfill server request after {tries} " + f"attempts. Last server response: {packet}" + ) + + logger.debug( + "Last response from server: %s length: %d", + packet, + len(packet), + ) + + # Receive OK packet from server. + packet = self._socket.recv() + logger.debug("<< Ok packet from server: %s", packet) + + return bytes(packet) + + def _get_connection(self) -> MySQLSocket: + """Get connection based on configuration + + This method will return the appropriated connection object using + the connection parameters. + + Returns subclass of MySQLBaseSocket. + """ + conn: Optional[MySQLSocket] = None + if self._unix_socket and os.name == "posix": + conn = MySQLUnixSocket(unix_socket=self.unix_socket) + else: + conn = MySQLTCPSocket( + host=self.server_host, + port=self.server_port, + force_ipv6=self._force_ipv6, + ) + + conn.set_connection_timeout(self._connection_timeout) + return conn + + def _open_connection(self) -> None: + """Open the connection to the MySQL server + + This method sets up and opens the connection to the MySQL server. + + Raises on errors. + """ + if self._auth_plugin == "authentication_kerberos_client" and not self._user: + cls = get_auth_plugin(self._auth_plugin, self._auth_plugin_class) + self._user = cls.get_user_from_credentials() + + self._protocol = MySQLProtocol() + self._socket = self._get_connection() + try: + self._socket.open_connection() + + # do initial handshake + self._do_handshake() + + # start authentication negotiation + self._do_auth( + self._user, + self._password, + self._database, + self._client_flags, + self._charset_id, + self._ssl, + self._conn_attrs, + ) + self.set_converter_class(self._converter_class) + + if self._client_flags & ClientFlag.COMPRESS: + # update the network layer accordingly + self._socket.switch_to_compressed_mode() + + self._socket.set_connection_timeout(None) + except Exception: + # close socket + self._socket.close_connection() + raise + + if ( + not self._ssl_disabled + and hasattr(self._socket.sock, "version") + and callable(self._socket.sock.version) + ): + # Raise a deprecation warning if TLSv1 or TLSv1.1 is being used + tls_version = self._socket.sock.version() + if tls_version in ("TLSv1", "TLSv1.1"): + warn_msg = ( + f"This connection is using {tls_version} which is now " + "deprecated and will be removed in a future release of " + "MySQL Connector/Python" + ) + warnings.warn(warn_msg, DeprecationWarning) + + def shutdown(self) -> None: + """Shut down connection to MySQL Server.""" + if not self._socket: + return + + try: + self._socket.shutdown() + except (AttributeError, Error): + pass # Getting an exception would mean we are disconnected. + + def close(self) -> None: + """Disconnect from the MySQL server""" + if self._span and self._span.is_recording(): + record_exception_event(self._span, sys.exc_info()[1]) + + if not self._socket: + return + + try: + self.cmd_quit() + except (AttributeError, Error): + pass # Getting an exception would mean we are disconnected. + + try: + self._socket.close_connection() + except Exception as err: + if OTEL_ENABLED: + record_exception_event(self._span, err) + raise + finally: + if OTEL_ENABLED: + end_span(self._span) + + self._handshake = None + + disconnect = close + + def _send_cmd( + self, + command: int, + argument: Optional[bytes] = None, + packet_number: int = 0, + packet: Optional[bytes] = None, + expect_response: bool = True, + compressed_packet_number: int = 0, + ) -> Optional[bytearray]: + """Send a command to the MySQL server + + This method sends a command with an optional argument. + If packet is not None, it will be sent and the argument will be + ignored. + + The packet_number is optional and should usually not be used. + + Some commands might not result in the MySQL server returning + a response. If a command does not return anything, you should + set expect_response to False. The _send_cmd method will then + return None instead of a MySQL packet. + + Returns a MySQL packet or None. + """ + self.handle_unread_result() + + try: + self._socket.send( + self._protocol.make_command(command, packet or argument), + packet_number, + compressed_packet_number, + ) + except AttributeError as err: + raise OperationalError("MySQL Connection not available") from err + + if not expect_response: + return None + return self._socket.recv() + + def _send_data(self, data_file: BinaryIO, send_empty_packet: bool = False) -> bytes: + """Send data to the MySQL server + + This method accepts a file-like object and sends its data + as is to the MySQL server. If the send_empty_packet is + True, it will send an extra empty package (for example + when using LOAD LOCAL DATA INFILE). + + Returns a MySQL packet. + """ + self.handle_unread_result() + + if not hasattr(data_file, "read"): + raise ValueError("expecting a file-like object") + + chunk_size = 131072 # 128 KB + try: + buf = data_file.read(chunk_size - 16) + while buf: + self._socket.send(buf) + buf = data_file.read(chunk_size - 16) + except AttributeError as err: + raise OperationalError("MySQL Connection not available") from err + + if send_empty_packet: + try: + self._socket.send(b"") + except AttributeError as err: + raise OperationalError("MySQL Connection not available") from err + + return bytes(self._socket.recv()) + + def _handle_server_status(self, flags: int) -> None: + """Handle the server flags found in MySQL packets + + This method handles the server flags send by MySQL OK and EOF + packets. It, for example, checks whether there exists more result + sets or whether there is an ongoing transaction. + """ + self._have_next_result = flag_is_set(ServerFlag.MORE_RESULTS_EXISTS, flags) + self._in_transaction = flag_is_set(ServerFlag.STATUS_IN_TRANS, flags) + + @property + def in_transaction(self) -> bool: + """MySQL session has started a transaction""" + return self._in_transaction + + def _handle_ok(self, packet: bytes) -> OkPacketType: + """Handle a MySQL OK packet + + This method handles a MySQL OK packet. When the packet is found to + be an Error packet, an error will be raised. If the packet is neither + an OK or an Error packet, InterfaceError will be raised. + + Returns a dict() + """ + if packet[4] == 0: + ok_pkt = self._protocol.parse_ok(packet) + self._handle_server_status(ok_pkt["status_flag"]) + return ok_pkt + if packet[4] == 255: + raise get_exception(packet) + raise InterfaceError("Expected OK packet") + + def _handle_eof(self, packet: bytes) -> EofPacketType: + """Handle a MySQL EOF packet + + This method handles a MySQL EOF packet. When the packet is found to + be an Error packet, an error will be raised. If the packet is neither + and OK or an Error packet, InterfaceError will be raised. + + Returns a dict() + """ + if packet[4] == 254: + eof = self._protocol.parse_eof(packet) + self._handle_server_status(eof["status_flag"]) + return eof + if packet[4] == 255: + raise get_exception(packet) + raise InterfaceError("Expected EOF packet") + + def _handle_load_data_infile(self, filename: str) -> OkPacketType: + """Handle a LOAD DATA INFILE LOCAL request""" + file_name = os.path.abspath(filename) + if os.path.islink(file_name): + raise OperationalError("Use of symbolic link is not allowed") + if not self._allow_local_infile and not self._allow_local_infile_in_path: + raise DatabaseError( + "LOAD DATA LOCAL INFILE file request rejected due to " + "restrictions on access." + ) + if not self._allow_local_infile and self._allow_local_infile_in_path: + # validate filename is inside of allow_local_infile_in_path path. + infile_path = os.path.abspath(self._allow_local_infile_in_path) + c_path = None + try: + c_path = os.path.commonpath([infile_path, file_name]) + except ValueError as err: + err_msg = ( + "{} while loading file `{}` and path `{}` given" + " in allow_local_infile_in_path" + ) + raise InterfaceError( + err_msg.format(str(err), file_name, infile_path) + ) from err + + if c_path != infile_path: + err_msg = ( + "The file `{}` is not found in the given " + "allow_local_infile_in_path {}" + ) + raise DatabaseError(err_msg.format(file_name, infile_path)) + + try: + data_file = open(file_name, "rb") # pylint: disable=consider-using-with + return self._handle_ok(self._send_data(data_file, send_empty_packet=True)) + except IOError: + # Send a empty packet to cancel the operation + try: + self._socket.send(b"") + except AttributeError as err: + raise OperationalError("MySQL Connection not available") from err + raise InterfaceError(f"File '{file_name}' could not be read") from None + finally: + try: + data_file.close() + except (IOError, NameError): + pass + + def _handle_result(self, packet: bytes) -> ResultType: + """Handle a MySQL Result + + This method handles a MySQL result, for example, after sending the + query command. OK and EOF packets will be handled and returned. If + the packet is an Error packet, an Error-exception will be + raised. + + The dictionary returned of: + - columns: column information + - eof: the EOF-packet information + + Returns a dict() + """ + if not packet or len(packet) < 4: + raise InterfaceError("Empty response") + if packet[4] == 0: + return self._handle_ok(packet) + if packet[4] == 251: + filename = packet[5:].decode() + return self._handle_load_data_infile(filename) + if packet[4] == 254: + return self._handle_eof(packet) + if packet[4] == 255: + raise get_exception(packet) + + # We have a text result set + column_count = self._protocol.parse_column_count(packet) + if not column_count or not isinstance(column_count, int): + raise InterfaceError("Illegal result set") + + self._columns_desc = [ + None, + ] * column_count + for i in range(0, column_count): + self._columns_desc[i] = self._protocol.parse_column( + self._socket.recv(), self.python_charset + ) + + eof = self._handle_eof(self._socket.recv()) + self.unread_result = True + return {"columns": self._columns_desc, "eof": eof} + + def get_row( + self, + binary: bool = False, + columns: Optional[List[DescriptionType]] = None, + raw: Optional[bool] = None, + ) -> Tuple[Optional[RowType], Optional[EofPacketType]]: + """Get the next rows returned by the MySQL server + + This method gets one row from the result set after sending, for + example, the query command. The result is a tuple consisting of the + row and the EOF packet. + If no row was available in the result set, the row data will be None. + + Returns a tuple. + """ + (rows, eof) = self.get_rows(count=1, binary=binary, columns=columns, raw=raw) + if rows: + return (rows[0], eof) + return (None, eof) + + def get_rows( + self, + count: Optional[int] = None, + binary: bool = False, + columns: Optional[List[DescriptionType]] = None, + raw: Optional[bool] = None, + prep_stmt: Any = None, + ) -> Tuple[List[RowType], Optional[EofPacketType]]: + """Get all rows returned by the MySQL server + + This method gets all rows returned by the MySQL server after sending, + for example, the query command. The result is a tuple consisting of + a list of rows and the EOF packet. + + Returns a tuple() + """ + if raw is None: + raw = self._raw + + if not self.unread_result: + raise InternalError("No result set available") + + rows: Tuple[List[Tuple[Any, ...]], Optional[EofPacketType]] = ([], None) + try: + if binary: + charset = self.charset + if charset == "utf8mb4": + charset = "utf8" + rows = self._protocol.read_binary_result( + self._socket, columns, count, charset + ) + else: + rows = self._protocol.read_text_result( + self._socket, self._server_version, count=count + ) + except Error as err: + self.unread_result = False + raise err + + rows, eof_p = rows + if ( + not (binary or raw) + and self._columns_desc is not None + and rows + and hasattr(self, "converter") + ): + row_to_python = self.converter.row_to_python + rows = [row_to_python(row, self._columns_desc) for row in rows] + + if eof_p is not None: + self._handle_server_status( + eof_p["status_flag"] + if "status_flag" in eof_p + else eof_p["server_status"] + ) + self.unread_result = False + + return rows, eof_p + + def consume_results(self) -> None: + """Consume results""" + if self.unread_result: + self.get_rows() + + def cmd_init_db(self, database: str) -> OkPacketType: + """Change the current database + + This method changes the current (default) database by sending the + INIT_DB command. The result is a dictionary containing the OK packet + information. + + Returns a dict() + """ + return self._handle_ok( + self._send_cmd(ServerCmd.INIT_DB, database.encode("utf-8")) + ) + + @with_context_propagation + def cmd_query( + self, + query: StrOrBytes, + raw: bool = False, + buffered: bool = False, + raw_as_string: bool = False, + ) -> ResultType: + """Send a query to the MySQL server + + This method send the query to the MySQL server and returns the result. + + If there was a text result, a tuple will be returned consisting of + the number of columns and a list containing information about these + columns. + + When the query doesn't return a text result, the OK or EOF packet + information as dictionary will be returned. In case the result was + an error, exception Error will be raised. + + Returns a tuple() + """ + if not isinstance(query, bytearray): + if isinstance(query, str): + query = query.encode("utf-8") + query = bytearray(query) + # Prepare query attrs + charset = self.charset if self.charset != "utf8mb4" else "utf8" + packet = bytearray() + if not self._query_attrs_supported and self._query_attrs: + warnings.warn( + "This version of the server does not support Query Attributes", + category=Warning, + ) + if self._client_flags & ClientFlag.CLIENT_QUERY_ATTRIBUTES: + names = [] + types = [] + values: List[bytes] = [] + null_bitmap = [0] * ((len(self._query_attrs) + 7) // 8) + for pos, attr_tuple in enumerate(self._query_attrs.items()): + value = attr_tuple[1] + flags = 0 + if value is None: + null_bitmap[(pos // 8)] |= 1 << (pos % 8) + types.append(int1store(FieldType.NULL) + int1store(flags)) + continue + if isinstance(value, int): + ( + packed, + field_type, + flags, + ) = self._protocol.prepare_binary_integer(value) + values.append(packed) + elif isinstance(value, str): + value = value.encode(charset) + values.append(lc_int(len(value)) + value) + field_type = FieldType.STRING + elif isinstance(value, bytes): + values.append(lc_int(len(value)) + value) + field_type = FieldType.STRING + elif isinstance(value, Decimal): + values.append( + lc_int(len(str(value).encode(charset))) + + str(value).encode(charset) + ) + field_type = FieldType.DECIMAL + elif isinstance(value, float): + values.append(struct.pack("<d", value)) + field_type = FieldType.DOUBLE + elif isinstance(value, (datetime.datetime, datetime.date)): + ( + packed, + field_type, + ) = self._protocol.prepare_binary_timestamp(value) + values.append(packed) + elif isinstance(value, (datetime.timedelta, datetime.time)): + (packed, field_type) = self._protocol.prepare_binary_time(value) + values.append(packed) + else: + raise ProgrammingError( + "MySQL binary protocol can not handle " + f"'{value.__class__.__name__}' objects" + ) + types.append(int1store(field_type) + int1store(flags)) + name = attr_tuple[0].encode(charset) + names.append(lc_int(len(name)) + name) + + # int<lenenc> parameter_count Number of parameters + packet.extend(lc_int(len(self._query_attrs))) + # int<lenenc> parameter_set_count Number of parameter sets. + # Currently always 1 + packet.extend(lc_int(1)) + if values: + packet.extend( + b"".join([struct.pack("B", bit) for bit in null_bitmap]) + + int1store(1) + ) + for _type, name in zip(types, names): + packet.extend(_type) + packet.extend(name) + + for value in values: + packet.extend(value) + + packet.extend(query) + query = bytes(packet) + try: + result = self._handle_result(self._send_cmd(ServerCmd.QUERY, query)) + except ProgrammingError as err: + if err.errno == 3948 and "Loading local data is disabled" in err.msg: + err_msg = ( + "LOAD DATA LOCAL INFILE file request rejected due " + "to restrictions on access." + ) + raise DatabaseError(err_msg) from err + raise + if self._have_next_result: + raise InterfaceError( + "Use cmd_query_iter for statements with multiple queries." + ) + + return result + + def cmd_query_iter( + self, statements: StrOrBytes + ) -> Generator[ResultType, None, None]: + """Send one or more statements to the MySQL server + + Similar to the cmd_query method, but instead returns a generator + object to iterate through results. It sends the statements to the + MySQL server and through the iterator you can get the results. + + statement = 'SELECT 1; INSERT INTO t1 VALUES (); SELECT 2' + for result in cnx.cmd_query(statement, iterate=True): + if 'columns' in result: + columns = result['columns'] + rows = cnx.get_rows() + else: + # do something useful with INSERT result + + Returns a generator. + """ + packet = bytearray() + if not isinstance(statements, bytearray): + if isinstance(statements, str): + statements = statements.encode("utf8") + statements = bytearray(statements) + + if self._client_flags & ClientFlag.CLIENT_QUERY_ATTRIBUTES: + # int<lenenc> parameter_count Number of parameters + packet.extend(lc_int(0)) + # int<lenenc> parameter_set_count Number of parameter sets. + # Currently always 1 + packet.extend(lc_int(1)) + + packet.extend(statements) + query = bytes(packet) + # Handle the first query result + yield self._handle_result(self._send_cmd(ServerCmd.QUERY, query)) + + # Handle next results, if any + while self._have_next_result: + self.handle_unread_result() + yield self._handle_result(self._socket.recv()) + + def cmd_refresh(self, options: int) -> OkPacketType: + """Send the Refresh command to the MySQL server + + This method sends the Refresh command to the MySQL server. The options + argument should be a bitwise value using constants.RefreshOption. + Usage example: + RefreshOption = mysql.connector.RefreshOption + refresh = RefreshOption.LOG | RefreshOption.THREADS + cnx.cmd_refresh(refresh) + + The result is a dictionary with the OK packet information. + + Returns a dict() + """ + return self._handle_ok(self._send_cmd(ServerCmd.REFRESH, int4store(options))) + + def cmd_quit(self) -> bytes: + """Close the current connection with the server + + This method sends the QUIT command to the MySQL server, closing the + current connection. Since the no response can be returned to the + client, cmd_quit() will return the packet it send. + + Returns a str() + """ + self.handle_unread_result() + + packet = self._protocol.make_command(ServerCmd.QUIT) + self._socket.send(packet, 0, 0) + return bytes(packet) + + def cmd_shutdown(self, shutdown_type: Optional[int] = None) -> EofPacketType: + """Shut down the MySQL Server + + This method sends the SHUTDOWN command to the MySQL server and is only + possible if the current user has SUPER privileges. The result is a + dictionary containing the OK packet information. + + Note: Most applications and scripts do not the SUPER privilege. + + Returns a dict() + """ + if shutdown_type: + if not ShutdownType.get_info(shutdown_type): + raise InterfaceError("Invalid shutdown type") + atype = shutdown_type + else: + atype = ShutdownType.SHUTDOWN_DEFAULT + return self._handle_eof(self._send_cmd(ServerCmd.SHUTDOWN, int4store(atype))) + + def cmd_statistics(self) -> StatsPacketType: + """Send the statistics command to the MySQL Server + + This method sends the STATISTICS command to the MySQL server. The + result is a dictionary with various statistical information. + + Returns a dict() + """ + self.handle_unread_result() + + packet = self._protocol.make_command(ServerCmd.STATISTICS) + self._socket.send(packet, 0, 0) + return self._protocol.parse_statistics(self._socket.recv()) + + def cmd_process_kill(self, mysql_pid: int) -> OkPacketType: + """Kill a MySQL process + + This method send the PROCESS_KILL command to the server along with + the process ID. The result is a dictionary with the OK packet + information. + + Returns a dict() + """ + return self._handle_ok( + self._send_cmd(ServerCmd.PROCESS_KILL, int4store(mysql_pid)) + ) + + def cmd_debug(self) -> EofPacketType: + """Send the DEBUG command + + This method sends the DEBUG command to the MySQL server, which + requires the MySQL user to have SUPER privilege. The output will go + to the MySQL server error log and the result of this method is a + dictionary with EOF packet information. + + Returns a dict() + """ + return self._handle_eof(self._send_cmd(ServerCmd.DEBUG)) + + def cmd_ping(self) -> OkPacketType: + """Send the PING command + + This method sends the PING command to the MySQL server. It is used to + check if the the connection is still valid. The result of this + method is dictionary with OK packet information. + + Returns a dict() + """ + return self._handle_ok(self._send_cmd(ServerCmd.PING)) + + def cmd_change_user( + self, + username: str = "", + password: str = "", + database: str = "", + charset: int = 45, + password1: str = "", + password2: str = "", + password3: str = "", + oci_config_file: str = "", + oci_config_profile: str = "", + ) -> Optional[OkPacketType]: + """Change the current logged in user + + This method allows to change the current logged in user information. + The result is a dictionary with OK packet information. + + Returns a dict() + """ + if not isinstance(charset, int): + raise ValueError("charset must be an integer") + if charset < 0: + raise ValueError("charset should be either zero or a postive integer") + + self._mfa_nfactor = 1 + self._user = username + self._password = password + self._password1 = password1 + self._password2 = password2 + self._password3 = password3 + + if self._password1 and password != self._password1: + password = self._password1 + + self.handle_unread_result() + + if self._compress: + raise NotSupportedError("Change user is not supported with compression") + packet = self._protocol.make_change_user( + handshake=self._handshake, + username=username, + password=password, + database=database, + charset=charset, + client_flags=self._client_flags, + ssl_enabled=self._ssl_active, + auth_plugin=self._auth_plugin, + conn_attrs=self._conn_attrs, + ) + self._socket.send(packet, 0, 0) + + if oci_config_file: + self._oci_config_file = oci_config_file + + self._oci_config_profile = oci_config_profile + + ok_packet = self._auth_switch_request(username, password) + + if not (self._client_flags & ClientFlag.CONNECT_WITH_DB) and database: + self.cmd_init_db(database) + + self._charset_id = charset + self._post_connection() + + return ok_packet + + @property + def database(self) -> str: + """Get the current database""" + return self.info_query("SELECT DATABASE()")[0] # type: ignore[return-value] + + @database.setter + def database(self, value: str) -> None: + """Set the current database""" + self.cmd_init_db(value) + + def is_connected(self) -> bool: + """Reports whether the connection to MySQL Server is available + + This method checks whether the connection to MySQL is available. + It is similar to ping(), but unlike the ping()-method, either True + or False is returned and no exception is raised. + + Returns True or False. + """ + try: + self.cmd_ping() + except Error: + return False # This method does not raise + return True + + def set_allow_local_infile_in_path(self, path: str) -> None: + """Set the path that user can upload files. + + Args: + path (str): Path that user can upload files. + """ + self._allow_local_infile_in_path = path + + def reset_session( + self, + user_variables: Optional[Dict[str, Any]] = None, + session_variables: Optional[Dict[str, Any]] = None, + ) -> None: + """Clears the current active session + + This method resets the session state, if the MySQL server is 5.7.3 + or later active session will be reset without re-authenticating. + For other server versions session will be reset by re-authenticating. + + It is possible to provide a sequence of variables and their values to + be set after clearing the session. This is possible for both user + defined variables and session variables. + This method takes two arguments user_variables and session_variables + which are dictionaries. + + Raises OperationalError if not connected, InternalError if there are + unread results and InterfaceError on errors. + """ + if not self.is_connected(): + raise OperationalError("MySQL Connection not available.") + + if not self.cmd_reset_connection(): + try: + self.cmd_change_user( + self._user, + self._password, + self._database, + self._charset_id, + self._password1, + self._password2, + self._password3, + self._oci_config_file, + self._oci_config_profile, + ) + except ProgrammingError: + self.reconnect() + + cur = self.cursor() + if user_variables: + for key, value in user_variables.items(): + cur.execute(f"SET @`{key}` = %s", (value,)) + if session_variables: + for key, value in session_variables.items(): + cur.execute(f"SET SESSION `{key}` = %s", (value,)) + + def ping(self, reconnect: bool = False, attempts: int = 1, delay: int = 0) -> None: + """Check availability of the MySQL server + + When reconnect is set to True, one or more attempts are made to try + to reconnect to the MySQL server using the reconnect()-method. + + delay is the number of seconds to wait between each retry. + + When the connection is not available, an InterfaceError is raised. Use + the is_connected()-method if you just want to check the connection + without raising an error. + + Raises InterfaceError on errors. + """ + try: + self.cmd_ping() + except Error as err: + if reconnect: + self.reconnect(attempts=attempts, delay=delay) + else: + raise InterfaceError("Connection to MySQL is not available") from err + + @property + def connection_id(self) -> Optional[int]: + """MySQL connection ID""" + if self._handshake: + return self._handshake.get("server_threadid") # type: ignore[return-value] + return None + + def cursor( + self, + buffered: Optional[bool] = None, + raw: Optional[bool] = None, + prepared: Optional[bool] = None, + cursor_class: Optional[Type[MySQLCursor]] = None, + dictionary: Optional[bool] = None, + named_tuple: Optional[bool] = None, + ) -> MySQLCursor: + """Instantiates and returns a cursor + + By default, MySQLCursor is returned. Depending on the options + while connecting, a buffered and/or raw cursor is instantiated + instead. Also depending upon the cursor options, rows can be + returned as dictionary or named tuple. + + Dictionary and namedtuple based cursors are available with buffered + output but not raw. + + It is possible to also give a custom cursor through the + cursor_class parameter, but it needs to be a subclass of + mysql.connector.cursor.CursorBase. + + Raises ProgrammingError when cursor_class is not a subclass of + CursorBase. Raises ValueError when cursor is not available. + + Returns a cursor-object + """ + self.handle_unread_result() + + if not self.is_connected(): + raise OperationalError("MySQL Connection not available") + if cursor_class is not None: + if not issubclass(cursor_class, CursorBase): + raise ProgrammingError( + "Cursor class needs be to subclass of cursor.CursorBase" + ) + return (cursor_class)(self) + + buffered = buffered if buffered is not None else self._buffered + raw = raw if raw is not None else self._raw + + cursor_type = 0 + if buffered is True: + cursor_type |= 1 + if raw is True: + cursor_type |= 2 + if dictionary is True: + cursor_type |= 4 + if named_tuple is True: + cursor_type |= 8 + if prepared is True: + cursor_type |= 16 + + types = { + 0: MySQLCursor, # 0 + 1: MySQLCursorBuffered, + 2: MySQLCursorRaw, + 3: MySQLCursorBufferedRaw, + 4: MySQLCursorDict, + 5: MySQLCursorBufferedDict, + 8: MySQLCursorNamedTuple, + 9: MySQLCursorBufferedNamedTuple, + 16: MySQLCursorPrepared, + 18: MySQLCursorPreparedRaw, + 20: MySQLCursorPreparedDict, + 24: MySQLCursorPreparedNamedTuple, + } + try: + return (types[cursor_type])(self) + except KeyError: + args = ("buffered", "raw", "dictionary", "named_tuple", "prepared") + raise ValueError( + "Cursor not available with given criteria: " + + ", ".join([args[i] for i in range(5) if cursor_type & (1 << i) != 0]) + ) from None + + def commit(self) -> None: + """Commit current transaction""" + self._execute_query("COMMIT") + + def rollback(self) -> None: + """Rollback current transaction""" + if self.unread_result: + self.get_rows() + + self._execute_query("ROLLBACK") + + def _execute_query(self, query: StrOrBytes) -> None: + """Execute a query + + This method simply calls cmd_query() after checking for unread + result. If there are still unread result, an InterfaceError + is raised. Otherwise whatever cmd_query() returns is returned. + + Returns a dict() + """ + self.handle_unread_result() + self.cmd_query(query) + + def info_query(self, query: StrOrBytes) -> Optional[RowType]: + """Send a query which only returns 1 row""" + cursor = self.cursor(buffered=True) + cursor.execute(query) + return cursor.fetchone() + + def _handle_binary_ok(self, packet: bytes) -> Dict[str, int]: + """Handle a MySQL Binary Protocol OK packet + + This method handles a MySQL Binary Protocol OK packet. When the + packet is found to be an Error packet, an error will be raised. If + the packet is neither an OK or an Error packet, InterfaceError + will be raised. + + Returns a dict() + """ + if packet[4] == 0: + return self._protocol.parse_binary_prepare_ok(packet) + if packet[4] == 255: + raise get_exception(packet) + raise InterfaceError("Expected Binary OK packet") + + def _handle_binary_result( + self, packet: bytes + ) -> Union[OkPacketType, Tuple[int, List[DescriptionType], EofPacketType]]: + """Handle a MySQL Result + + This method handles a MySQL result, for example, after sending the + query command. OK and EOF packets will be handled and returned. If + the packet is an Error packet, an Error exception will be raised. + + The tuple returned by this method consist of: + - the number of columns in the result, + - a list of tuples with information about the columns, + - the EOF packet information as a dictionary. + + Returns tuple() or dict() + """ + if not packet or len(packet) < 4: + raise InterfaceError("Empty response") + if packet[4] == 0: + return self._handle_ok(packet) + if packet[4] == 254: + return self._handle_eof(packet) + if packet[4] == 255: + raise get_exception(packet) + + # We have a binary result set + column_count = self._protocol.parse_column_count(packet) + if not column_count or not isinstance(column_count, int): + raise InterfaceError("Illegal result set.") + + columns: List[DescriptionType] = [None] * column_count + for i in range(0, column_count): + columns[i] = self._protocol.parse_column( + self._socket.recv(), self.python_charset + ) + + eof = self._handle_eof(self._socket.recv()) + return (column_count, columns, eof) + + def cmd_stmt_fetch(self, statement_id: int, rows: int = 1) -> None: + """Fetch a MySQL statement Result Set + + This method will send the FETCH command to MySQL together with the + given statement id and the number of rows to fetch. + """ + packet = self._protocol.make_stmt_fetch(statement_id, rows) + self.unread_result = False + self._send_cmd(ServerCmd.STMT_FETCH, packet, expect_response=False) + self.unread_result = True + + def cmd_stmt_prepare( + self, statement: bytes + ) -> Mapping[str, Union[int, List[DescriptionType]]]: + """Prepare a MySQL statement + + This method will send the PREPARE command to MySQL together with the + given statement. + + Returns a dict() + """ + packet = self._send_cmd(ServerCmd.STMT_PREPARE, statement) + result = self._handle_binary_ok(packet) + + result["columns"] = [] + result["parameters"] = [] + if result["num_params"] > 0: + for _ in range(0, result["num_params"]): + result["parameters"].append( + self._protocol.parse_column( + self._socket.recv(), self.python_charset + ) + ) + self._handle_eof(self._socket.recv()) + if result["num_columns"] > 0: + for _ in range(0, result["num_columns"]): + result["columns"].append( + self._protocol.parse_column( + self._socket.recv(), self.python_charset + ) + ) + self._handle_eof(self._socket.recv()) + + return result + + @with_context_propagation + def cmd_stmt_execute( + self, + statement_id: int, + data: Sequence[SupportedMysqlBinaryProtocolTypes] = (), + parameters: Sequence[Any] = (), + flags: int = 0, + ) -> Union[OkPacketType, Tuple[int, List[DescriptionType], EofPacketType]]: + """Execute a prepared MySQL statement""" + parameters = list(parameters) + long_data_used = {} + + if data: + for param_id, _ in enumerate(parameters): + if isinstance(data[param_id], IOBase): + binary = True + try: + binary = "b" not in data[param_id].mode # type: ignore[union-attr] + except AttributeError: + pass + self.cmd_stmt_send_long_data(statement_id, param_id, data[param_id]) + long_data_used[param_id] = (binary,) + if not self._query_attrs_supported and self._query_attrs: + warnings.warn( + "This version of the server does not support Query Attributes", + category=Warning, + ) + if self._client_flags & ClientFlag.CLIENT_QUERY_ATTRIBUTES: + execute_packet = self._protocol.make_stmt_execute( + statement_id, + data, + tuple(parameters), + flags, + long_data_used, + self.charset, + self.query_attrs, + self._converter_str_fallback, + ) + else: + execute_packet = self._protocol.make_stmt_execute( + statement_id, + data, + tuple(parameters), + flags, + long_data_used, + self.charset, + converter_str_fallback=self._converter_str_fallback, + ) + packet = self._send_cmd(ServerCmd.STMT_EXECUTE, packet=execute_packet) + result = self._handle_binary_result(packet) + return result + + def cmd_stmt_close(self, statement_id: int) -> None: + """Deallocate a prepared MySQL statement + + This method deallocates the prepared statement using the + statement_id. Note that the MySQL server does not return + anything. + """ + self._send_cmd( + ServerCmd.STMT_CLOSE, + int4store(statement_id), + expect_response=False, + ) + + def cmd_stmt_send_long_data( + self, statement_id: int, param_id: int, data: BinaryIO + ) -> int: + """Send data for a column + + This methods send data for a column (for example BLOB) for statement + identified by statement_id. The param_id indicate which parameter + the data belongs too. + The data argument should be a file-like object. + + Since MySQL does not send anything back, no error is raised. When + the MySQL server is not reachable, an OperationalError is raised. + + cmd_stmt_send_long_data should be called before cmd_stmt_execute. + + The total bytes send is returned. + + Returns int. + """ + chunk_size = 131072 # 128 KB + total_sent = 0 + try: + buf = data.read(chunk_size) + while buf: + packet = self._protocol.prepare_stmt_send_long_data( + statement_id, param_id, buf + ) + self._send_cmd( + ServerCmd.STMT_SEND_LONG_DATA, + packet=packet, + expect_response=False, + ) + total_sent += len(buf) + buf = data.read(chunk_size) + except AttributeError as err: + raise OperationalError("MySQL Connection not available") from err + + return total_sent + + def cmd_stmt_reset(self, statement_id: int) -> None: + """Reset data for prepared statement sent as long data + + The result is a dictionary with OK packet information. + + Returns a dict() + """ + self._handle_ok(self._send_cmd(ServerCmd.STMT_RESET, int4store(statement_id))) + + def cmd_reset_connection(self) -> bool: + """Resets the session state without re-authenticating + + Reset command only works on MySQL server 5.7.3 or later. + The result is True for a successful reset otherwise False. + + Returns bool + """ + try: + self._handle_ok(self._send_cmd(ServerCmd.RESET_CONNECTION)) + self._post_connection() + return True + except (NotSupportedError, OperationalError): + return False + + def handle_unread_result(self) -> None: + """Check whether there is an unread result""" + if self.can_consume_results: + self.consume_results() + elif self.unread_result: + raise InternalError("Unread result found") diff --git a/virt/lib/python3.9/site-packages/mysql/connector/connection_cext 3.py b/virt/lib/python3.9/site-packages/mysql/connector/connection_cext 3.py new file mode 100644 index 00000000..a514fa51 --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysql/connector/connection_cext 3.py @@ -0,0 +1,1004 @@ +# Copyright (c) 2014, 2023, Oracle and/or its affiliates. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, as +# published by the Free Software Foundation. +# +# This program is also distributed with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an +# additional permission to link the program and your derivative works +# with the separately licensed software that they have included with +# MySQL. +# +# Without limiting anything contained in the foregoing, this file, +# which is part of MySQL Connector/Python, is also subject to the +# Universal FOSS Exception, version 1.0, a copy of which can be found at +# http://oss.oracle.com/licenses/universal-foss-exception. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License, version 2.0, for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# mypy: disable-error-code="arg-type,index" + +"""Connection class using the C Extension.""" + +import os +import platform +import socket +import sys + +from typing import Any, Dict, List, Optional, Sequence, Tuple, Type, Union + +from . import version +from .abstracts import MySQLConnectionAbstract +from .constants import CharacterSet, ClientFlag, FieldFlag, ServerFlag, ShutdownType +from .conversion import MySQLConverter +from .errors import ( + InterfaceError, + InternalError, + OperationalError, + ProgrammingError, + get_mysql_exception, +) +from .protocol import MySQLProtocol +from .types import ( + CextEofPacketType, + CextResultType, + DescriptionType, + ParamsSequenceOrDictType, + RowType, + StatsPacketType, + StrOrBytes, +) + +HAVE_CMYSQL = False + +try: + import _mysql_connector + + from _mysql_connector import MySQLInterfaceError, MySQLPrepStmt + + from .cursor_cext import ( + CMySQLCursor, + CMySQLCursorBuffered, + CMySQLCursorBufferedDict, + CMySQLCursorBufferedNamedTuple, + CMySQLCursorBufferedRaw, + CMySQLCursorDict, + CMySQLCursorNamedTuple, + CMySQLCursorPrepared, + CMySQLCursorPreparedDict, + CMySQLCursorPreparedNamedTuple, + CMySQLCursorPreparedRaw, + CMySQLCursorRaw, + ) + + HAVE_CMYSQL = True +except ImportError as exc: + raise ImportError( + f"MySQL Connector/Python C Extension not available ({exc})" + ) from exc + +from .opentelemetry.constants import OTEL_ENABLED +from .opentelemetry.context_propagation import with_context_propagation + +if OTEL_ENABLED: + from .opentelemetry.instrumentation import end_span, record_exception_event + + +class CMySQLConnection(MySQLConnectionAbstract): + """Class initiating a MySQL Connection using Connector/C.""" + + def __init__(self, **kwargs: Any) -> None: + """Initialization""" + if not HAVE_CMYSQL: + raise RuntimeError("MySQL Connector/Python C Extension not available") + self._cmysql: Optional[ + _mysql_connector.MySQL # pylint: disable=c-extension-no-member + ] = None + self._columns: List[DescriptionType] = [] + self._plugin_dir: str = os.path.join( + os.path.dirname(os.path.abspath(_mysql_connector.__file__)), + "mysql", + "vendor", + "plugin", + ) + if platform.system() == "Linux": + # Use the authentication plugins from system if they aren't bundled + if not os.path.exists(self._plugin_dir): + self._plugin_dir = ( + "/usr/lib64/mysql/plugin" + if os.path.exists("/usr/lib64/mysql/plugin") + else "/usr/lib/mysql/plugin" + ) + + self.converter: Optional[MySQLConverter] = None + super().__init__() + + if kwargs: + try: + self.connect(**kwargs) + except Exception: + self.close() + raise + + def _add_default_conn_attrs(self) -> None: + """Add default connection attributes""" + license_chunks = version.LICENSE.split(" ") + if license_chunks[0] == "GPLv2": + client_license = "GPL-2.0" + else: + client_license = "Commercial" + + self._conn_attrs.update( + { + "_connector_name": "mysql-connector-python", + "_connector_license": client_license, + "_connector_version": ".".join([str(x) for x in version.VERSION[0:3]]), + "_source_host": socket.gethostname(), + } + ) + + def _do_handshake(self) -> None: + """Gather information of the MySQL server before authentication""" + self._handshake = { + "protocol": self._cmysql.get_proto_info(), + "server_version_original": self._cmysql.get_server_info(), + "server_threadid": self._cmysql.thread_id(), + "charset": None, + "server_status": None, + "auth_plugin": None, + "auth_data": None, + "capabilities": self._cmysql.st_server_capabilities(), + } + + self._server_version = self._check_server_version( + self._handshake["server_version_original"] + ) + CharacterSet.set_mysql_version(self._server_version) + + @property + def _server_status(self) -> int: + """Returns the server status attribute of MYSQL structure""" + return self._cmysql.st_server_status() + + def set_allow_local_infile_in_path(self, path: str) -> None: + """set local_infile_in_path + + Set allow_local_infile_in_path. + """ + + if self._cmysql: + self._cmysql.set_load_data_local_infile_option(path) + + def set_unicode(self, value: bool = True) -> None: + """Toggle unicode mode + + Set whether we return string fields as unicode or not. + Default is True. + """ + self._use_unicode = value + if self._cmysql: + self._cmysql.use_unicode(value) + if self.converter: + self.converter.set_unicode(value) + + @property + def autocommit(self) -> bool: + """Get whether autocommit is on or off""" + value = self.info_query("SELECT @@session.autocommit")[0] + return value == 1 + + @autocommit.setter + def autocommit(self, value: bool) -> None: + """Toggle autocommit""" + try: + self._cmysql.autocommit(value) + self._autocommit = value + except MySQLInterfaceError as err: + raise get_mysql_exception( + msg=err.msg, errno=err.errno, sqlstate=err.sqlstate + ) from err + + @property + def database(self) -> str: + """Get the current database""" + return self.info_query("SELECT DATABASE()")[0] # type: ignore[return-value] + + @database.setter + def database(self, value: str) -> None: + """Set the current database""" + try: + self._cmysql.select_db(value) + except MySQLInterfaceError as err: + raise get_mysql_exception( + msg=err.msg, errno=err.errno, sqlstate=err.sqlstate + ) from err + + @property + def in_transaction(self) -> int: + """MySQL session has started a transaction""" + return self._server_status & ServerFlag.STATUS_IN_TRANS + + def _open_connection(self) -> None: + charset_name = CharacterSet.get_info(self._charset_id)[0] + # pylint: disable=c-extension-no-member + self._cmysql = _mysql_connector.MySQL( + buffered=self._buffered, + raw=self._raw, + charset_name=charset_name, + connection_timeout=(self._connection_timeout or 0), + use_unicode=self._use_unicode, + auth_plugin=self._auth_plugin, + plugin_dir=self._plugin_dir, + ) + # pylint: enable=c-extension-no-member + if not self.isset_client_flag(ClientFlag.CONNECT_ARGS): + self._conn_attrs = {} + cnx_kwargs = { + "host": self._host, + "user": self._user, + "password": self._password, + "password1": self._password1, + "password2": self._password2, + "password3": self._password3, + "database": self._database, + "port": self._port, + "client_flags": self._client_flags, + "unix_socket": self._unix_socket, + "compress": self._compress, + "ssl_disabled": True, + "conn_attrs": self._conn_attrs, + "local_infile": self._allow_local_infile, + "load_data_local_dir": self._allow_local_infile_in_path, + "oci_config_file": self._oci_config_file, + "oci_config_profile": self._oci_config_profile, + "fido_callback": self._fido_callback, + } + + tls_versions = self._ssl.get("tls_versions") + if tls_versions is not None: + tls_versions.sort(reverse=True) # type: ignore[union-attr] + tls_versions = ",".join(tls_versions) + if self._ssl.get("tls_ciphersuites") is not None: + ssl_ciphersuites = self._ssl.get("tls_ciphersuites")[0] + tls_ciphersuites = self._ssl.get("tls_ciphersuites")[1] + else: + ssl_ciphersuites = None + tls_ciphersuites = None + if ( + tls_versions is not None + and "TLSv1.3" in tls_versions + and not tls_ciphersuites + ): + tls_ciphersuites = "TLS_AES_256_GCM_SHA384" + if not self._ssl_disabled: + cnx_kwargs.update( + { + "ssl_ca": self._ssl.get("ca"), + "ssl_cert": self._ssl.get("cert"), + "ssl_key": self._ssl.get("key"), + "ssl_cipher_suites": ssl_ciphersuites, + "tls_versions": tls_versions, + "tls_cipher_suites": tls_ciphersuites, + "ssl_verify_cert": self._ssl.get("verify_cert") or False, + "ssl_verify_identity": self._ssl.get("verify_identity") or False, + "ssl_disabled": self._ssl_disabled, + } + ) + + if os.name == "nt" and self._auth_plugin_class == "MySQLKerberosAuthPlugin": + cnx_kwargs["use_kerberos_gssapi"] = True + + try: + self._cmysql.connect(**cnx_kwargs) + self._cmysql.converter_str_fallback = self._converter_str_fallback + if self.converter: + self.converter.str_fallback = self._converter_str_fallback + except MySQLInterfaceError as err: + raise get_mysql_exception( + msg=err.msg, errno=err.errno, sqlstate=err.sqlstate + ) from err + + self._do_handshake() + + def close(self) -> None: + """Disconnect from the MySQL server""" + if self._span and self._span.is_recording(): + record_exception_event(self._span, sys.exc_info()[1]) + + if not self._cmysql: + return + + try: + self.free_result() + self._cmysql.close() + except MySQLInterfaceError as err: + if OTEL_ENABLED: + record_exception_event(self._span, err) + raise get_mysql_exception( + msg=err.msg, errno=err.errno, sqlstate=err.sqlstate + ) from err + finally: + if OTEL_ENABLED: + end_span(self._span) + + disconnect = close + + def is_closed(self) -> bool: + """Return True if the connection to MySQL Server is closed.""" + return not self._cmysql.connected() + + def is_connected(self) -> bool: + """Reports whether the connection to MySQL Server is available""" + if self._cmysql: + self.handle_unread_result() + return self._cmysql.ping() + + return False + + def ping(self, reconnect: bool = False, attempts: int = 1, delay: int = 0) -> None: + """Check availability of the MySQL server + + When reconnect is set to True, one or more attempts are made to try + to reconnect to the MySQL server using the reconnect()-method. + + delay is the number of seconds to wait between each retry. + + When the connection is not available, an InterfaceError is raised. Use + the is_connected()-method if you just want to check the connection + without raising an error. + + Raises InterfaceError on errors. + """ + self.handle_unread_result() + + try: + connected = self._cmysql.ping() + except AttributeError: + pass # Raise or reconnect later + else: + if connected: + return + + if reconnect: + self.reconnect(attempts=attempts, delay=delay) + else: + raise InterfaceError("Connection to MySQL is not available") + + def set_character_set_name(self, charset: str) -> None: + """Sets the default character set name for current connection.""" + self._cmysql.set_character_set(charset) + + def info_query(self, query: StrOrBytes) -> Optional[RowType]: + """Send a query which only returns 1 row""" + first_row = () + try: + self._cmysql.query(query) + if self._cmysql.have_result_set: + first_row = self._cmysql.fetch_row() + if self._cmysql.fetch_row(): + self._cmysql.free_result() + raise InterfaceError("Query should not return more than 1 row") + self._cmysql.free_result() + except MySQLInterfaceError as err: + raise get_mysql_exception( + msg=err.msg, errno=err.errno, sqlstate=err.sqlstate + ) from err + + return first_row + + @property + def connection_id(self) -> Optional[int]: + """MySQL connection ID""" + try: + return self._cmysql.thread_id() + except MySQLInterfaceError: + pass # Just return None + + return None + + def get_rows( + self, + count: Optional[int] = None, + binary: bool = False, + columns: Optional[List[DescriptionType]] = None, + raw: Optional[bool] = None, + prep_stmt: Optional[MySQLPrepStmt] = None, + ) -> Tuple[List[RowType], Optional[CextEofPacketType]]: + """Get all or a subset of rows returned by the MySQL server""" + unread_result = prep_stmt.have_result_set if prep_stmt else self.unread_result + if not (self._cmysql and unread_result): + raise InternalError("No result set available") + + if raw is None: + raw = self._raw + + rows: List[Tuple[Any, ...]] = [] + if count is not None and count <= 0: + raise AttributeError("count should be 1 or higher, or None") + + counter = 0 + try: + fetch_row = prep_stmt.fetch_row if prep_stmt else self._cmysql.fetch_row + if self.converter: + # When using a converter class, the C extension should not + # convert the values. This can be accomplished by setting + # the raw option to True. + self._cmysql.raw(True) + row = fetch_row() + while row: + if not self._raw and self.converter: + row = list(row) + for i, _ in enumerate(row): + if not raw: + row[i] = self.converter.to_python(self._columns[i], row[i]) + row = tuple(row) + rows.append(row) + counter += 1 + if count and counter == count: + break + row = fetch_row() + if not row: + _eof: Optional[CextEofPacketType] = self.fetch_eof_columns(prep_stmt)[ + "eof" + ] # type: ignore[assignment] + if prep_stmt: + prep_stmt.free_result() + self._unread_result = False + else: + self.free_result() + else: + _eof = None + except MySQLInterfaceError as err: + if prep_stmt: + prep_stmt.free_result() + raise InterfaceError(str(err)) from err + self.free_result() + raise get_mysql_exception( + msg=err.msg, errno=err.errno, sqlstate=err.sqlstate + ) from err + + return rows, _eof + + def get_row( + self, + binary: bool = False, + columns: Optional[List[DescriptionType]] = None, + raw: Optional[bool] = None, + prep_stmt: Optional[MySQLPrepStmt] = None, + ) -> Tuple[Optional[RowType], CextEofPacketType]: + """Get the next rows returned by the MySQL server""" + try: + rows, eof = self.get_rows( + count=1, + binary=binary, + columns=columns, + raw=raw, + prep_stmt=prep_stmt, + ) + if rows: + return (rows[0], eof) + return (None, eof) + except IndexError: + # No row available + return (None, None) + + def next_result(self) -> Optional[bool]: + """Reads the next result""" + if self._cmysql: + self._cmysql.consume_result() + return self._cmysql.next_result() + return None + + def free_result(self) -> None: + """Frees the result""" + if self._cmysql: + self._cmysql.free_result() + + def commit(self) -> None: + """Commit current transaction""" + if self._cmysql: + self.handle_unread_result() + self._cmysql.commit() + + def rollback(self) -> None: + """Rollback current transaction""" + if self._cmysql: + self._cmysql.consume_result() + self._cmysql.rollback() + + def cmd_init_db(self, database: str) -> None: + """Change the current database""" + try: + self._cmysql.select_db(database) + except MySQLInterfaceError as err: + raise get_mysql_exception( + msg=err.msg, errno=err.errno, sqlstate=err.sqlstate + ) from err + + def fetch_eof_columns( + self, prep_stmt: Optional[MySQLPrepStmt] = None + ) -> CextResultType: + """Fetch EOF and column information""" + have_result_set = ( + prep_stmt.have_result_set if prep_stmt else self._cmysql.have_result_set + ) + if not have_result_set: + raise InterfaceError("No result set") + + fields = prep_stmt.fetch_fields() if prep_stmt else self._cmysql.fetch_fields() + self._columns = [] + for col in fields: + self._columns.append( + ( + col[4], + int(col[8]), + None, + None, + None, + None, + ~int(col[9]) & FieldFlag.NOT_NULL, + int(col[9]), + int(col[6]), + ) + ) + + return { + "eof": { + "status_flag": self._server_status, + "warning_count": self._cmysql.st_warning_count(), + }, + "columns": self._columns, + } + + def fetch_eof_status(self) -> Optional[CextEofPacketType]: + """Fetch EOF and status information""" + if self._cmysql: + return { + "warning_count": self._cmysql.st_warning_count(), + "field_count": self._cmysql.st_field_count(), + "insert_id": self._cmysql.insert_id(), + "affected_rows": self._cmysql.affected_rows(), + "server_status": self._server_status, + } + + return None + + def cmd_stmt_prepare(self, statement: bytes) -> MySQLPrepStmt: + """Prepares the SQL statement""" + if not self._cmysql: + raise OperationalError("MySQL Connection not available") + + try: + stmt = self._cmysql.stmt_prepare(statement) + stmt.converter_str_fallback = self._converter_str_fallback + return stmt + except MySQLInterfaceError as err: + raise InterfaceError(str(err)) from err + + def cmd_stmt_execute( + self, statement_id: MySQLPrepStmt, *args: Any + ) -> Optional[Union[CextEofPacketType, CextResultType]]: + """Executes the prepared statement""" + try: + statement_id.stmt_execute(*args) + except MySQLInterfaceError as err: + raise InterfaceError(str(err)) from err + + self._columns = [] + if not statement_id.have_result_set: + # No result + self._unread_result = False + return self.fetch_eof_status() + + self._unread_result = True + return self.fetch_eof_columns(statement_id) + + def cmd_stmt_close(self, statement_id: MySQLPrepStmt) -> None: + """Closes the prepared statement""" + if self._unread_result: + raise InternalError("Unread result found") + statement_id.stmt_close() + + def cmd_stmt_reset(self, statement_id: MySQLPrepStmt) -> None: + """Resets the prepared statement""" + if self._unread_result: + raise InternalError("Unread result found") + statement_id.stmt_reset() + + @with_context_propagation + def cmd_query( + self, + query: StrOrBytes, + raw: Optional[bool] = None, + buffered: bool = False, + raw_as_string: bool = False, + ) -> Optional[Union[CextEofPacketType, CextResultType]]: + """Send a query to the MySQL server""" + self.handle_unread_result() + if raw is None: + raw = self._raw + try: + if not isinstance(query, bytes): + query = query.encode("utf-8") + self._cmysql.query( + query, + raw=raw, + buffered=buffered, + raw_as_string=raw_as_string, + query_attrs=self.query_attrs, + ) + except MySQLInterfaceError as err: + raise get_mysql_exception( + err.errno, msg=err.msg, sqlstate=err.sqlstate + ) from err + except AttributeError as err: + addr = ( + self._unix_socket if self._unix_socket else f"{self._host}:{self._port}" + ) + raise OperationalError( + errno=2055, values=(addr, "Connection not available.") + ) from err + + self._columns = [] + if not self._cmysql.have_result_set: + # No result + return self.fetch_eof_status() + + return self.fetch_eof_columns() + + _execute_query = cmd_query + + def cursor( + self, + buffered: Optional[bool] = None, + raw: Optional[bool] = None, + prepared: Optional[bool] = None, + cursor_class: Optional[Type[CMySQLCursor]] = None, + dictionary: Optional[bool] = None, + named_tuple: Optional[bool] = None, + ) -> CMySQLCursor: + """Instantiates and returns a cursor using C Extension + + By default, CMySQLCursor is returned. Depending on the options + while connecting, a buffered and/or raw cursor is instantiated + instead. Also depending upon the cursor options, rows can be + returned as dictionary or named tuple. + + Dictionary and namedtuple based cursors are available with buffered + output but not raw. + + It is possible to also give a custom cursor through the + cursor_class parameter, but it needs to be a subclass of + mysql.connector.cursor_cext.CMySQLCursor. + + Raises ProgrammingError when cursor_class is not a subclass of + CursorBase. Raises ValueError when cursor is not available. + + Returns instance of CMySQLCursor or subclass. + + :param buffered: Return a buffering cursor + :param raw: Return a raw cursor + :param prepared: Return a cursor which uses prepared statements + :param cursor_class: Use a custom cursor class + :param dictionary: Rows are returned as dictionary + :param named_tuple: Rows are returned as named tuple + :return: Subclass of CMySQLCursor + :rtype: CMySQLCursor or subclass + """ + self.handle_unread_result(prepared) + if not self.is_connected(): + raise OperationalError("MySQL Connection not available.") + if cursor_class is not None: + if not issubclass(cursor_class, CMySQLCursor): + raise ProgrammingError( + "Cursor class needs be to subclass of cursor_cext.CMySQLCursor" + ) + return (cursor_class)(self) + + buffered = buffered or self._buffered + raw = raw or self._raw + + cursor_type = 0 + if buffered is True: + cursor_type |= 1 + if raw is True: + cursor_type |= 2 + if dictionary is True: + cursor_type |= 4 + if named_tuple is True: + cursor_type |= 8 + if prepared is True: + cursor_type |= 16 + + types = { + 0: CMySQLCursor, # 0 + 1: CMySQLCursorBuffered, + 2: CMySQLCursorRaw, + 3: CMySQLCursorBufferedRaw, + 4: CMySQLCursorDict, + 5: CMySQLCursorBufferedDict, + 8: CMySQLCursorNamedTuple, + 9: CMySQLCursorBufferedNamedTuple, + 16: CMySQLCursorPrepared, + 18: CMySQLCursorPreparedRaw, + 20: CMySQLCursorPreparedDict, + 24: CMySQLCursorPreparedNamedTuple, + } + try: + return (types[cursor_type])(self) + except KeyError: + args = ("buffered", "raw", "dictionary", "named_tuple", "prepared") + raise ValueError( + "Cursor not available with given criteria: " + + ", ".join([args[i] for i in range(5) if cursor_type & (1 << i) != 0]) + ) from None + + @property + def num_rows(self) -> int: + """Returns number of rows of current result set""" + if not self._cmysql.have_result_set: + raise InterfaceError("No result set") + + return self._cmysql.num_rows() + + @property + def warning_count(self) -> int: + """Returns number of warnings""" + if not self._cmysql: + return 0 + + return self._cmysql.warning_count() + + @property + def result_set_available(self) -> bool: + """Check if a result set is available""" + if not self._cmysql: + return False + + return self._cmysql.have_result_set + + @property # type: ignore[misc] + def unread_result(self) -> bool: + """Check if there are unread results or rows""" + return self.result_set_available + + @property + def more_results(self) -> bool: + """Check if there are more results""" + return self._cmysql.more_results() + + def prepare_for_mysql( + self, params: ParamsSequenceOrDictType + ) -> Union[Sequence[bytes], Dict[str, bytes],]: + """Prepare parameters for statements + + This method is use by cursors to prepared parameters found in the + list (or tuple) params. + + Returns dict. + """ + result: Union[List[Any], Dict[str, Any]] = [] + if isinstance(params, (list, tuple)): + if self.converter: + result = [ + self.converter.quote( + self.converter.escape( + self.converter.to_mysql(value), self._sql_mode + ) + ) + for value in params + ] + else: + result = self._cmysql.convert_to_mysql(*params) + elif isinstance(params, dict): + result = {} + if self.converter: + for key, value in params.items(): + result[key] = self.converter.quote( + self.converter.escape( + self.converter.to_mysql(value), self._sql_mode + ) + ) + else: + for key, value in params.items(): + result[key] = self._cmysql.convert_to_mysql(value)[0] + else: + raise ProgrammingError( + f"Could not process parameters: {type(params).__name__}({params})," + " it must be of type list, tuple or dict" + ) + + return result + + def consume_results(self) -> None: + """Consume the current result + + This method consume the result by reading (consuming) all rows. + """ + self._cmysql.consume_result() + + def cmd_change_user( + self, + username: str = "", + password: str = "", + database: str = "", + charset: int = 45, + password1: str = "", + password2: str = "", + password3: str = "", + oci_config_file: Optional[str] = None, + oci_config_profile: Optional[str] = None, + ) -> None: + """Change the current logged in user""" + try: + self._cmysql.change_user( + username, + password, + database, + password1, + password2, + password3, + oci_config_file, + oci_config_profile, + ) + + except MySQLInterfaceError as err: + raise get_mysql_exception( + msg=err.msg, errno=err.errno, sqlstate=err.sqlstate + ) from err + + self._charset_id = charset + self._user = username # updating user accordingly + self._post_connection() + + def cmd_reset_connection(self) -> bool: + """Resets the session state without re-authenticating + + Reset command only works on MySQL server 5.7.3 or later. + The result is True for a successful reset otherwise False. + + Returns bool + """ + res = self._cmysql.reset_connection() + if res: + self._post_connection() + return res + + def cmd_refresh(self, options: int) -> Optional[CextEofPacketType]: + """Send the Refresh command to the MySQL server""" + try: + self.handle_unread_result() + self._cmysql.refresh(options) + except MySQLInterfaceError as err: + raise get_mysql_exception( + msg=err.msg, errno=err.errno, sqlstate=err.sqlstate + ) from err + + return self.fetch_eof_status() + + def cmd_quit(self) -> None: + """Close the current connection with the server""" + self.close() + + def cmd_shutdown(self, shutdown_type: Optional[int] = None) -> None: + """Shut down the MySQL Server""" + if not self._cmysql: + raise OperationalError("MySQL Connection not available") + + if shutdown_type: + if not ShutdownType.get_info(shutdown_type): + raise InterfaceError("Invalid shutdown type") + level = shutdown_type + else: + level = ShutdownType.SHUTDOWN_DEFAULT + + try: + self._cmysql.shutdown(level) + except MySQLInterfaceError as err: + raise get_mysql_exception( + msg=err.msg, errno=err.errno, sqlstate=err.sqlstate + ) from err + self.close() + + def cmd_statistics(self) -> StatsPacketType: + """Return statistics from the MySQL server""" + self.handle_unread_result() + + try: + stat = self._cmysql.stat() + return MySQLProtocol().parse_statistics(stat, with_header=False) + except (MySQLInterfaceError, InterfaceError) as err: + raise get_mysql_exception( + msg=err.msg, errno=err.errno, sqlstate=err.sqlstate + ) from err + + def cmd_process_kill(self, mysql_pid: int) -> None: + """Kill a MySQL process""" + if not isinstance(mysql_pid, int): + raise ValueError("MySQL PID must be int") + self.info_query(f"KILL {mysql_pid}") + + def cmd_debug(self) -> Any: + """Send the DEBUG command""" + raise NotImplementedError + + def cmd_ping(self) -> Any: + """Send the PING command""" + raise NotImplementedError + + def cmd_query_iter(self, statements: Any) -> Any: + """Send one or more statements to the MySQL server""" + raise NotImplementedError + + def cmd_stmt_send_long_data( + self, statement_id: Any, param_id: Any, data: Any + ) -> Any: + """Send data for a column""" + raise NotImplementedError + + def handle_unread_result(self, prepared: bool = False) -> None: + """Check whether there is an unread result""" + unread_result = self._unread_result if prepared is True else self.unread_result + if self.can_consume_results: + self.consume_results() + elif unread_result: + raise InternalError("Unread result found") + + def reset_session( + self, + user_variables: Optional[Dict[str, Any]] = None, + session_variables: Optional[Dict[str, Any]] = None, + ) -> None: + """Clears the current active session + + This method resets the session state, if the MySQL server is 5.7.3 + or later active session will be reset without re-authenticating. + For other server versions session will be reset by re-authenticating. + + It is possible to provide a sequence of variables and their values to + be set after clearing the session. This is possible for both user + defined variables and session variables. + This method takes two arguments user_variables and session_variables + which are dictionaries. + + Raises OperationalError if not connected, InternalError if there are + unread results and InterfaceError on errors. + """ + if not self.is_connected(): + raise OperationalError("MySQL Connection not available.") + + if not self.cmd_reset_connection(): + try: + self.cmd_change_user( + self._user, + self._password, + self._database, + self._charset_id, + self._password1, + self._password2, + self._password3, + self._oci_config_file, + self._oci_config_profile, + ) + except ProgrammingError: + self.reconnect() + + if user_variables or session_variables: + cur = self.cursor() + if user_variables: + for key, value in user_variables.items(): + cur.execute(f"SET @`{key}` = %s", (value,)) + if session_variables: + for key, value in session_variables.items(): + cur.execute(f"SET SESSION `{key}` = %s", (value,)) + cur.close() diff --git a/virt/lib/python3.9/site-packages/mysql/connector/cursor 3.py b/virt/lib/python3.9/site-packages/mysql/connector/cursor 3.py new file mode 100644 index 00000000..2677dd6a --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysql/connector/cursor 3.py @@ -0,0 +1,1756 @@ +# Copyright (c) 2009, 2023, Oracle and/or its affiliates. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, as +# published by the Free Software Foundation. +# +# This program is also distributed with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an +# additional permission to link the program and your derivative works +# with the separately licensed software that they have included with +# MySQL. +# +# Without limiting anything contained in the foregoing, this file, +# which is part of MySQL Connector/Python, is also subject to the +# Universal FOSS Exception, version 1.0, a copy of which can be found at +# http://oss.oracle.com/licenses/universal-foss-exception. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License, version 2.0, for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# mypy: disable-error-code="assignment,arg-type,attr-defined,index,override,call-overload" + +"""Cursor classes.""" +from __future__ import annotations + +import re +import warnings +import weakref + +from collections import namedtuple +from decimal import Decimal +from typing import ( + Any, + Dict, + Generator, + Iterator, + List, + NoReturn, + Optional, + Sequence, + Tuple, + Type, + Union, +) +from weakref import CallableProxyType + +from .abstracts import NAMED_TUPLE_CACHE, MySQLConnectionAbstract, MySQLCursorAbstract +from .constants import ServerFlag +from .errors import ( + Error, + InterfaceError, + NotSupportedError, + ProgrammingError, + get_mysql_exception, +) +from .types import ( + DescriptionType, + EofPacketType, + ParamsDictType, + ParamsSequenceOrDictType, + ParamsSequenceType, + ResultType, + RowType, + StrOrBytes, + ToPythonOutputTypes, + WarningType, +) + +SQL_COMMENT = r"\/\*.*?\*\/" +RE_SQL_COMMENT = re.compile( + rf"""({SQL_COMMENT})|(["'`][^"'`]*?({SQL_COMMENT})[^"'`]*?["'`])""", + re.I | re.M | re.S, +) +RE_SQL_ON_DUPLICATE = re.compile( + r"""\s*ON\s+DUPLICATE\s+KEY(?:[^"'`]*["'`][^"'`]*["'`])*[^"'`]*$""", + re.I | re.M | re.S, +) +RE_SQL_INSERT_STMT = re.compile( + rf"({SQL_COMMENT}|\s)*INSERT({SQL_COMMENT}|\s)" + r"*(?:IGNORE\s+)?INTO\s+[`'\"]?.+[`'\"]?(?:\.[`'\"]?.+[`'\"]?)" + r"{0,2}\s+VALUES\s*(\(.+\)).*", + re.I | re.M | re.S, +) +RE_SQL_INSERT_VALUES = re.compile(r".*VALUES\s*(\(.+\)).*", re.I | re.M | re.S) +RE_PY_PARAM = re.compile(b"(%s)") +RE_PY_MAPPING_PARAM = re.compile( + rb""" + % + \((?P<mapping_key>[^)]+)\) + (?P<conversion_type>[diouxXeEfFgGcrs%]) + """, + re.X, +) +RE_SQL_SPLIT_STMTS = re.compile( + b""";(?=(?:[^"'`]*(?:"[^"]*"|'[^']*'|`[^`]*`))*[^"'`]*$)""" +) +RE_SQL_FIND_PARAM = re.compile(b"""%s(?=(?:[^"'`]*["'`][^"'`]*["'`])*[^"'`]*$)""") +RE_SQL_PYTHON_REPLACE_PARAM = re.compile(r"%\(.*?\)s") +RE_SQL_PYTHON_CAPTURE_PARAM_NAME = re.compile(r"%\((.*?)\)s") + +ERR_NO_RESULT_TO_FETCH = "No result set to fetch from" + +MAX_RESULTS = 4294967295 + + +class _ParamSubstitutor: + """ + Substitutes parameters into SQL statement. + """ + + def __init__(self, params: Sequence[bytes]) -> None: + self.params: Sequence[bytes] = params + self.index: int = 0 + + def __call__(self, matchobj: re.Match) -> bytes: + index = self.index + self.index += 1 + try: + return bytes(self.params[index]) + except IndexError: + raise ProgrammingError( + "Not enough parameters for the SQL statement" + ) from None + + @property + def remaining(self) -> int: + """Returns number of parameters remaining to be substituted""" + return len(self.params) - self.index + + +def _bytestr_format_dict(bytestr: bytes, value_dict: Dict[bytes, bytes]) -> bytes: + """ + >>> _bytestr_format_dict(b'%(a)s', {b'a': b'foobar'}) + b'foobar + >>> _bytestr_format_dict(b'%%(a)s', {b'a': b'foobar'}) + b'%%(a)s' + >>> _bytestr_format_dict(b'%%%(a)s', {b'a': b'foobar'}) + b'%%foobar' + >>> _bytestr_format_dict(b'%(x)s %(y)s', + ... {b'x': b'x=%(y)s', b'y': b'y=%(x)s'}) + b'x=%(y)s y=%(x)s' + """ + + def replace(matchobj: re.Match) -> bytes: + """Replace pattern.""" + value: Optional[bytes] = None + groups = matchobj.groupdict() + if groups["conversion_type"] == b"%": + value = b"%" + if groups["conversion_type"] == b"s": + key = groups["mapping_key"] + value = value_dict[key] + if value is None: + raise ValueError( + f"Unsupported conversion_type: {groups['conversion_type']}" + ) + return value + + stmt = RE_PY_MAPPING_PARAM.sub(replace, bytestr) + return stmt + + +class CursorBase(MySQLCursorAbstract): + """ + Base for defining MySQLCursor. This class is a skeleton and defines + methods and members as required for the Python Database API + Specification v2.0. + + It's better to inherite from MySQLCursor. + """ + + _raw: bool = False + + def __init__(self) -> None: + self._description: Optional[List[DescriptionType]] = None + self._rowcount: int = -1 + self.arraysize: int = 1 + super().__init__() + + def callproc(self, procname: str, args: Sequence[Any] = ()) -> Any: + """Calls a stored procedue with the given arguments + + The arguments will be set during this session, meaning + they will be called like _<procname>__arg<nr> where + <nr> is an enumeration (+1) of the arguments. + + Coding Example: + 1) Definining the Stored Routine in MySQL: + CREATE PROCEDURE multiply(IN pFac1 INT, IN pFac2 INT, OUT pProd INT) + BEGIN + SET pProd := pFac1 * pFac2; + END + + 2) Executing in Python: + args = (5,5,0) # 0 is to hold pprod + cursor.callproc('multiply', args) + print(cursor.fetchone()) + + Does not return a value, but a result set will be + available when the CALL-statement execute successfully. + Raises exceptions when something is wrong. + """ + + def close(self) -> Any: + """Close the cursor.""" + + def execute( + self, + operation: Any, + params: Union[Sequence[Any], Dict[str, Any]] = (), + multi: bool = False, + ) -> Any: + """Executes the given operation + + Executes the given operation substituting any markers with + the given parameters. + + For example, getting all rows where id is 5: + cursor.execute("SELECT * FROM t1 WHERE id = %s", (5,)) + + The multi argument should be set to True when executing multiple + statements in one operation. If not set and multiple results are + found, an InterfaceError will be raised. + + If warnings where generated, and connection.get_warnings is True, then + self._warnings will be a list containing these warnings. + + Returns an iterator when multi is True, otherwise None. + """ + + def executemany( + self, operation: Any, seq_params: Sequence[Union[Sequence[Any], Dict[str, Any]]] + ) -> Any: + """Execute the given operation multiple times + + The executemany() method will execute the operation iterating + over the list of parameters in seq_params. + + Example: Inserting 3 new employees and their phone number + + data = [ + ('Jane','555-001'), + ('Joe', '555-001'), + ('John', '555-003') + ] + stmt = "INSERT INTO employees (name, phone) VALUES ('%s','%s')" + cursor.executemany(stmt, data) + + INSERT statements are optimized by batching the data, that is + using the MySQL multiple rows syntax. + + Results are discarded. If they are needed, consider looping over + data using the execute() method. + """ + + def fetchone(self) -> Optional[Sequence[Any]]: + """Return next row of a query result set. + + Returns: + tuple or None: A row from query result set. + """ + + def fetchmany(self, size: int = 1) -> List[Sequence[Any]]: + """Return the next set of rows of a query result set. + + When no more rows are available, it returns an empty list. + The number of rows returned can be specified using the size argument, + which defaults to one. + + Returns: + list: The next set of rows of a query result set. + """ + + def fetchall(self) -> List[Sequence[Any]]: + """Return all rows of a query result set. + + Returns: + list: A list of tuples with all rows of a query result set. + """ + + def nextset(self) -> Any: + """Not Implemented.""" + + def setinputsizes(self, sizes: Any) -> Any: + """Not Implemented.""" + + def setoutputsize(self, size: Any, column: Any = None) -> Any: + """Not Implemented.""" + + def reset(self, free: bool = True) -> Any: + """Reset the cursor to default""" + + @property + def description(self) -> Optional[List[DescriptionType]]: + """Returns description of columns in a result + + This property returns a list of tuples describing the columns in + in a result set. A tuple is described as follows:: + + (column_name, + type, + None, + None, + None, + None, + null_ok, + column_flags) # Addition to PEP-249 specs + + Returns a list of tuples. + """ + return self._description + + @property + def rowcount(self) -> int: + """Returns the number of rows produced or affected + + This property returns the number of rows produced by queries + such as a SELECT, or affected rows when executing DML statements + like INSERT or UPDATE. + + Note that for non-buffered cursors it is impossible to know the + number of rows produced before having fetched them all. For those, + the number of rows will be -1 right after execution, and + incremented when fetching rows. + + Returns an integer. + """ + return self._rowcount + + +class MySQLCursor(CursorBase): + """Default cursor for interacting with MySQL + + This cursor will execute statements and handle the result. It will + not automatically fetch all rows. + + MySQLCursor should be inherited whenever other functionallity is + required. An example would to change the fetch* member functions + to return dictionaries instead of lists of values. + + Implements the Python Database API Specification v2.0 (PEP-249) + """ + + def __init__( + self, connection: Optional[Type[MySQLConnectionAbstract]] = None + ) -> None: + CursorBase.__init__(self) + self._connection: CallableProxyType[Type[MySQLConnectionAbstract]] = None + self._nextrow: Tuple[Optional[RowType], Optional[EofPacketType]] = ( + None, + None, + ) + self._binary: bool = False + + if connection is not None: + self._set_connection(connection) + + def __iter__(self) -> Iterator[RowType]: + """ + Iteration over the result set which calls self.fetchone() + and returns the next row. + """ + return iter(self.fetchone, None) + + def _set_connection(self, connection: Type[MySQLConnectionAbstract]) -> None: + """Set the connection""" + try: + self._connection = weakref.proxy(connection) + self._connection.is_connected() + except (AttributeError, TypeError): + raise InterfaceError(errno=2048) from None + + def _reset_result(self) -> None: + """Reset the cursor to default""" + self._rowcount: int = -1 + self._nextrow = (None, None) + self._stored_results: List[MySQLCursor] = [] + self._warnings: Optional[List[WarningType]] = None + self._warning_count: int = 0 + self._description: Optional[List[DescriptionType]] = None + self._executed: Optional[StrOrBytes] = None + self._executed_list: List[StrOrBytes] = [] + self.reset() + + def _have_unread_result(self) -> bool: + """Check whether there is an unread result""" + try: + return self._connection.unread_result + except AttributeError: + return False + + def _check_executed(self) -> None: + """Check if the statement has been executed. + + Raises an error if the statement has not been executed. + """ + if self._executed is None: + raise InterfaceError(ERR_NO_RESULT_TO_FETCH) + + def __next__(self) -> RowType: + """ + Used for iterating over the result set. Calles self.fetchone() + to get the next row. + """ + try: + row = self.fetchone() + except InterfaceError: + raise StopIteration from None + if not row: + raise StopIteration + return row + + def close(self) -> bool: + """Close the cursor + + Returns True when successful, otherwise False. + """ + if self._connection is None: + return False + + self._connection.handle_unread_result() + self._reset_result() + self._connection = None + + return True + + def _process_params_dict( + self, params: ParamsDictType + ) -> Dict[bytes, Union[bytes, Decimal]]: + """Process query parameters given as dictionary""" + res: Dict[bytes, Any] = {} + try: + sql_mode = self._connection.sql_mode + to_mysql = self._connection.converter.to_mysql + escape = self._connection.converter.escape + quote = self._connection.converter.quote + for key, value in params.items(): + conv = value + conv = to_mysql(conv) + conv = escape(conv, sql_mode) + if not isinstance(value, Decimal): + conv = quote(conv) + res[key.encode()] = conv + except Exception as err: + raise ProgrammingError( + f"Failed processing pyformat-parameters; {err}" + ) from err + return res + + def _process_params( + self, params: ParamsSequenceType + ) -> Tuple[Union[bytes, Decimal], ...]: + """Process query parameters.""" + res = params[:] + try: + sql_mode = self._connection.sql_mode + to_mysql = self._connection.converter.to_mysql + escape = self._connection.converter.escape + quote = self._connection.converter.quote + res = [to_mysql(value) for value in res] + res = [escape(value, sql_mode) for value in res] + res = [ + quote(value) if not isinstance(params[i], Decimal) else value + for i, value in enumerate(res) + ] + except Exception as err: + raise ProgrammingError( + f"Failed processing format-parameters; {err}" + ) from err + return tuple(res) + + def _handle_noresultset(self, res: ResultType) -> None: + """Handles result of execute() when there is no result set""" + try: + self._rowcount = res["affected_rows"] + self._last_insert_id = res["insert_id"] + self._warning_count = res["warning_count"] + except (KeyError, TypeError) as err: + raise ProgrammingError(f"Failed handling non-resultset; {err}") from None + + self._handle_warnings() + + def _handle_resultset(self) -> None: + """Handles result set + + This method handles the result set and is called after reading + and storing column information in _handle_result(). For non-buffering + cursors, this method is usually doing nothing. + """ + + def _handle_result(self, result: ResultType) -> None: + """ + Handle the result after a command was send. The result can be either + an OK-packet or a dictionary containing column/eof information. + + Raises InterfaceError when result is not a dict() or result is + invalid. + """ + if not isinstance(result, dict): + raise InterfaceError("Result was not a dict()") + + if "columns" in result: + # Weak test, must be column/eof information + self._description = result["columns"] + self._connection.unread_result = True + self._handle_resultset() + elif "affected_rows" in result: + # Weak test, must be an OK-packet + self._connection.unread_result = False + self._handle_noresultset(result) + else: + raise InterfaceError("Invalid result") + + def _execute_iter( + self, query_iter: Generator[ResultType, None, None] + ) -> Generator[MySQLCursor, None, None]: + """Generator returns MySQLCursor objects for multiple statements + + This method is only used when multiple statements are executed + by the execute() method. It uses zip() to make an iterator from the + given query_iter (result of MySQLConnection.cmd_query_iter()) and + the list of statements that were executed. + """ + executed_list = RE_SQL_SPLIT_STMTS.split(self._executed) + + i = 0 + while True: + try: + result = next(query_iter) + self._reset_result() + self._handle_result(result) + try: + self._executed = executed_list[i].strip() + i += 1 + except IndexError: + self._executed = executed_list[0] + + yield self + except StopIteration: + return + + def execute( + self, + operation: StrOrBytes, + params: Optional[ParamsSequenceOrDictType] = None, + multi: bool = False, + ) -> Optional[Generator[MySQLCursor, None, None]]: + """Executes the given operation + + Executes the given operation substituting any markers with + the given parameters. + + For example, getting all rows where id is 5: + cursor.execute("SELECT * FROM t1 WHERE id = %s", (5,)) + + The multi argument should be set to True when executing multiple + statements in one operation. If not set and multiple results are + found, an InterfaceError will be raised. + + If warnings where generated, and connection.get_warnings is True, then + self._warnings will be a list containing these warnings. + + Returns an iterator when multi is True, otherwise None. + """ + if not operation: + return None + + try: + if not self._connection: + raise ProgrammingError + except (ProgrammingError, ReferenceError) as err: + raise ProgrammingError("Cursor is not connected") from err + + self._connection.handle_unread_result() + + self._reset_result() + stmt: StrOrBytes = "" + + try: + if not isinstance(operation, (bytes, bytearray)): + stmt = operation.encode(self._connection.python_charset) + else: + stmt = operation + except (UnicodeDecodeError, UnicodeEncodeError) as err: + raise ProgrammingError(str(err)) from err + + if params: + if isinstance(params, dict): + stmt = _bytestr_format_dict(stmt, self._process_params_dict(params)) + elif isinstance(params, (list, tuple)): + psub = _ParamSubstitutor(self._process_params(params)) + stmt = RE_PY_PARAM.sub(psub, stmt) + if psub.remaining != 0: + raise ProgrammingError( + "Not all parameters were used in the SQL statement" + ) + else: + raise ProgrammingError( + f"Could not process parameters: {type(params).__name__}({params})," + " it must be of type list, tuple or dict" + ) + + self._executed = stmt + if multi: + self._executed_list = [] + return self._execute_iter(self._connection.cmd_query_iter(stmt)) + + try: + self._handle_result(self._connection.cmd_query(stmt)) + except InterfaceError as err: + if self._connection.have_next_result: + raise InterfaceError( + "Use multi=True when executing multiple statements" + ) from err + raise + return None + + def _batch_insert( + self, operation: str, seq_params: Sequence[ParamsSequenceOrDictType] + ) -> Optional[bytes]: + """Implements multi row insert""" + + def remove_comments(match: re.Match) -> str: + """Remove comments from INSERT statements. + + This function is used while removing comments from INSERT + statements. If the matched string is a comment not enclosed + by quotes, it returns an empty string, else the string itself. + """ + if match.group(1): + return "" + return match.group(2) + + tmp = re.sub( + RE_SQL_ON_DUPLICATE, + "", + re.sub(RE_SQL_COMMENT, remove_comments, operation), + ) + + matches = re.search(RE_SQL_INSERT_VALUES, tmp) + if not matches: + raise InterfaceError( + "Failed rewriting statement for multi-row INSERT. Check SQL syntax" + ) + fmt = matches.group(1).encode(self._connection.python_charset) + values = [] + + try: + stmt = operation.encode(self._connection.python_charset) + for params in seq_params: + tmp = fmt + if isinstance(params, dict): + tmp = _bytestr_format_dict(tmp, self._process_params_dict(params)) + else: + psub = _ParamSubstitutor(self._process_params(params)) + tmp = RE_PY_PARAM.sub(psub, tmp) + if psub.remaining != 0: + raise ProgrammingError( + "Not all parameters were used in the SQL statement" + ) + values.append(tmp) + if fmt in stmt: + stmt = stmt.replace(fmt, b",".join(values), 1) + self._executed = stmt + return stmt + return None + except (UnicodeDecodeError, UnicodeEncodeError) as err: + raise ProgrammingError(str(err)) from err + except Error: + raise + except Exception as err: + raise InterfaceError(f"Failed executing the operation; {err}") from None + + def executemany( + self, operation: str, seq_params: Sequence[ParamsSequenceOrDictType] + ) -> Optional[Generator[MySQLCursor, None, None]]: + """Execute the given operation multiple times + + The executemany() method will execute the operation iterating + over the list of parameters in seq_params. + + Example: Inserting 3 new employees and their phone number + + data = [ + ('Jane','555-001'), + ('Joe', '555-001'), + ('John', '555-003') + ] + stmt = "INSERT INTO employees (name, phone) VALUES ('%s','%s)" + cursor.executemany(stmt, data) + + INSERT statements are optimized by batching the data, that is + using the MySQL multiple rows syntax. + + Results are discarded. If they are needed, consider looping over + data using the execute() method. + """ + if not operation or not seq_params: + return None + self._connection.handle_unread_result() + + try: + _ = iter(seq_params) + except TypeError as err: + raise ProgrammingError("Parameters for query must be an Iterable") from err + + # Optimize INSERTs by batching them + if re.match(RE_SQL_INSERT_STMT, operation): + if not seq_params: + self._rowcount = 0 + return None + stmt = self._batch_insert(operation, seq_params) + if stmt is not None: + self._executed = stmt + return self.execute(stmt) + + rowcnt = 0 + try: + for params in seq_params: + self.execute(operation, params) + if self.with_rows and self._have_unread_result(): + self.fetchall() + rowcnt += self._rowcount + except (ValueError, TypeError) as err: + raise InterfaceError(f"Failed executing the operation; {err}") from None + self._rowcount = rowcnt + return None + + def stored_results(self) -> Iterator[MySQLCursor]: + """Returns an iterator for stored results + + This method returns an iterator over results which are stored when + callproc() is called. The iterator will provide MySQLCursorBuffered + instances. + + Returns a iterator. + """ + return iter(self._stored_results) + + def callproc( + self, + procname: str, + args: Sequence[Any] = (), + ) -> Optional[Union[Dict[str, ToPythonOutputTypes], RowType]]: + """Calls a stored procedure with the given arguments + + The arguments will be set during this session, meaning + they will be called like _<procname>__arg<nr> where + <nr> is an enumeration (+1) of the arguments. + + Coding Example: + 1) Defining the Stored Routine in MySQL: + CREATE PROCEDURE multiply(IN pFac1 INT, IN pFac2 INT, OUT pProd INT) + BEGIN + SET pProd := pFac1 * pFac2; + END + + 2) Executing in Python: + args = (5, 5, 0) # 0 is to hold pprod + cursor.callproc('multiply', args) + print(cursor.fetchone()) + + For OUT and INOUT parameters the user should provide the + type of the parameter as well. The argument should be a + tuple with first item as the value of the parameter to pass + and second argument the type of the argument. + + In the above example, one can call callproc method like: + args = (5, 5, (0, 'INT')) + cursor.callproc('multiply', args) + + The type of the argument given in the tuple will be used by + the MySQL CAST function to convert the values in the corresponding + MySQL type (See CAST in MySQL Reference for more information) + + Does not return a value, but a result set will be + available when the CALL-statement execute successfully. + Raises exceptions when something is wrong. + """ + if not procname or not isinstance(procname, str): + raise ValueError("procname must be a string") + + if not isinstance(args, (tuple, list)): + raise ValueError("args must be a sequence") + + argfmt = "@_{name}_arg{index}" + self._stored_results = [] + + results = [] + try: + argnames = [] + argtypes = [] + + # MySQL itself does support calling procedures with their full + # name <database>.<procedure_name>. It's necessary to split + # by '.' and grab the procedure name from procname. + procname_abs = procname.split(".")[-1] + if args: + argvalues = [] + for idx, arg in enumerate(args): + argname = argfmt.format(name=procname_abs, index=idx + 1) + argnames.append(argname) + if isinstance(arg, tuple): + argtypes.append(f" CAST({argname} AS {arg[1]})") + argvalues.append(arg[0]) + else: + argtypes.append(argname) + argvalues.append(arg) + + placeholders = ",".join(f"{arg}=%s" for arg in argnames) + self.execute(f"SET {placeholders}", argvalues) + + call = f"CALL {procname}({','.join(argnames)})" + + # We disable consuming results temporary to make sure we + # getting all results + can_consume_results = self._connection.can_consume_results + for result in self._connection.cmd_query_iter(call): + self._connection.can_consume_results = False + if isinstance(self, (MySQLCursorDict, MySQLCursorBufferedDict)): + cursor_class = MySQLCursorBufferedDict + elif isinstance( + self, + (MySQLCursorNamedTuple, MySQLCursorBufferedNamedTuple), + ): + cursor_class = MySQLCursorBufferedNamedTuple + elif self._raw: + cursor_class = MySQLCursorBufferedRaw + else: + cursor_class = MySQLCursorBuffered + # pylint: disable=protected-access + cur = cursor_class(self._connection.get_self()) + cur._executed = f"(a result of {call})" + cur._handle_result(result) + # pylint: enable=protected-access + if cur.warnings is not None: + self._warnings = cur.warnings + if "columns" in result: + results.append(cur) + self._connection.can_consume_results = can_consume_results + + if argnames: + # Create names aliases to be compatible with namedtuples + args = [ + f"{name} AS {alias}" + for name, alias in zip( + argtypes, [arg.lstrip("@_") for arg in argnames] + ) + ] + select = f"SELECT {','.join(args)}" + self.execute(select) + self._stored_results = results + return self.fetchone() + + self._stored_results = results + return tuple() + + except Error: + raise + except Exception as err: + raise InterfaceError(f"Failed calling stored routine; {err}") from None + + def getlastrowid(self) -> Optional[int]: + """Returns the value generated for an AUTO_INCREMENT column + + Returns the value generated for an AUTO_INCREMENT column by + the previous INSERT or UPDATE statement. + + Returns a long value or None. + """ + return self._last_insert_id + + def _fetch_warnings(self) -> Optional[List[WarningType]]: + """ + Fetch warnings doing a SHOW WARNINGS. Can be called after getting + the result. + + Returns a result set or None when there were no warnings. + """ + res = [] + try: + cur = self._connection.cursor(raw=False) + cur.execute("SHOW WARNINGS") + res = cur.fetchall() + cur.close() + except Exception as err: + raise InterfaceError(f"Failed getting warnings; {err}") from None + + if res: + return res + + return None + + def _handle_warnings(self) -> None: + """Handle possible warnings after all results are consumed. + + Raises: + Error: Also raises exceptions if raise_on_warnings is set. + """ + if self._connection.get_warnings and self._warning_count: + self._warnings = self._fetch_warnings() + + if not self._warnings: + return + + err = get_mysql_exception( + self._warnings[0][1], + self._warnings[0][2], + warning=not self._connection.raise_on_warnings, + ) + + if self._connection.raise_on_warnings: + raise err + + warnings.warn(err, stacklevel=4) + + def _handle_eof(self, eof: EofPacketType) -> None: + """Handle EOF packet""" + self._connection.unread_result = False + self._nextrow = (None, None) + self._warning_count = eof["warning_count"] + self._handle_warnings() + + def _fetch_row(self, raw: bool = False) -> Optional[RowType]: + """Returns the next row in the result set + + Returns a tuple or None. + """ + if not self._have_unread_result(): + return None + row = None + + if self._nextrow == (None, None): + (row, eof) = self._connection.get_row( + binary=self._binary, columns=self.description, raw=raw + ) + else: + (row, eof) = self._nextrow + + if row: + self._nextrow = self._connection.get_row( + binary=self._binary, columns=self.description, raw=raw + ) + eof = self._nextrow[1] + if eof is not None: + self._handle_eof(eof) + if self._rowcount == -1: + self._rowcount = 1 + else: + self._rowcount += 1 + if eof: + self._handle_eof(eof) + + return row + + def fetchone(self) -> Optional[RowType]: + """Return next row of a query result set. + + Returns: + tuple or None: A row from query result set. + """ + self._check_executed() + return self._fetch_row() + + def fetchmany(self, size: Optional[int] = None) -> List[RowType]: + """Return the next set of rows of a query result set. + + When no more rows are available, it returns an empty list. + The number of rows returned can be specified using the size argument, + which defaults to one. + + Returns: + list: The next set of rows of a query result set. + """ + self._check_executed() + res = [] + cnt = size or self.arraysize + while cnt > 0 and self._have_unread_result(): + cnt -= 1 + row = self.fetchone() + if row: + res.append(row) + return res + + def fetchall(self) -> List[RowType]: + """Return all rows of a query result set. + + Returns: + list: A list of tuples with all rows of a query result set. + """ + self._check_executed() + if not self._have_unread_result(): + return [] + + (rows, eof) = self._connection.get_rows() + if self._nextrow[0]: + rows.insert(0, self._nextrow[0]) + + self._handle_eof(eof) + rowcount = len(rows) + if rowcount >= 0 and self._rowcount == -1: + self._rowcount = 0 + self._rowcount += rowcount + return rows + + @property + def column_names(self) -> Tuple[str, ...]: + """Returns column names + + This property returns the columns names as a tuple. + + Returns a tuple. + """ + if not self.description: + return tuple() + return tuple(d[0] for d in self.description) + + @property + def statement(self) -> Optional[str]: + """Returns the executed statement + + This property returns the executed statement. When multiple + statements were executed, the current statement in the iterator + will be returned. + """ + if self._executed is None: + return None + try: + return self._executed.strip().decode("utf-8") # type: ignore[union-attr] + except (AttributeError, UnicodeDecodeError): + return self._executed.strip() # type: ignore[return-value] + + @property + def with_rows(self) -> bool: + """Returns whether the cursor could have rows returned + + This property returns True when column descriptions are available + and possibly also rows, which will need to be fetched. + + Returns True or False. + """ + if not self.description: + return False + return True + + def __str__(self) -> str: + fmt = "{class_name}: {stmt}" + if self._executed: + try: + executed = self._executed.decode("utf-8") # type: ignore[union-attr] + except AttributeError: + executed = self._executed + if len(executed) > 40: + executed = executed[:40] + ".." + else: + executed = "(Nothing executed yet)" + return fmt.format(class_name=self.__class__.__name__, stmt=executed) + + +class MySQLCursorBuffered(MySQLCursor): + """Cursor which fetches rows within execute()""" + + def __init__( + self, connection: Optional[Type[MySQLConnectionAbstract]] = None + ) -> None: + super().__init__(connection) + self._rows: Optional[List[RowType]] = None + self._next_row: int = 0 + + def _handle_resultset(self) -> None: + (self._rows, eof) = self._connection.get_rows() + self._rowcount = len(self._rows) + self._handle_eof(eof) + self._next_row = 0 + try: + self._connection.unread_result = False + except AttributeError: + pass + + def reset(self, free: bool = True) -> None: + self._rows = None + + def _fetch_row(self, raw: bool = False) -> Optional[RowType]: + row = None + try: + row = self._rows[self._next_row] + except (IndexError, TypeError): + return None + self._next_row += 1 + return row + + def fetchone(self) -> Optional[RowType]: + """Return next row of a query result set. + + Returns: + tuple or None: A row from query result set. + """ + self._check_executed() + return self._fetch_row() + + def fetchall(self) -> List[RowType]: + """Return all rows of a query result set. + + Returns: + list: A list of tuples with all rows of a query result set. + """ + if self._executed is None or self._rows is None: + raise InterfaceError(ERR_NO_RESULT_TO_FETCH) + res = [] + res = self._rows[self._next_row :] + self._next_row = len(self._rows) + return res + + def fetchmany(self, size: Optional[int] = None) -> List[RowType]: + """Return the next set of rows of a query result set. + + When no more rows are available, it returns an empty list. + The number of rows returned can be specified using the size argument, + which defaults to one. + + Returns: + list: The next set of rows of a query result set. + """ + self._check_executed() + res = [] + cnt = size or self.arraysize + while cnt > 0: + cnt -= 1 + row = self.fetchone() + if row: + res.append(row) + + return res + + @property + def with_rows(self) -> bool: + return self._rows is not None + + +class MySQLCursorRaw(MySQLCursor): + """ + Skips conversion from MySQL datatypes to Python types when fetching rows. + """ + + _raw: bool = True + + def fetchone(self) -> Optional[RowType]: + """Return next row of a query result set. + + Returns: + tuple or None: A row from query result set. + """ + self._check_executed() + return self._fetch_row(raw=True) + + def fetchall(self) -> List[RowType]: + """Return all rows of a query result set. + + Returns: + list: A list of tuples with all rows of a query result set. + """ + self._check_executed() + if not self._have_unread_result(): + return [] + (rows, eof) = self._connection.get_rows(raw=True) + if self._nextrow[0]: + rows.insert(0, self._nextrow[0]) + self._handle_eof(eof) + rowcount = len(rows) + if rowcount >= 0 and self._rowcount == -1: + self._rowcount = 0 + self._rowcount += rowcount + return rows + + +class MySQLCursorBufferedRaw(MySQLCursorBuffered): + """ + Cursor which skips conversion from MySQL datatypes to Python types when + fetching rows and fetches rows within execute(). + """ + + _raw: bool = True + + def _handle_resultset(self) -> None: + (self._rows, eof) = self._connection.get_rows(raw=self._raw) + self._rowcount = len(self._rows) + self._handle_eof(eof) + self._next_row = 0 + try: + self._connection.unread_result = False + except AttributeError: + pass + + def fetchone(self) -> Optional[RowType]: + """Return next row of a query result set. + + Returns: + tuple or None: A row from query result set. + """ + self._check_executed() + return self._fetch_row() + + def fetchall(self) -> List[RowType]: + """Return all rows of a query result set. + + Returns: + list: A list of tuples with all rows of a query result set. + """ + self._check_executed() + return list(self._rows[self._next_row :]) + + @property + def with_rows(self) -> bool: + return self._rows is not None + + +class MySQLCursorPrepared(MySQLCursor): + """Cursor using MySQL Prepared Statements""" + + def __init__(self, connection: Optional[Type[MySQLConnectionAbstract]] = None): + super().__init__(connection) + self._rows: Optional[List[RowType]] = None + self._next_row: int = 0 + self._prepared: Optional[Dict[str, Union[int, List[DescriptionType]]]] = None + self._binary: bool = True + self._have_result: Optional[bool] = None + self._last_row_sent: bool = False + self._cursor_exists: bool = False + + def reset(self, free: bool = True) -> None: + if self._prepared: + try: + self._connection.cmd_stmt_close(self._prepared["statement_id"]) + except Error: + # We tried to deallocate, but it's OK when we fail. + pass + self._prepared = None + self._last_row_sent = False + self._cursor_exists = False + + def _handle_noresultset(self, res: ResultType) -> None: + self._handle_server_status(res.get("status_flag", res.get("server_status", 0))) + super()._handle_noresultset(res) + + def _handle_server_status(self, flags: int) -> None: + """Check for SERVER_STATUS_CURSOR_EXISTS and + SERVER_STATUS_LAST_ROW_SENT flags set by the server. + """ + self._cursor_exists = flags & ServerFlag.STATUS_CURSOR_EXISTS != 0 + self._last_row_sent = flags & ServerFlag.STATUS_LAST_ROW_SENT != 0 + + def _handle_eof(self, eof: EofPacketType) -> None: + self._handle_server_status(eof.get("status_flag", eof.get("server_status", 0))) + super()._handle_eof(eof) + + def callproc(self, procname: Any, args: Any = ()) -> NoReturn: + """Calls a stored procedue + + Not supported with MySQLCursorPrepared. + """ + raise NotSupportedError() + + def close(self) -> None: + """Close the cursor + + This method will try to deallocate the prepared statement and close + the cursor. + """ + self.reset() + super().close() + + def _row_to_python(self, rowdata: Any, desc: Any = None) -> Any: + """Convert row data from MySQL to Python types + + The conversion is done while reading binary data in the + protocol module. + """ + + def _handle_result(self, result: ResultType) -> None: + """Handle result after execution""" + if isinstance(result, dict): + self._connection.unread_result = False + self._have_result = False + self._handle_noresultset(result) + else: + self._description = result[1] + self._connection.unread_result = True + self._have_result = True + + if "status_flag" in result[2]: # type: ignore[operator] + self._handle_server_status(result[2]["status_flag"]) + elif "server_status" in result[2]: # type: ignore[operator] + self._handle_server_status(result[2]["server_status"]) + + def execute( + self, + operation: StrOrBytes, + params: Optional[ParamsSequenceOrDictType] = None, + multi: bool = False, + ) -> None: # multi is unused + """Prepare and execute a MySQL Prepared Statement + + This method will prepare the given operation and execute it using + the optionally given parameters. + + If the cursor instance already had a prepared statement, it is + first closed. + + Note: argument "multi" is unused. + """ + charset = self._connection.charset + if charset == "utf8mb4": + charset = "utf8" + + if not isinstance(operation, str): + try: + operation = operation.decode(charset) + except UnicodeDecodeError as err: + raise ProgrammingError(str(err)) from err + + if isinstance(params, dict): + replacement_keys = re.findall(RE_SQL_PYTHON_CAPTURE_PARAM_NAME, operation) + try: + # Replace params dict with params tuple in correct order. + params = tuple(params[key] for key in replacement_keys) + except KeyError as err: + raise ProgrammingError( + "Not all placeholders were found in the parameters dict" + ) from err + # Convert %(name)s to ? before sending it to MySQL + operation = re.sub(RE_SQL_PYTHON_REPLACE_PARAM, "?", operation) + + if operation is not self._executed: + if self._prepared: + self._connection.cmd_stmt_close(self._prepared["statement_id"]) + self._executed = operation + + try: + operation = operation.encode(charset) + except UnicodeEncodeError as err: + raise ProgrammingError(str(err)) from err + + if b"%s" in operation: + # Convert %s to ? before sending it to MySQL + operation = re.sub(RE_SQL_FIND_PARAM, b"?", operation) + + try: + self._prepared = self._connection.cmd_stmt_prepare(operation) + except Error: + self._executed = None + raise + + self._connection.cmd_stmt_reset(self._prepared["statement_id"]) + + if self._prepared["parameters"] and not params: + return + if params: + if not isinstance(params, (tuple, list)): + raise ProgrammingError( + errno=1210, + msg=f"Incorrect type of argument: {type(params).__name__}({params})" + ", it must be of type tuple or list the argument given to " + "the prepared statement", + ) + if len(self._prepared["parameters"]) != len(params): + raise ProgrammingError( + errno=1210, + msg="Incorrect number of arguments executing prepared statement", + ) + + if params is None: + params = () + res = self._connection.cmd_stmt_execute( + self._prepared["statement_id"], + data=params, + parameters=self._prepared["parameters"], + ) + self._handle_result(res) + + def executemany( + self, + operation: str, + seq_params: Sequence[ParamsSequenceType], + ) -> None: + """Prepare and execute a MySQL Prepared Statement many times + + This method will prepare the given operation and execute with each + tuple found the list seq_params. + + If the cursor instance already had a prepared statement, it is + first closed. + + executemany() simply calls execute(). + """ + rowcnt = 0 + try: + for params in seq_params: + self.execute(operation, params) + if self.with_rows and self._have_unread_result(): + self.fetchall() + rowcnt += self._rowcount + except (ValueError, TypeError) as err: + raise InterfaceError(f"Failed executing the operation; {err}") from None + self._rowcount = rowcnt + + def fetchone(self) -> Optional[RowType]: + """Return next row of a query result set. + + Returns: + tuple or None: A row from query result set. + """ + self._check_executed() + if self._cursor_exists: + self._connection.cmd_stmt_fetch(self._prepared["statement_id"]) + return self._fetch_row() or None + + def fetchmany(self, size: Optional[int] = None) -> List[RowType]: + """Return the next set of rows of a query result set. + + When no more rows are available, it returns an empty list. + The number of rows returned can be specified using the size argument, + which defaults to one. + + Returns: + list: The next set of rows of a query result set. + """ + self._check_executed() + res = [] + cnt = size or self.arraysize + while cnt > 0 and self._have_unread_result(): + cnt -= 1 + row = self._fetch_row() + if row: + res.append(row) + return res + + def fetchall(self) -> List[RowType]: + """Return all rows of a query result set. + + Returns: + list: A list of tuples with all rows of a query result set. + """ + self._check_executed() + rows = [] + if self._nextrow[0]: + rows.append(self._nextrow[0]) + while self._have_unread_result(): + if self._cursor_exists: + self._connection.cmd_stmt_fetch( + self._prepared["statement_id"], MAX_RESULTS + ) + (tmp, eof) = self._connection.get_rows( + binary=self._binary, columns=self.description + ) + rows.extend(tmp) + self._handle_eof(eof) + self._rowcount = len(rows) + return rows + + +class MySQLCursorDict(MySQLCursor): + """ + Cursor fetching rows as dictionaries. + + The fetch methods of this class will return dictionaries instead of tuples. + Each row is a dictionary that looks like: + row = { + "col1": value1, + "col2": value2 + } + """ + + def _row_to_python( + self, + rowdata: RowType, + desc: Optional[List[DescriptionType]] = None, # pylint: disable=unused-argument + ) -> Optional[Dict[str, ToPythonOutputTypes]]: + """Convert a MySQL text result row to Python types + + Returns a dictionary. + """ + return dict(zip(self.column_names, rowdata)) if rowdata else None + + def fetchone(self) -> Optional[Dict[str, ToPythonOutputTypes]]: + """Return next row of a query result set. + + Returns: + dict or None: A dict from query result set. + """ + return self._row_to_python(super().fetchone(), self.description) + + def fetchall(self) -> List[Optional[Dict[str, ToPythonOutputTypes]]]: + """Return all rows of a query result set. + + Returns: + list: A list of dictionaries with all rows of a query + result set where column names are used as keys. + """ + return [ + self._row_to_python(row, self.description) + for row in super().fetchall() + if row + ] + + +class MySQLCursorNamedTuple(MySQLCursor): + """ + Cursor fetching rows as named tuple. + + The fetch methods of this class will return namedtuples instead of tuples. + Each row is returned as a namedtuple and the values can be accessed as: + row.col1, row.col2 + """ + + def _row_to_python( + self, + rowdata: RowType, + desc: Optional[List[DescriptionType]] = None, # pylint: disable=unused-argument + ) -> Optional[RowType]: + """Convert a MySQL text result row to Python types + + Returns a named tuple. + """ + row = rowdata + + if row: + columns = tuple(self.column_names) + try: + named_tuple = NAMED_TUPLE_CACHE[columns] + except KeyError: + named_tuple = namedtuple("Row", columns) # type:ignore[no-redef, misc] + NAMED_TUPLE_CACHE[columns] = named_tuple + return named_tuple(*row) + return None + + def fetchone(self) -> Optional[RowType]: + """Return next row of a query result set. + + Returns: + tuple or None: A row from query result set. + """ + row = super().fetchone() + if not row: + return None + return ( + self._row_to_python(row, self.description) + if hasattr(self._connection, "converter") + else row + ) + + def fetchall(self) -> List[Optional[RowType]]: + """Return all rows of a query result set. + + Returns: + list: A list of tuples with all rows of a query result set. + """ + return [ + self._row_to_python(row, self.description) + for row in super().fetchall() + if row + ] + + +class MySQLCursorBufferedDict(MySQLCursorDict, MySQLCursorBuffered): + """ + Buffered Cursor fetching rows as dictionaries. + """ + + def fetchone(self) -> Optional[Dict[str, ToPythonOutputTypes]]: + """Return next row of a query result set. + + Returns: + tuple or None: A row from query result set. + """ + self._check_executed() + row = self._fetch_row() + if row: + return self._row_to_python(row, self.description) + return None + + def fetchall(self) -> List[Optional[Dict[str, ToPythonOutputTypes]]]: + """Return all rows of a query result set. + + Returns: + list: A list of tuples with all rows of a query result set. + """ + if self._executed is None or self._rows is None: + raise InterfaceError(ERR_NO_RESULT_TO_FETCH) + res = [] + for row in self._rows[self._next_row :]: + res.append(self._row_to_python(row, self.description)) + self._next_row = len(self._rows) + return res + + +class MySQLCursorBufferedNamedTuple(MySQLCursorNamedTuple, MySQLCursorBuffered): + """ + Buffered Cursor fetching rows as named tuple. + """ + + def fetchone(self) -> Optional[RowType]: + """Return next row of a query result set. + + Returns: + tuple or None: A row from query result set. + """ + self._check_executed() + row = self._fetch_row() + if row: + return self._row_to_python(row, self.description) + return None + + def fetchall(self) -> List[Optional[RowType]]: + """Return all rows of a query result set. + + Returns: + list: A list of tuples with all rows of a query result set. + """ + if self._executed is None or self._rows is None: + raise InterfaceError(ERR_NO_RESULT_TO_FETCH) + res = [] + for row in self._rows[self._next_row :]: + res.append(self._row_to_python(row, self.description)) + self._next_row = len(self._rows) + return res + + +class MySQLCursorPreparedDict(MySQLCursorDict, MySQLCursorPrepared): # type: ignore[misc] + """ + This class is a blend of features from MySQLCursorDict and MySQLCursorPrepared + + Multiple inheritance in python is allowed but care must be taken + when assuming methods resolution. In the case of multiple + inheritance, a given attribute is first searched in the current + class if it's not found then it's searched in the parent classes. + The parent classes are searched in a left-right fashion and each + class is searched once. + Based on python's attribute resolution, in this case, attributes + are searched as follows: + 1. MySQLCursorPreparedDict (current class) + 2. MySQLCursorDict (left parent class) + 3. MySQLCursorPrepared (right parent class) + 4. MySQLCursor (base class) + """ + + def fetchmany( + self, size: Optional[int] = None + ) -> List[Dict[str, ToPythonOutputTypes]]: + """Return the next set of rows of a query result set. + + When no more rows are available, it returns an empty list. + The number of rows returned can be specified using the size argument, + which defaults to one. + + Returns: + list: The next set of rows of a query result set represented + as a list of dictionaries where column names are used as keys. + """ + return [ + self._row_to_python(row, self.description) + for row in super().fetchmany(size=size) + if row + ] + + +class MySQLCursorPreparedNamedTuple(MySQLCursorNamedTuple, MySQLCursorPrepared): + """ + This class is a blend of features from MySQLCursorNamedTuple and MySQLCursorPrepared + """ + + def fetchmany(self, size: Optional[int] = None) -> List[RowType]: + """Return the next set of rows of a query result set. + + When no more rows are available, it returns an empty list. + The number of rows returned can be specified using the size argument, + which defaults to one. + + Returns: + list: The next set of rows of a query result set represented + as a list of named tuples where column names are used as names. + """ + return [ + self._row_to_python(row, self.description) + for row in super().fetchmany(size=size) + if row + ] + + +class MySQLCursorPreparedRaw(MySQLCursorPrepared): + """ + This class is a blend of features from MySQLCursorRaw and MySQLCursorPrepared + """ + + _raw: bool = True + + def fetchone(self) -> Optional[RowType]: + """Return next row of a query result set. + + Returns: + tuple or None: A row from query result set. + """ + self._check_executed() + if self._cursor_exists: + self._connection.cmd_stmt_fetch(self._prepared["statement_id"]) + return self._fetch_row(raw=self._raw) or None + + def fetchmany(self, size: Optional[int] = None) -> List[RowType]: + """Return the next set of rows of a query result set. + + When no more rows are available, it returns an empty list. + The number of rows returned can be specified using the size argument, + which defaults to one. + + Returns: + list: The next set of rows of a query result set. + """ + self._check_executed() + res = [] + cnt = size or self.arraysize + while cnt > 0 and self._have_unread_result(): + cnt -= 1 + row = self._fetch_row(raw=self._raw) + if row: + res.append(row) + return res + + def fetchall(self) -> List[RowType]: + """Return all rows of a query result set. + + Returns: + list: A list of tuples with all rows of a query result set. + """ + self._check_executed() + rows = [] + if self._nextrow[0]: + rows.append(self._nextrow[0]) + while self._have_unread_result(): + if self._cursor_exists: + self._connection.cmd_stmt_fetch( + self._prepared["statement_id"], MAX_RESULTS + ) + (tmp, eof) = self._connection.get_rows( + raw=self._raw, binary=self._binary, columns=self.description + ) + rows.extend(tmp) + self._handle_eof(eof) + self._rowcount = len(rows) + return rows diff --git a/virt/lib/python3.9/site-packages/mysql/connector/django/introspection 3.py b/virt/lib/python3.9/site-packages/mysql/connector/django/introspection 3.py new file mode 100644 index 00000000..0de48612 --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysql/connector/django/introspection 3.py @@ -0,0 +1,461 @@ +# Copyright (c) 2020, 2022, Oracle and/or its affiliates. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, as +# published by the Free Software Foundation. +# +# This program is also distributed with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an +# additional permission to link the program and your derivative works +# with the separately licensed software that they have included with +# MySQL. +# +# Without limiting anything contained in the foregoing, this file, +# which is part of MySQL Connector/Python, is also subject to the +# Universal FOSS Exception, version 1.0, a copy of which can be found at +# http://oss.oracle.com/licenses/universal-foss-exception. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License, version 2.0, for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# mypy: disable-error-code="override,attr-defined,call-arg" + +"""Database Introspection.""" + +from collections import namedtuple +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple + +import sqlparse + +from django import VERSION as DJANGO_VERSION +from django.db.backends.base.introspection import ( + BaseDatabaseIntrospection, + FieldInfo as BaseFieldInfo, + TableInfo, +) +from django.db.models import Index +from django.utils.datastructures import OrderedSet + +from mysql.connector.constants import FieldType + +# from .base import CursorWrapper produces a circular import error, +# avoiding importing CursorWrapper explicitly, using a documented +# trick; write the imports inside if TYPE_CHECKING: so that they +# are not executed at runtime. +# Ref: https://buildmedia.readthedocs.org/media/pdf/mypy/stable/mypy.pdf [page 42] +if TYPE_CHECKING: + # CursorWraper is used exclusively for type hinting + from mysql.connector.django.base import CursorWrapper + +# Based on my investigation, named tuples to +# comply with mypy need to define a static list or tuple +# for field_names (second argument). In this case, the field +# names are created dynamically for FieldInfo which triggers +# a mypy error. The solution is not straightforward since +# FieldInfo attributes are Django version dependent. Code +# refactory is needed to fix this issue. +FieldInfo = namedtuple( # type: ignore[misc] + "FieldInfo", + BaseFieldInfo._fields + ("extra", "is_unsigned", "has_json_constraint"), +) +if DJANGO_VERSION < (3, 2, 0): + InfoLine = namedtuple( + "InfoLine", + "col_name data_type max_len num_prec num_scale extra column_default " + "is_unsigned", + ) +else: + InfoLine = namedtuple( # type: ignore[no-redef] + "InfoLine", + "col_name data_type max_len num_prec num_scale extra column_default " + "collation is_unsigned", + ) + + +class DatabaseIntrospection(BaseDatabaseIntrospection): + """Encapsulate backend-specific introspection utilities.""" + + data_types_reverse = { + FieldType.BLOB: "TextField", + FieldType.DECIMAL: "DecimalField", + FieldType.NEWDECIMAL: "DecimalField", + FieldType.DATE: "DateField", + FieldType.DATETIME: "DateTimeField", + FieldType.DOUBLE: "FloatField", + FieldType.FLOAT: "FloatField", + FieldType.INT24: "IntegerField", + FieldType.LONG: "IntegerField", + FieldType.LONGLONG: "BigIntegerField", + FieldType.SHORT: "SmallIntegerField", + FieldType.STRING: "CharField", + FieldType.TIME: "TimeField", + FieldType.TIMESTAMP: "DateTimeField", + FieldType.TINY: "IntegerField", + FieldType.TINY_BLOB: "TextField", + FieldType.MEDIUM_BLOB: "TextField", + FieldType.LONG_BLOB: "TextField", + FieldType.VAR_STRING: "CharField", + } + + def get_field_type(self, data_type: str, description: FieldInfo) -> str: + field_type = super().get_field_type(data_type, description) # type: ignore[arg-type] + if "auto_increment" in description.extra: + if field_type == "IntegerField": + return "AutoField" + if field_type == "BigIntegerField": + return "BigAutoField" + if field_type == "SmallIntegerField": + return "SmallAutoField" + if description.is_unsigned: + if field_type == "BigIntegerField": + return "PositiveBigIntegerField" + if field_type == "IntegerField": + return "PositiveIntegerField" + if field_type == "SmallIntegerField": + return "PositiveSmallIntegerField" + # JSON data type is an alias for LONGTEXT in MariaDB, use check + # constraints clauses to introspect JSONField. + if description.has_json_constraint: + return "JSONField" + return field_type + + def get_table_list(self, cursor: "CursorWrapper") -> List[TableInfo]: + """Return a list of table and view names in the current database.""" + cursor.execute("SHOW FULL TABLES") + return [ + TableInfo(row[0], {"BASE TABLE": "t", "VIEW": "v"}.get(row[1])) + for row in cursor.fetchall() + ] + + def get_table_description( + self, cursor: "CursorWrapper", table_name: str + ) -> List[FieldInfo]: + """ + Return a description of the table with the DB-API cursor.description + interface." + """ + json_constraints: Dict[Any, Any] = {} + # A default collation for the given table. + cursor.execute( + """ + SELECT table_collation + FROM information_schema.tables + WHERE table_schema = DATABASE() + AND table_name = %s + """, + [table_name], + ) + row = cursor.fetchone() + default_column_collation = row[0] if row else "" + # information_schema database gives more accurate results for some figures: + # - varchar length returned by cursor.description is an internal length, + # not visible length (#5725) + # - precision and scale (for decimal fields) (#5014) + # - auto_increment is not available in cursor.description + if DJANGO_VERSION < (3, 2, 0): + cursor.execute( + """ + SELECT + column_name, data_type, character_maximum_length, + numeric_precision, numeric_scale, extra, column_default, + CASE + WHEN column_type LIKE '%% unsigned' THEN 1 + ELSE 0 + END AS is_unsigned + FROM information_schema.columns + WHERE table_name = %s AND table_schema = DATABASE() + """, + [table_name], + ) + else: + cursor.execute( + """ + SELECT + column_name, data_type, character_maximum_length, + numeric_precision, numeric_scale, extra, column_default, + CASE + WHEN collation_name = %s THEN NULL + ELSE collation_name + END AS collation_name, + CASE + WHEN column_type LIKE '%% unsigned' THEN 1 + ELSE 0 + END AS is_unsigned + FROM information_schema.columns + WHERE table_name = %s AND table_schema = DATABASE() + """, + [default_column_collation, table_name], + ) + field_info = {line[0]: InfoLine(*line) for line in cursor.fetchall()} + + cursor.execute( + f"SELECT * FROM {self.connection.ops.quote_name(table_name)} LIMIT 1" + ) + + def to_int(i: Any) -> Optional[int]: + return int(i) if i is not None else i + + fields = [] + for line in cursor.description: + info = field_info[line[0]] + if DJANGO_VERSION < (3, 2, 0): + fields.append( + FieldInfo( + *line[:3], + to_int(info.max_len) or line[3], + to_int(info.num_prec) or line[4], + to_int(info.num_scale) or line[5], + line[6], + info.column_default, + info.extra, + info.is_unsigned, + line[0] in json_constraints, + ) + ) + else: + fields.append( + FieldInfo( + *line[:3], + to_int(info.max_len) or line[3], + to_int(info.num_prec) or line[4], + to_int(info.num_scale) or line[5], + line[6], + info.column_default, + info.collation, + info.extra, + info.is_unsigned, + line[0] in json_constraints, + ) + ) + return fields + + def get_indexes( + self, cursor: "CursorWrapper", table_name: str + ) -> Dict[int, Dict[str, bool]]: + """Return indexes from table.""" + cursor.execute(f"SHOW INDEX FROM {self.connection.ops.quote_name(table_name)}") + # Do a two-pass search for indexes: on first pass check which indexes + # are multicolumn, on second pass check which single-column indexes + # are present. + rows = list(cursor.fetchall()) + multicol_indexes = set() + for row in rows: + if row[3] > 1: + multicol_indexes.add(row[2]) + indexes: Dict[int, Dict[str, bool]] = {} + for row in rows: + if row[2] in multicol_indexes: + continue + if row[4] not in indexes: + indexes[row[4]] = {"primary_key": False, "unique": False} + # It's possible to have the unique and PK constraints in + # separate indexes. + if row[2] == "PRIMARY": + indexes[row[4]]["primary_key"] = True + if not row[1]: + indexes[row[4]]["unique"] = True + return indexes + + def get_primary_key_column( + self, cursor: "CursorWrapper", table_name: str + ) -> Optional[int]: + """ + Returns the name of the primary key column for the given table + """ + for column in self.get_indexes(cursor, table_name).items(): + if column[1]["primary_key"]: + return column[0] + return None + + def get_sequences( + self, cursor: "CursorWrapper", table_name: str, table_fields: Any = () + ) -> List[Dict[str, str]]: + for field_info in self.get_table_description(cursor, table_name): + if "auto_increment" in field_info.extra: + # MySQL allows only one auto-increment column per table. + return [{"table": table_name, "column": field_info.name}] + return [] + + def get_relations( + self, cursor: "CursorWrapper", table_name: str + ) -> Dict[str, Tuple[str, str]]: + """ + Return a dictionary of {field_name: (field_name_other_table, other_table)} + representing all relationships to the given table. + """ + constraints = self.get_key_columns(cursor, table_name) + relations = {} + for my_fieldname, other_table, other_field in constraints: + relations[my_fieldname] = (other_field, other_table) + return relations + + def get_key_columns( + self, cursor: "CursorWrapper", table_name: str + ) -> List[Tuple[str, str, str]]: + """ + Return a list of (column_name, referenced_table_name, referenced_column_name) + for all key columns in the given table. + """ + key_columns: List[Any] = [] + cursor.execute( + """ + SELECT column_name, referenced_table_name, referenced_column_name + FROM information_schema.key_column_usage + WHERE table_name = %s + AND table_schema = DATABASE() + AND referenced_table_name IS NOT NULL + AND referenced_column_name IS NOT NULL""", + [table_name], + ) + key_columns.extend(cursor.fetchall()) + return key_columns + + def get_storage_engine(self, cursor: "CursorWrapper", table_name: str) -> str: + """ + Retrieve the storage engine for a given table. Return the default + storage engine if the table doesn't exist. + """ + cursor.execute( + "SELECT engine FROM information_schema.tables WHERE table_name = %s", + [table_name], + ) + result = cursor.fetchone() + # pylint: disable=protected-access + if not result: + return self.connection.features._mysql_storage_engine + # pylint: enable=protected-access + return result[0] + + def _parse_constraint_columns( + self, check_clause: Any, columns: Set[str] + ) -> OrderedSet: + check_columns: OrderedSet = OrderedSet() + statement = sqlparse.parse(check_clause)[0] + tokens = (token for token in statement.flatten() if not token.is_whitespace) + for token in tokens: + if ( + token.ttype == sqlparse.tokens.Name + and self.connection.ops.quote_name(token.value) == token.value + and token.value[1:-1] in columns + ): + check_columns.add(token.value[1:-1]) + return check_columns + + def get_constraints( + self, cursor: "CursorWrapper", table_name: str + ) -> Dict[str, Any]: + """ + Retrieve any constraints or keys (unique, pk, fk, check, index) across + one or more columns. + """ + constraints: Dict[str, Any] = {} + # Get the actual constraint names and columns + name_query = """ + SELECT kc.`constraint_name`, kc.`column_name`, + kc.`referenced_table_name`, kc.`referenced_column_name` + FROM information_schema.key_column_usage AS kc + WHERE + kc.table_schema = DATABASE() AND + kc.table_name = %s + ORDER BY kc.`ordinal_position` + """ + cursor.execute(name_query, [table_name]) + for constraint, column, ref_table, ref_column in cursor.fetchall(): + if constraint not in constraints: + constraints[constraint] = { + "columns": OrderedSet(), + "primary_key": False, + "unique": False, + "index": False, + "check": False, + "foreign_key": (ref_table, ref_column) if ref_column else None, + } + if self.connection.features.supports_index_column_ordering: + constraints[constraint]["orders"] = [] + constraints[constraint]["columns"].add(column) + # Now get the constraint types + type_query = """ + SELECT c.constraint_name, c.constraint_type + FROM information_schema.table_constraints AS c + WHERE + c.table_schema = DATABASE() AND + c.table_name = %s + """ + cursor.execute(type_query, [table_name]) + for constraint, kind in cursor.fetchall(): + if kind.lower() == "primary key": + constraints[constraint]["primary_key"] = True + constraints[constraint]["unique"] = True + elif kind.lower() == "unique": + constraints[constraint]["unique"] = True + # Add check constraints. + if self.connection.features.can_introspect_check_constraints: + unnamed_constraints_index = 0 + columns = { + info.name for info in self.get_table_description(cursor, table_name) + } + type_query = """ + SELECT cc.constraint_name, cc.check_clause + FROM + information_schema.check_constraints AS cc, + information_schema.table_constraints AS tc + WHERE + cc.constraint_schema = DATABASE() AND + tc.table_schema = cc.constraint_schema AND + cc.constraint_name = tc.constraint_name AND + tc.constraint_type = 'CHECK' AND + tc.table_name = %s + """ + cursor.execute(type_query, [table_name]) + for constraint, check_clause in cursor.fetchall(): + constraint_columns = self._parse_constraint_columns( + check_clause, columns + ) + # Ensure uniqueness of unnamed constraints. Unnamed unique + # and check columns constraints have the same name as + # a column. + if set(constraint_columns) == {constraint}: + unnamed_constraints_index += 1 + constraint = f"__unnamed_constraint_{unnamed_constraints_index}__" + constraints[constraint] = { + "columns": constraint_columns, + "primary_key": False, + "unique": False, + "index": False, + "check": True, + "foreign_key": None, + } + # Now add in the indexes + cursor.execute(f"SHOW INDEX FROM {self.connection.ops.quote_name(table_name)}") + for _, _, index, _, column, order, type_ in [ + x[:6] + (x[10],) for x in cursor.fetchall() + ]: + if index not in constraints: + constraints[index] = { + "columns": OrderedSet(), + "primary_key": False, + "unique": False, + "check": False, + "foreign_key": None, + } + if self.connection.features.supports_index_column_ordering: + constraints[index]["orders"] = [] + constraints[index]["index"] = True + constraints[index]["type"] = ( + Index.suffix if type_ == "BTREE" else type_.lower() + ) + constraints[index]["columns"].add(column) + if self.connection.features.supports_index_column_ordering: + constraints[index]["orders"].append("DESC" if order == "D" else "ASC") + # Convert the sorted sets to lists + for constraint in constraints.values(): + constraint["columns"] = list(constraint["columns"]) + return constraints diff --git a/virt/lib/python3.9/site-packages/mysql/connector/opentelemetry/instrumentation 3.py b/virt/lib/python3.9/site-packages/mysql/connector/opentelemetry/instrumentation 3.py new file mode 100644 index 00000000..f2ebd30e --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysql/connector/opentelemetry/instrumentation 3.py @@ -0,0 +1,514 @@ +"""MySQL instrumentation supporting mysql-connector.""" +# mypy: disable-error-code="no-redef" +# pylint: disable=protected-access,global-statement,invalid-name + +from __future__ import annotations + +import functools +import re + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Any, Callable, Collection, Dict, Optional, Union + +# pylint: disable=cyclic-import +if TYPE_CHECKING: + # `TYPE_CHECKING` is always False at run time, hence circular import + # will not happen at run time (no error happens whatsoever). + # Since pylint is a static checker it happens that `TYPE_CHECKING` + # is True when analyzing the code which makes pylint believe there + # is a circular import issue when there isn't. + + from ..abstracts import MySQLConnectionAbstract + from ..connection import MySQLConnection + from ..cursor import MySQLCursor + from ..pooling import PooledMySQLConnection + + try: + from ..connection_cext import CMySQLConnection + from ..cursor_cext import CMySQLCursor + except ImportError: + # The cext is not available. + pass + +from ... import connector +from ..constants import CNX_POOL_ARGS, DEFAULT_CONFIGURATION +from ..logger import logger +from ..version import VERSION_TEXT + +try: + # pylint: disable=unused-import + # try to load otel from the system + from opentelemetry import trace # check api + from opentelemetry.sdk.trace import TracerProvider # check sdk + from opentelemetry.semconv.trace import SpanAttributes # check semconv + + OTEL_SYSTEM_AVAILABLE = True +except ImportError: + try: + # falling back to the bundled installation + from mysql.opentelemetry import trace + from mysql.opentelemetry.semconv.trace import SpanAttributes + + OTEL_SYSTEM_AVAILABLE = False + except ImportError as missing_dependencies_err: + raise connector.errors.ProgrammingError( + "Bundled installation has missing dependencies. " + "Please use `pip install mysql-connector-python[opentelemetry]`, " + "or for an editable install use `pip install -e '.[opentelemetry]'`, " + "to install the dependencies required by the bundled opentelemetry package." + ) from missing_dependencies_err + + +from .constants import ( + CONNECTION_SPAN_NAME, + DB_SYSTEM, + DEFAULT_THREAD_ID, + DEFAULT_THREAD_NAME, + FIRST_SUPPORTED_VERSION, + NET_SOCK_FAMILY, + NET_SOCK_HOST_ADDR, + NET_SOCK_HOST_PORT, + NET_SOCK_PEER_ADDR, + NET_SOCK_PEER_PORT, + OPTION_CNX_SPAN, + OPTION_CNX_TRACER, +) + +leading_comment_remover: re.Pattern = re.compile(r"^/\*.*?\*/") + + +def record_exception_event(span: trace.Span, exc: Optional[Exception]) -> None: + """Records an exeception event.""" + if not span or not span.is_recording() or not exc: + return + + span.set_status(trace.Status(trace.StatusCode.ERROR)) + span.record_exception(exc) + + +def end_span(span: trace.Span) -> None: + """Ends span.""" + if not span or not span.is_recording(): + return + + span.end() + + +def get_operation_name(operation: str) -> str: + """Parse query to extract operation name.""" + if operation and isinstance(operation, str): + # Strip leading comments so we get the operation name. + return leading_comment_remover.sub("", operation).split()[0] + return "" + + +def set_connection_span_attrs( + cnx: Optional["MySQLConnectionAbstract"], + cnx_span: trace.Span, + cnx_kwargs: Optional[Dict[str, Any]] = None, +) -> None: + """Defines connection span attributes. If `cnx` is None then we use `cnx_kwargs` + to get basic net information. Basic net attributes are defined such as: + + * DB_SYSTEM + * NET_TRANSPORT + * NET_SOCK_FAMILY + + Socket-level attributes [*] are also defined [**]. + + [*]: Socket-level attributes identify peer and host that are directly connected to + each other. Since instrumentations may have limited knowledge on network + information, instrumentations SHOULD populate such attributes to the best of + their knowledge when populate them at all. + + [**]: `CMySQLConnection` connections have no access to socket-level + details so socket-level attributes aren't included. `MySQLConnection` + connections, on the other hand, do include socket-level attributes. + + References: + [1]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/ + specification/trace/semantic_conventions/span-general.md + """ + # pylint: disable=broad-exception-caught + if not cnx_span or not cnx_span.is_recording(): + return + + if cnx_kwargs is None: + cnx_kwargs = {} + + is_tcp = not cnx._unix_socket if cnx else "unix_socket" not in cnx_kwargs + + attrs: Dict[str, Any] = { + SpanAttributes.DB_SYSTEM: DB_SYSTEM, + SpanAttributes.NET_TRANSPORT: "ip_tcp" if is_tcp else "inproc", + NET_SOCK_FAMILY: "inet" if is_tcp else "unix", + } + + # Only socket and tcp connections are supported. + if is_tcp: + attrs[SpanAttributes.NET_PEER_NAME] = ( + cnx._host if cnx else cnx_kwargs.get("host", DEFAULT_CONFIGURATION["host"]) + ) + attrs[SpanAttributes.NET_PEER_PORT] = ( + cnx._port if cnx else cnx_kwargs.get("port", DEFAULT_CONFIGURATION["port"]) + ) + + if hasattr(cnx, "_socket") and cnx._socket: + try: + ( + attrs[NET_SOCK_PEER_ADDR], + sock_peer_port, + ) = cnx._socket.sock.getpeername() + + ( + attrs[NET_SOCK_HOST_ADDR], + attrs[NET_SOCK_HOST_PORT], + ) = cnx._socket.sock.getsockname() + except Exception as sock_err: + logger.warning("Connection socket is down %s", sock_err) + else: + if attrs[SpanAttributes.NET_PEER_PORT] != sock_peer_port: + # NET_SOCK_PEER_PORT is recommended if different than net.peer.port + # and if net.sock.peer.addr is set. + attrs[NET_SOCK_PEER_PORT] = sock_peer_port + else: + # For Unix domain socket, net.sock.peer.addr attribute represents + # destination name and net.peer.name SHOULD NOT be set. + attrs[NET_SOCK_PEER_ADDR] = ( + cnx._unix_socket if cnx else cnx_kwargs.get("unix_socket") + ) + + if hasattr(cnx, "_socket") and cnx._socket: + try: + attrs[NET_SOCK_HOST_ADDR] = cnx._socket.sock.getsockname() + except Exception as sock_err: + logger.warning("Connection socket is down %s", sock_err) + + cnx_span.set_attributes(attrs) + + +def instrument_execution( + query_method: Callable, + tracer: trace.Tracer, + connection_span_link: trace.Link, + wrapped: Union["MySQLCursor", "CMySQLCursor"], + *args: Any, + **kwargs: Any, +) -> Callable: + """Instruments the execution of `query_method`. + + A query span with a link to the corresponding connection span is generated. + """ + connection: Union["MySQLConnection", "CMySQLConnection"] = ( + getattr(wrapped, "_connection") + if hasattr(wrapped, "_connection") + else getattr(wrapped, "_cnx") + ) + + # SpanAttributes.DB_NAME: connection.database or ""; introduces performance + # degradation, at this time the database attribute is something nice to have but + # not a requirement. + query_span_attributes: Dict = { + SpanAttributes.DB_SYSTEM: DB_SYSTEM, + SpanAttributes.DB_USER: connection._user, + SpanAttributes.THREAD_ID: DEFAULT_THREAD_ID, + SpanAttributes.THREAD_NAME: DEFAULT_THREAD_NAME, + "cursor_type": wrapped.__class__.__name__, + } + with tracer.start_as_current_span( + name=get_operation_name(args[0]) or "SQL statement", + kind=trace.SpanKind.CLIENT, + links=[connection_span_link], + attributes=query_span_attributes, + ): + return query_method(*args, **kwargs) + + +class BaseMySQLTracer(ABC): + """Base class that provides basic object wrapper functionality.""" + + @abstractmethod + def __init__(self) -> None: + """Must be implemented by subclasses.""" + + def __getattr__(self, attr: str) -> Any: + """Gets an attribute. + + Attributes defined in the wrapper object have higher precedence + than those wrapped object equivalent. Attributes not found in + the wrapper are then searched in the wrapped object. + """ + if attr in self.__dict__: + # this object has it + return getattr(self, attr) + # proxy to the wrapped object + return getattr(self._wrapped, attr) + + def __setattr__(self, name: str, value: Any) -> None: + if "_wrapped" not in self.__dict__: + self.__dict__["_wrapped"] = value + return + + if name in self.__dict__: + # this object has it + super().__setattr__(name, value) + return + # proxy to the wrapped object + self._wrapped.__setattr__(name, value) + + def __enter__(self) -> Any: + """Magic method.""" + self._wrapped.__enter__() + return self + + def __exit__(self, *args: Any, **kwargs: Any) -> None: + """Magic method.""" + self._wrapped.__exit__(*args, **kwargs) + + def get_wrapped_class(self) -> str: + """Gets the wrapped class name.""" + return self._wrapped.__class__.__name__ + + +class TracedMySQLCursor(BaseMySQLTracer): + """Wrapper class for a `MySQLCursor` or `CMySQLCursor` object.""" + + def __init__( + self, + wrapped: Union["MySQLCursor", "CMySQLCursor"], + tracer: trace.Tracer, + connection_span: trace.Span, + ): + """Constructor.""" + self._wrapped: Union["MySQLCursor", "CMySQLCursor"] = wrapped + self._tracer: trace.Tracer = tracer + self._connection_span_link: trace.Link = trace.Link( + connection_span.get_span_context() + ) + + def execute(self, *args: Any, **kwargs: Any) -> Any: + """Instruments execute method.""" + return instrument_execution( + self._wrapped.execute, + self._tracer, + self._connection_span_link, + self._wrapped, + *args, + **kwargs, + ) + + def executemany(self, *args: Any, **kwargs: Any) -> Any: + """Instruments executemany method.""" + return instrument_execution( + self._wrapped.executemany, + self._tracer, + self._connection_span_link, + self._wrapped, + *args, + **kwargs, + ) + + def callproc(self, *args: Any, **kwargs: Any) -> Any: + """Instruments callproc method.""" + return instrument_execution( + self._wrapped.callproc, + self._tracer, + self._connection_span_link, + self._wrapped, + *args, + **kwargs, + ) + + +class TracedMySQLConnection(BaseMySQLTracer): + """Wrapper class for a `MySQLConnection` or `CMySQLConnection` object.""" + + def __init__(self, wrapped: Union["MySQLConnection", "CMySQLConnection"]) -> None: + """Constructor.""" + self._wrapped: Union["MySQLConnection", "CMySQLConnection"] = wrapped + + # call `sql_mode` so its value is cached internally and querying it does not + # interfere when recording query span events later. + _ = self._wrapped.sql_mode + + def cursor(self, *args: Any, **kwargs: Any) -> TracedMySQLCursor: + """Wraps the cursor object.""" + return TracedMySQLCursor( + wrapped=self._wrapped.cursor(*args, **kwargs), + tracer=self._tracer, + connection_span=self._span, + ) + + +def instrument_connect( + connect: Callable[ + ..., Union["MySQLConnection", "CMySQLConnection", "PooledMySQLConnection"] + ], + tracer_provider: Optional[trace.TracerProvider] = None, +) -> Callable[ + ..., Union["MySQLConnection", "CMySQLConnection", "PooledMySQLConnection"] +]: + """Retrurn the instrumented version of `connect`.""" + + # let's preserve `connect` identity. + @functools.wraps(connect) + def wrapper( + *args: Any, **kwargs: Any + ) -> Union["MySQLConnection", "CMySQLConnection", "PooledMySQLConnection"]: + """Wraps the connection object returned by the method `connect`. + + Instrumentation for PooledConnections is not supported. + """ + if any(key in kwargs for key in CNX_POOL_ARGS): + logger.warning("Instrumentation for pooled connections not supported") + return connect(*args, **kwargs) + + tracer = trace.get_tracer( + instrumenting_module_name="MySQL Connector/Python", + instrumenting_library_version=VERSION_TEXT, + tracer_provider=tracer_provider, + ) + + # The connection span is passed in as an argument so the connection object can + # keep a pointer to it. + kwargs[OPTION_CNX_SPAN] = tracer.start_span( + name=CONNECTION_SPAN_NAME, kind=trace.SpanKind.CLIENT + ) + kwargs[OPTION_CNX_TRACER] = tracer + + # Add basic net information. + set_connection_span_attrs(None, kwargs[OPTION_CNX_SPAN], kwargs) + + # Connection may fail at this point, in case it does, basic net info is already + # included so the user can check the net configuration she/he provided. + cnx = connect(*args, **kwargs) + + # connection went ok, let's refine the net information. + set_connection_span_attrs(cnx, cnx._span, kwargs) # type: ignore[arg-type] + + return TracedMySQLConnection( + wrapped=cnx, # type: ignore[return-value, arg-type] + ) + + return wrapper + + +class MySQLInstrumentor: + """MySQL instrumentation supporting mysql-connector-python.""" + + _instance: Optional[MySQLInstrumentor] = None + + def __new__(cls, *args: Any, **kwargs: Any) -> MySQLInstrumentor: + """Singlenton. + + Restricts the instantiation to a singular instance. + """ + if cls._instance is None: + # create instance + cls._instance = object.__new__(cls, *args, **kwargs) + # keep a pointer to the uninstrumented connect method + setattr(cls._instance, "_original_connect", connector.connect) + return cls._instance + + def instrumentation_dependencies(self) -> Collection[str]: + """Return a list of python packages with versions + that the will be instrumented (e.g., versions >= 8.1.0).""" + return [f"mysql-connector-python >= {FIRST_SUPPORTED_VERSION}"] + + def instrument(self, **kwargs: Any) -> None: + """Instrument the library. + + Args: + trace_module: reference to the 'trace' module from opentelemetry. + tracer_provider (optional): TracerProvider instance. + + NOTE: Instrumentation for pooled connections not supported. + """ + if connector.connect != getattr(self, "_original_connect"): + logger.warning("MySQL Connector/Python module already instrumented.") + return + connector.connect = instrument_connect( + connect=getattr(self, "_original_connect"), + tracer_provider=kwargs.get("tracer_provider"), + ) + + def instrument_connection( + self, + connection: Union["MySQLConnection", "CMySQLConnection"], + tracer_provider: Optional[trace.TracerProvider] = None, + ) -> Union["MySQLConnection", "CMySQLConnection"]: + """Enable instrumentation in a MySQL connection. + + Args: + connection: uninstrumented connection instance. + trace_module: reference to the 'trace' module from opentelemetry. + tracer_provider (optional): TracerProvider instance. + + Returns: + connection: instrumented connection instace. + + NOTE: Instrumentation for pooled connections not supported. + """ + if isinstance(connection, TracedMySQLConnection): + logger.warning("Connection already instrumented.") + return connection + + if not hasattr(connection, "_span") or not hasattr(connection, "_tracer"): + logger.warning( + "Instrumentation for class %s not supported.", + connection.__class__.__name__, + ) + return connection + + tracer = trace.get_tracer( + instrumenting_module_name="MySQL Connector/Python", + instrumenting_library_version=VERSION_TEXT, + tracer_provider=tracer_provider, + ) + connection._span = tracer.start_span( + name=CONNECTION_SPAN_NAME, kind=trace.SpanKind.CLIENT + ) + connection._tracer = tracer + + set_connection_span_attrs(connection, connection._span) + + return TracedMySQLConnection(wrapped=connection) # type: ignore[return-value] + + def uninstrument(self, **kwargs: Any) -> None: + """Uninstrument the library.""" + # pylint: disable=unused-argument + if connector.connect == getattr(self, "_original_connect"): + logger.warning("MySQL Connector/Python module already uninstrumented.") + return + connector.connect = getattr(self, "_original_connect") + + def uninstrument_connection( + self, connection: Union["MySQLConnection", "CMySQLConnection"] + ) -> Union["MySQLConnection", "CMySQLConnection"]: + """Disable instrumentation in a MySQL connection. + + Args: + connection: instrumented connection instance. + + Returns: + connection: uninstrumented connection instace. + + NOTE: Instrumentation for pooled connections not supported. + """ + if not hasattr(connection, "_span"): + logger.warning( + "Uninstrumentation for class %s not supported.", + connection.__class__.__name__, + ) + return connection + + if not isinstance(connection, TracedMySQLConnection): + logger.warning("Connection already uninstrumented.") + return connection + + # stop connection span recording + if connection._span and connection._span.is_recording(): + connection._span.end() + connection._span = None + + return connection._wrapped diff --git a/virt/lib/python3.9/site-packages/mysql/connector/plugins/authentication_kerberos_client 3.py b/virt/lib/python3.9/site-packages/mysql/connector/plugins/authentication_kerberos_client 3.py new file mode 100644 index 00000000..12853c1c --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysql/connector/plugins/authentication_kerberos_client 3.py @@ -0,0 +1,462 @@ +# Copyright (c) 2022, Oracle and/or its affiliates. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, as +# published by the Free Software Foundation. +# +# This program is also distributed with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an +# additional permission to link the program and your derivative works +# with the separately licensed software that they have included with +# MySQL. +# +# Without limiting anything contained in the foregoing, this file, +# which is part of MySQL Connector/Python, is also subject to the +# Universal FOSS Exception, version 1.0, a copy of which can be found at +# http://oss.oracle.com/licenses/universal-foss-exception. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License, version 2.0, for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# mypy: disable-error-code="str-bytes-safe,misc" + +"""Kerberos Authentication Plugin.""" + +import getpass +import os +import struct + +from pathlib import Path +from typing import Any, Optional, Tuple + +from ..errors import InterfaceError, ProgrammingError +from ..logger import logger + +try: + import gssapi +except ImportError: + gssapi = None + if os.name != "nt": + raise ProgrammingError( + "Module gssapi is required for GSSAPI authentication " + "mechanism but was not found. Unable to authenticate " + "with the server" + ) from None + +try: + import sspi + import sspicon +except ImportError: + sspi = None + sspicon = None + +from . import BaseAuthPlugin + +AUTHENTICATION_PLUGIN_CLASS = ( + "MySQLSSPIKerberosAuthPlugin" if os.name == "nt" else "MySQLKerberosAuthPlugin" +) + + +# pylint: disable=c-extension-no-member,no-member +class MySQLKerberosAuthPlugin(BaseAuthPlugin): + """Implement the MySQL Kerberos authentication plugin.""" + + plugin_name: str = "authentication_kerberos_client" + requires_ssl: bool = False + context: Optional[gssapi.SecurityContext] = None + + @staticmethod + def get_user_from_credentials() -> str: + """Get user from credentials without realm.""" + try: + creds = gssapi.Credentials(usage="initiate") + user = str(creds.name) + if user.find("@") != -1: + user, _ = user.split("@", 1) + return user + except gssapi.raw.misc.GSSError: + return getpass.getuser() + + @staticmethod + def get_store() -> dict: + """Get a credentials store dictionary. + + Returns: + dict: Credentials store dictionary with the krb5 ccache name. + + Raises: + InterfaceError: If 'KRB5CCNAME' environment variable is empty. + """ + krb5ccname = os.environ.get( + "KRB5CCNAME", + f"/tmp/krb5cc_{os.getuid()}" + if os.name == "posix" + else Path("%TEMP%").joinpath("krb5cc"), + ) + if not krb5ccname: + raise InterfaceError( + "The 'KRB5CCNAME' environment variable is set to empty" + ) + logger.debug("Using krb5 ccache name: FILE:%s", krb5ccname) + store = {b"ccache": f"FILE:{krb5ccname}".encode("utf-8")} + return store + + def _acquire_cred_with_password(self, upn: str) -> gssapi.raw.creds.Creds: + """Acquire and store credentials through provided password. + + Args: + upn (str): User Principal Name. + + Returns: + gssapi.raw.creds.Creds: GSSAPI credentials. + """ + logger.debug("Attempt to acquire credentials through provided password") + user = gssapi.Name(upn, gssapi.NameType.user) + password = self._password.encode("utf-8") + + try: + acquire_cred_result = gssapi.raw.acquire_cred_with_password( + user, password, usage="initiate" + ) + creds = acquire_cred_result.creds + gssapi.raw.store_cred_into( + self.get_store(), + creds=creds, + mech=gssapi.MechType.kerberos, + overwrite=True, + set_default=True, + ) + except gssapi.raw.misc.GSSError as err: + raise ProgrammingError( + f"Unable to acquire credentials with the given password: {err}" + ) from err + return creds + + @staticmethod + def _parse_auth_data(packet: bytes) -> Tuple[str, str]: + """Parse authentication data. + + Get the SPN and REALM from the authentication data packet. + + Format: + SPN string length two bytes <B1> <B2> + + SPN string + + UPN realm string length two bytes <B1> <B2> + + UPN realm string + + Returns: + tuple: With 'spn' and 'realm'. + """ + spn_len = struct.unpack("<H", packet[:2])[0] + packet = packet[2:] + + spn = struct.unpack(f"<{spn_len}s", packet[:spn_len])[0] + packet = packet[spn_len:] + + realm_len = struct.unpack("<H", packet[:2])[0] + realm = struct.unpack(f"<{realm_len}s", packet[2:])[0] + + return spn.decode(), realm.decode() + + def auth_response(self, auth_data: Optional[bytes] = None) -> Optional[bytes]: + """Prepare the first message to the server.""" + spn = None + realm = None + + if auth_data: + try: + spn, realm = self._parse_auth_data(auth_data) + except struct.error as err: + raise InterruptedError(f"Invalid authentication data: {err}") from err + + if spn is None: + return self.prepare_password() + + upn = f"{self._username}@{realm}" if self._username else None + + logger.debug("Service Principal: %s", spn) + logger.debug("Realm: %s", realm) + + try: + # Attempt to retrieve credentials from cache file + creds: Any = gssapi.Credentials(usage="initiate") + creds_upn = str(creds.name) + + logger.debug("Cached credentials found") + logger.debug("Cached credentials UPN: %s", creds_upn) + + # Remove the realm from user + if creds_upn.find("@") != -1: + creds_user, creds_realm = creds_upn.split("@", 1) + else: + creds_user = creds_upn + creds_realm = None + + upn = f"{self._username}@{realm}" if self._username else creds_upn + + # The user from cached credentials matches with the given user? + if self._username and self._username != creds_user: + logger.debug( + "The user from cached credentials doesn't match with the " + "given user" + ) + if self._password is not None: + creds = self._acquire_cred_with_password(upn) + if creds_realm and creds_realm != realm and self._password is not None: + creds = self._acquire_cred_with_password(upn) + except gssapi.raw.exceptions.ExpiredCredentialsError as err: + if upn and self._password is not None: + creds = self._acquire_cred_with_password(upn) + else: + raise InterfaceError(f"Credentials has expired: {err}") from err + except gssapi.raw.misc.GSSError as err: + if upn and self._password is not None: + creds = self._acquire_cred_with_password(upn) + else: + raise InterfaceError( + f"Unable to retrieve cached credentials error: {err}" + ) from err + + flags = ( + gssapi.RequirementFlag.mutual_authentication, + gssapi.RequirementFlag.extended_error, + gssapi.RequirementFlag.delegate_to_peer, + ) + name = gssapi.Name(spn, name_type=gssapi.NameType.kerberos_principal) + cname = name.canonicalize(gssapi.MechType.kerberos) + self.context = gssapi.SecurityContext( + name=cname, creds=creds, flags=sum(flags), usage="initiate" + ) + + try: + initial_client_token: Optional[bytes] = self.context.step() + except gssapi.raw.misc.GSSError as err: + raise InterfaceError(f"Unable to initiate security context: {err}") from err + + logger.debug("Initial client token: %s", initial_client_token) + return initial_client_token + + def auth_continue( + self, tgt_auth_challenge: Optional[bytes] + ) -> Tuple[Optional[bytes], bool]: + """Continue with the Kerberos TGT service request. + + With the TGT authentication service given response generate a TGT + service request. This method must be invoked sequentially (in a loop) + until the security context is completed and an empty response needs to + be send to acknowledge the server. + + Args: + tgt_auth_challenge: the challenge for the negotiation. + + Returns: + tuple (bytearray TGS service request, + bool True if context is completed otherwise False). + """ + logger.debug("tgt_auth challenge: %s", tgt_auth_challenge) + + resp: Optional[bytes] = self.context.step(tgt_auth_challenge) + + logger.debug("Context step response: %s", resp) + logger.debug("Context completed?: %s", self.context.complete) + + return resp, self.context.complete + + def auth_accept_close_handshake(self, message: bytes) -> bytes: + """Accept handshake and generate closing handshake message for server. + + This method verifies the server authenticity from the given message + and included signature and generates the closing handshake for the + server. + + When this method is invoked the security context is already established + and the client and server can send GSSAPI formated secure messages. + + To finish the authentication handshake the server sends a message + with the security layer availability and the maximum buffer size. + + Since the connector only uses the GSSAPI authentication mechanism to + authenticate the user with the server, the server will verify clients + message signature and terminate the GSSAPI authentication and send two + messages; an authentication acceptance b'\x01\x00\x00\x08\x01' and a + OK packet (that must be received after sent the returned message from + this method). + + Args: + message: a wrapped gssapi message from the server. + + Returns: + bytearray (closing handshake message to be send to the server). + """ + if not self.context.complete: + raise ProgrammingError("Security context is not completed") + logger.debug("Server message: %s", message) + logger.debug("GSSAPI flags in use: %s", self.context.actual_flags) + try: + unwraped = self.context.unwrap(message) + logger.debug("Unwraped: %s", unwraped) + except gssapi.raw.exceptions.BadMICError as err: + logger.debug("Unable to unwrap server message: %s", err) + raise InterfaceError(f"Unable to unwrap server message: {err}") from err + + logger.debug("Unwrapped server message: %s", unwraped) + # The message contents for the clients closing message: + # - security level 1 byte, must be always 1. + # - conciliated buffer size 3 bytes, without importance as no + # further GSSAPI messages will be sends. + response = bytearray(b"\x01\x00\x00\00") + # Closing handshake must not be encrypted. + logger.debug("Message response: %s", response) + wraped = self.context.wrap(response, encrypt=False) + logger.debug( + "Wrapped message response: %s, length: %d", + wraped[0], + len(wraped[0]), + ) + + return wraped.message + + +class MySQLSSPIKerberosAuthPlugin(BaseAuthPlugin): + """Implement the MySQL Kerberos authentication plugin with Windows SSPI""" + + plugin_name: str = "authentication_kerberos_client" + requires_ssl: bool = False + context: Any = None + clientauth: Any = None + + @staticmethod + def _parse_auth_data(packet: bytes) -> Tuple[str, str]: + """Parse authentication data. + + Get the SPN and REALM from the authentication data packet. + + Format: + SPN string length two bytes <B1> <B2> + + SPN string + + UPN realm string length two bytes <B1> <B2> + + UPN realm string + + Returns: + tuple: With 'spn' and 'realm'. + """ + spn_len = struct.unpack("<H", packet[:2])[0] + packet = packet[2:] + + spn = struct.unpack(f"<{spn_len}s", packet[:spn_len])[0] + packet = packet[spn_len:] + + realm_len = struct.unpack("<H", packet[:2])[0] + realm = struct.unpack(f"<{realm_len}s", packet[2:])[0] + + return spn.decode(), realm.decode() + + def auth_response(self, auth_data: Optional[bytes] = None) -> Optional[bytes]: + """Prepare the first message to the server.""" + logger.debug("auth_response for sspi") + spn = None + realm = None + + if auth_data: + try: + spn, realm = self._parse_auth_data(auth_data) + except struct.error as err: + raise InterruptedError(f"Invalid authentication data: {err}") from err + + logger.debug("Service Principal: %s", spn) + logger.debug("Realm: %s", realm) + + if sspicon is None or sspi is None: + raise ProgrammingError( + 'Package "pywin32" (Python for Win32 (pywin32) extensions)' + " is not installed." + ) + + flags = (sspicon.ISC_REQ_MUTUAL_AUTH, sspicon.ISC_REQ_DELEGATE) + + if self._username and self._password: + _auth_info = (self._username, realm, self._password) + else: + _auth_info = None + + targetspn = spn + logger.debug("targetspn: %s", targetspn) + logger.debug("_auth_info is None: %s", _auth_info is None) + + # The Security Support Provider Interface (SSPI) is an interface + # that allows us to choose from a set of SSPs available in the + # system; the idea of SSPI is to keep interface consistent no + # matter what back end (a.k.a., SSP) we choose. + + # When using SSPI we should not use Kerberos directly as SSP, + # as remarked in [2], but we can use it indirectly via another + # SSP named Negotiate that acts as an application layer between + # SSPI and the other SSPs [1]. + + # Negotiate can select between Kerberos and NTLM on the fly; + # it chooses Kerberos unless it cannot be used by one of the + # systems involved in the authentication or the calling + # application did not provide sufficient information to use + # Kerberos. + + # prefix: https://docs.microsoft.com/en-us/windows/win32/secauthn + # [1] prefix/microsoft-negotiate?source=recommendations + # [2] prefix/microsoft-kerberos?source=recommendations + self.clientauth = sspi.ClientAuth( + "Negotiate", + targetspn=targetspn, + auth_info=_auth_info, + scflags=sum(flags), + datarep=sspicon.SECURITY_NETWORK_DREP, + ) + + try: + data = None + err, out_buf = self.clientauth.authorize(data) + logger.debug("Context step err: %s", err) + logger.debug("Context step out_buf: %s", out_buf) + logger.debug("Context completed?: %s", self.clientauth.authenticated) + initial_client_token = out_buf[0].Buffer + logger.debug("pkg_info: %s", self.clientauth.pkg_info) + except Exception as err: + raise InterfaceError(f"Unable to initiate security context: {err}") from err + + logger.debug("Initial client token: %s", initial_client_token) + return initial_client_token + + def auth_continue( + self, tgt_auth_challenge: Optional[bytes] + ) -> Tuple[Optional[bytes], bool]: + """Continue with the Kerberos TGT service request. + + With the TGT authentication service given response generate a TGT + service request. This method must be invoked sequentially (in a loop) + until the security context is completed and an empty response needs to + be send to acknowledge the server. + + Args: + tgt_auth_challenge: the challenge for the negotiation. + + Returns: + tuple (bytearray TGS service request, + bool True if context is completed otherwise False). + """ + logger.debug("tgt_auth challenge: %s", tgt_auth_challenge) + + err, out_buf = self.clientauth.authorize(tgt_auth_challenge) + + logger.debug("Context step err: %s", err) + logger.debug("Context step out_buf: %s", out_buf) + resp = out_buf[0].Buffer + logger.debug("Context step resp: %s", resp) + logger.debug("Context completed?: %s", self.clientauth.authenticated) + + return resp, self.clientauth.authenticated diff --git a/virt/lib/python3.9/site-packages/mysql/connector/plugins/authentication_ldap_sasl_client 3.py b/virt/lib/python3.9/site-packages/mysql/connector/plugins/authentication_ldap_sasl_client 3.py new file mode 100644 index 00000000..27d4a477 --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysql/connector/plugins/authentication_ldap_sasl_client 3.py @@ -0,0 +1,496 @@ +# Copyright (c) 2022, Oracle and/or its affiliates. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, as +# published by the Free Software Foundation. +# +# This program is also distributed with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an +# additional permission to link the program and your derivative works +# with the separately licensed software that they have included with +# MySQL. +# +# Without limiting anything contained in the foregoing, this file, +# which is part of MySQL Connector/Python, is also subject to the +# Universal FOSS Exception, version 1.0, a copy of which can be found at +# http://oss.oracle.com/licenses/universal-foss-exception. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License, version 2.0, for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""LDAP SASL Authentication Plugin.""" + +import hmac + +from base64 import b64decode, b64encode +from hashlib import sha1, sha256 +from typing import Any, Callable, List, Optional, Tuple +from uuid import uuid4 + +from ..errors import InterfaceError, ProgrammingError +from ..logger import logger +from ..types import StrOrBytes + +try: + import gssapi +except ImportError: + raise ProgrammingError( + "Module gssapi is required for GSSAPI authentication " + "mechanism but was not found. Unable to authenticate " + "with the server" + ) from None + +from ..utils import ( + normalize_unicode_string as norm_ustr, + validate_normalized_unicode_string as valid_norm, +) +from . import BaseAuthPlugin + +AUTHENTICATION_PLUGIN_CLASS = "MySQLLdapSaslPasswordAuthPlugin" + + +# pylint: disable=c-extension-no-member,no-member +class MySQLLdapSaslPasswordAuthPlugin(BaseAuthPlugin): + """Class implementing the MySQL ldap sasl authentication plugin. + + The MySQL's ldap sasl authentication plugin support two authentication + methods SCRAM-SHA-1 and GSSAPI (using Kerberos). This implementation only + support SCRAM-SHA-1 and SCRAM-SHA-256. + + SCRAM-SHA-1 amd SCRAM-SHA-256 + This method requires 2 messages from client and 2 responses from + server. + + The first message from client will be generated by prepare_password(), + after receive the response from the server, it is required that this + response is passed back to auth_continue() which will return the + second message from the client. After send this second message to the + server, the second server respond needs to be passed to auth_finalize() + to finish the authentication process. + """ + + sasl_mechanisms: List[str] = ["SCRAM-SHA-1", "SCRAM-SHA-256", "GSSAPI"] + requires_ssl: bool = False + plugin_name: str = "authentication_ldap_sasl_client" + def_digest_mode: Callable = sha1 + client_nonce: Optional[str] = None + client_salt: Any = None + server_salt: Optional[str] = None + krb_service_principal: Optional[StrOrBytes] = None + iterations: int = 0 + server_auth_var: Optional[str] = None + target_name: Optional[gssapi.Name] = None + ctx: gssapi.SecurityContext = None + servers_first: Optional[str] = None + server_nonce: Optional[str] = None + + @staticmethod + def _xor(bytes1: bytes, bytes2: bytes) -> bytes: + return bytes([b1 ^ b2 for b1, b2 in zip(bytes1, bytes2)]) + + def _hmac(self, password: bytes, salt: bytes) -> bytes: + digest_maker = hmac.new(password, salt, self.def_digest_mode) + return digest_maker.digest() + + def _hi(self, password: str, salt: bytes, count: int) -> bytes: + """Prepares Hi + Hi(password, salt, iterations) where Hi(p,s,i) is defined as + PBKDF2 (HMAC, p, s, i, output length of H). + """ + pw = password.encode() + hi = self._hmac(pw, salt + b"\x00\x00\x00\x01") + aux = hi + for _ in range(count - 1): + aux = self._hmac(pw, aux) + hi = self._xor(hi, aux) + return hi + + @staticmethod + def _normalize(string: str) -> str: + norm_str = norm_ustr(string) + broken_rule = valid_norm(norm_str) + if broken_rule is not None: + raise InterfaceError(f"broken_rule: {broken_rule}") + return norm_str + + def _first_message(self) -> bytes: + """This method generates the first message to the server to start the + + The client-first message consists of a gs2-header, + the desired username, and a randomly generated client nonce cnonce. + + The first message from the server has the form: + b'n,a=<user_name>,n=<user_name>,r=<client_nonce> + + Returns client's first message + """ + cfm_fprnat = "n,a={user_name},n={user_name},r={client_nonce}" + self.client_nonce = str(uuid4()).replace("-", "") + cfm: StrOrBytes = cfm_fprnat.format( + user_name=self._normalize(self._username), + client_nonce=self.client_nonce, + ) + + if isinstance(cfm, str): + cfm = cfm.encode("utf8") + return cfm + + def _first_message_krb(self) -> Optional[bytes]: + """Get a TGT Authentication request and initiates security context. + + This method will contact the Kerberos KDC in order of obtain a TGT. + """ + user_name = gssapi.raw.names.import_name( + self._username.encode("utf8"), name_type=gssapi.NameType.user + ) + + # Use defaults store = {'ccache': 'FILE:/tmp/krb5cc_1000'}#, + # 'keytab':'/etc/some.keytab' } + # Attempt to retrieve credential from default cache file. + try: + cred: Any = gssapi.Credentials() + logger.debug( + "# Stored credentials found, if password was given it will be ignored." + ) + try: + # validate credentials has not expired. + cred.lifetime + except gssapi.raw.exceptions.ExpiredCredentialsError as err: + logger.warning(" Credentials has expired: %s", err) + cred.acquire(user_name) + raise InterfaceError(f"Credentials has expired: {err}") from err + except gssapi.raw.misc.GSSError as err: + if not self._password: + raise InterfaceError( + f"Unable to retrieve stored credentials error: {err}" + ) from err + try: + logger.debug("# Attempt to retrieve credentials with given password") + acquire_cred_result = gssapi.raw.acquire_cred_with_password( + user_name, + self._password.encode("utf8"), + usage="initiate", + ) + cred = acquire_cred_result[0] + except gssapi.raw.misc.GSSError as err2: + raise ProgrammingError( + f"Unable to retrieve credentials with the given password: {err2}" + ) from err + + flags_l = ( + gssapi.RequirementFlag.mutual_authentication, + gssapi.RequirementFlag.extended_error, + gssapi.RequirementFlag.delegate_to_peer, + ) + + if self.krb_service_principal: + service_principal = self.krb_service_principal + else: + service_principal = "ldap/ldapauth" + logger.debug("# service principal: %s", service_principal) + servk = gssapi.Name( + service_principal, name_type=gssapi.NameType.kerberos_principal + ) + self.target_name = servk + self.ctx = gssapi.SecurityContext( + name=servk, creds=cred, flags=sum(flags_l), usage="initiate" + ) + + try: + # step() returns bytes | None, see documentation, + # so this method could return a NULL payload. + # ref: https://pythongssapi.github.io/<suffix> + # suffix: python-gssapi/latest/gssapi.html#gssapi.sec_contexts.SecurityContext + initial_client_token = self.ctx.step() + except gssapi.raw.misc.GSSError as err: + raise InterfaceError(f"Unable to initiate security context: {err}") from err + + logger.debug("# initial client token: %s", initial_client_token) + return initial_client_token + + def auth_continue_krb( + self, tgt_auth_challenge: Optional[bytes] + ) -> Tuple[Optional[bytes], bool]: + """Continue with the Kerberos TGT service request. + + With the TGT authentication service given response generate a TGT + service request. This method must be invoked sequentially (in a loop) + until the security context is completed and an empty response needs to + be send to acknowledge the server. + + Args: + tgt_auth_challenge the challenge for the negotiation. + + Returns: tuple (bytearray TGS service request, + bool True if context is completed otherwise False). + """ + logger.debug("tgt_auth challenge: %s", tgt_auth_challenge) + + resp = self.ctx.step(tgt_auth_challenge) + logger.debug("# context step response: %s", resp) + logger.debug("# context completed?: %s", self.ctx.complete) + + return resp, self.ctx.complete + + def auth_accept_close_handshake(self, message: bytes) -> bytes: + """Accept handshake and generate closing handshake message for server. + + This method verifies the server authenticity from the given message + and included signature and generates the closing handshake for the + server. + + When this method is invoked the security context is already established + and the client and server can send GSSAPI formated secure messages. + + To finish the authentication handshake the server sends a message + with the security layer availability and the maximum buffer size. + + Since the connector only uses the GSSAPI authentication mechanism to + authenticate the user with the server, the server will verify clients + message signature and terminate the GSSAPI authentication and send two + messages; an authentication acceptance b'\x01\x00\x00\x08\x01' and a + OK packet (that must be received after sent the returned message from + this method). + + Args: + message a wrapped hssapi message from the server. + + Returns: bytearray closing handshake message to be send to the server. + """ + if not self.ctx.complete: + raise ProgrammingError("Security context is not completed.") + logger.debug("# servers message: %s", message) + logger.debug("# GSSAPI flags in use: %s", self.ctx.actual_flags) + try: + unwraped = self.ctx.unwrap(message) + logger.debug("# unwraped: %s", unwraped) + except gssapi.raw.exceptions.BadMICError as err: + raise InterfaceError(f"Unable to unwrap server message: {err}") from err + + logger.debug("# unwrapped server message: %s", unwraped) + # The message contents for the clients closing message: + # - security level 1 byte, must be always 1. + # - conciliated buffer size 3 bytes, without importance as no + # further GSSAPI messages will be sends. + response = bytearray(b"\x01\x00\x00\00") + # Closing handshake must not be encrypted. + logger.debug("# message response: %s", response) + wraped = self.ctx.wrap(response, encrypt=False) + logger.debug( + "# wrapped message response: %s, length: %d", + wraped[0], + len(wraped[0]), + ) + + return wraped.message + + def auth_response( # type: ignore[override] + self, + auth_data: Optional[str] = None, + ) -> Optional[bytes]: + """This method will prepare the fist message to the server. + + Returns bytes to send to the server as the first message. + """ + krb_service_principal = auth_data + auth_mechanism = self._auth_data.decode() + self.krb_service_principal = krb_service_principal + logger.debug("read_method_name_from_server: %s", auth_mechanism) + if auth_mechanism not in self.sasl_mechanisms: + auth_mechanisms = '", "'.join(self.sasl_mechanisms[:-1]) + raise InterfaceError( + f'The sasl authentication method "{auth_mechanism}" requested ' + f'from the server is not supported. Only "{auth_mechanisms}" ' + f'and "{self.sasl_mechanisms[-1]}" are supported' + ) + + if b"GSSAPI" in self._auth_data: + return self._first_message_krb() + + if self._auth_data == b"SCRAM-SHA-256": + self.def_digest_mode = sha256 + + return self._first_message() + + def _second_message(self) -> bytes: + """This method generates the second message to the server + + Second message consist on the concatenation of the client and the + server nonce, and cproof. + + c=<n,a=<user_name>>,r=<server_nonce>,p=<client_proof> + where: + <client_proof>: xor(<client_key>, <client_signature>) + + <client_key>: hmac(salted_password, b"Client Key") + <client_signature>: hmac(<stored_key>, <auth_msg>) + <stored_key>: h(<client_key>) + <auth_msg>: <client_first_no_header>,<servers_first>, + c=<client_header>,r=<server_nonce> + <client_first_no_header>: n=<username>r=<client_nonce> + """ + if not self._auth_data: + raise InterfaceError("Missing authentication data (seed)") + + passw = self._normalize(self._password) + salted_password = self._hi(passw, b64decode(self.server_salt), self.iterations) + logger.debug("salted_password: %s", b64encode(salted_password).decode()) + + client_key = self._hmac(salted_password, b"Client Key") + logger.debug("client_key: %s", b64encode(client_key).decode()) + + stored_key = self.def_digest_mode(client_key).digest() + logger.debug("stored_key: %s", b64encode(stored_key).decode()) + + server_key = self._hmac(salted_password, b"Server Key") + logger.debug("server_key: %s", b64encode(server_key).decode()) + + client_first_no_header = ",".join( + [ + f"n={self._normalize(self._username)}", + f"r={self.client_nonce}", + ] + ) + logger.debug("client_first_no_header: %s", client_first_no_header) + + client_header = b64encode( + f"n,a={self._normalize(self._username)},".encode() + ).decode() + + auth_msg = ",".join( + [ + client_first_no_header, + self.servers_first, + f"c={client_header}", + f"r={self.server_nonce}", + ] + ) + logger.debug("auth_msg: %s", auth_msg) + + client_signature = self._hmac(stored_key, auth_msg.encode()) + logger.debug("client_signature: %s", b64encode(client_signature).decode()) + + client_proof = self._xor(client_key, client_signature) + logger.debug("client_proof: %s", b64encode(client_proof).decode()) + + self.server_auth_var = b64encode( + self._hmac(server_key, auth_msg.encode()) + ).decode() + logger.debug("server_auth_var: %s", self.server_auth_var) + + msg = ",".join( + [ + f"c={client_header}", + f"r={self.server_nonce}", + f"p={b64encode(client_proof).decode()}", + ] + ) + logger.debug("second_message: %s", msg) + return msg.encode() + + def _validate_first_reponse(self, servers_first: bytes) -> None: + """Validates first message from the server. + + Extracts the server's salt and iterations from the servers 1st response. + First message from the server is in the form: + <server_salt>,i=<iterations> + """ + if not servers_first or not isinstance(servers_first, (bytearray, bytes)): + raise InterfaceError(f"Unexpected server message: {repr(servers_first)}") + try: + servers_first_str = servers_first.decode() + self.servers_first = servers_first_str + r_server_nonce, s_salt, i_counter = servers_first_str.split(",") + except ValueError: + raise InterfaceError( + f"Unexpected server message: {servers_first_str}" + ) from None + if ( + not r_server_nonce.startswith("r=") + or not s_salt.startswith("s=") + or not i_counter.startswith("i=") + ): + raise InterfaceError( + f"Incomplete reponse from the server: {servers_first_str}" + ) + if self.client_nonce in r_server_nonce: + self.server_nonce = r_server_nonce[2:] + logger.debug("server_nonce: %s", self.server_nonce) + else: + raise InterfaceError( + "Unable to authenticate response: response not well formed " + f"{servers_first_str}" + ) + self.server_salt = s_salt[2:] + logger.debug( + "server_salt: %s length: %s", + self.server_salt, + len(self.server_salt), + ) + try: + i_counter = i_counter[2:] + logger.debug("iterations: %s", i_counter) + self.iterations = int(i_counter) + except Exception as err: + raise InterfaceError( + f"Unable to authenticate: iterations not found {servers_first_str}" + ) from err + + def auth_continue(self, servers_first_response: bytes) -> bytes: + """return the second message from the client. + + Returns bytes to send to the server as the second message. + """ + self._validate_first_reponse(servers_first_response) + return self._second_message() + + def _validate_second_reponse(self, servers_second: bytearray) -> bool: + """Validates second message from the server. + + The client and the server prove to each other they have the same Auth + variable. + + The second message from the server consist of the server's proof: + server_proof = HMAC(<server_key>, <auth_msg>) + where: + <server_key>: hmac(<salted_password>, b"Server Key") + <auth_msg>: <client_first_no_header>,<servers_first>, + c=<client_header>,r=<server_nonce> + + Our server_proof must be equal to the Auth variable send on this second + response. + """ + if ( + not servers_second + or not isinstance(servers_second, bytearray) + or len(servers_second) <= 2 + or not servers_second.startswith(b"v=") + ): + raise InterfaceError("The server's proof is not well formated") + server_var = servers_second[2:].decode() + logger.debug("server auth variable: %s", server_var) + return self.server_auth_var == server_var + + def auth_finalize(self, servers_second_response: bytearray) -> bool: + """finalize the authentication process. + + Raises InterfaceError if the ervers_second_response is invalid. + + Returns True in successful authentication False otherwise. + """ + if not self._validate_second_reponse(servers_second_response): + raise InterfaceError( + "Authentication failed: Unable to proof server identity" + ) + return True + + +# pylint: enable=c-extension-no-member,no-member diff --git a/virt/lib/python3.9/site-packages/mysql/connector/pooling 3.py b/virt/lib/python3.9/site-packages/mysql/connector/pooling 3.py new file mode 100644 index 00000000..fc347eb0 --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysql/connector/pooling 3.py @@ -0,0 +1,622 @@ +# Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, as +# published by the Free Software Foundation. +# +# This program is also distributed with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an +# additional permission to link the program and your derivative works +# with the separately licensed software that they have included with +# MySQL. +# +# Without limiting anything contained in the foregoing, this file, +# which is part of MySQL Connector/Python, is also subject to the +# Universal FOSS Exception, version 1.0, a copy of which can be found at +# http://oss.oracle.com/licenses/universal-foss-exception. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License, version 2.0, for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""Implementing pooling of connections to MySQL servers.""" +from __future__ import annotations + +import queue +import random +import re +import threading + +from types import TracebackType +from typing import Any, Dict, NoReturn, Optional, Tuple, Type, Union +from uuid import uuid4 + +try: + import dns.exception + import dns.resolver +except ImportError: + HAVE_DNSPYTHON = False +else: + HAVE_DNSPYTHON = True + +try: + from .connection_cext import CMySQLConnection +except ImportError: + CMySQLConnection = None # type: ignore[misc] + +from .connection import MySQLConnection +from .constants import CNX_POOL_ARGS, DEFAULT_CONFIGURATION +from .errors import ( + Error, + InterfaceError, + NotSupportedError, + PoolError, + ProgrammingError, +) +from .optionfiles import read_option_files + +CONNECTION_POOL_LOCK = threading.RLock() +CNX_POOL_MAXSIZE = 32 +CNX_POOL_MAXNAMESIZE = 64 +CNX_POOL_NAMEREGEX = re.compile(r"[^a-zA-Z0-9._:\-*$#]") +ERROR_NO_CEXT = "MySQL Connector/Python C Extension not available" +MYSQL_CNX_CLASS: Union[type, Tuple[type, ...]] = ( + MySQLConnection if CMySQLConnection is None else (MySQLConnection, CMySQLConnection) +) + +_CONNECTION_POOLS: Dict[str, MySQLConnectionPool] = {} + + +def _get_pooled_connection(**kwargs: Any) -> PooledMySQLConnection: + """Return a pooled MySQL connection.""" + # If no pool name specified, generate one + pool_name = ( + kwargs["pool_name"] if "pool_name" in kwargs else generate_pool_name(**kwargs) + ) + + if kwargs.get("use_pure") is False and CMySQLConnection is None: + raise ImportError(ERROR_NO_CEXT) + + # Setup the pool, ensuring only 1 thread can update at a time + with CONNECTION_POOL_LOCK: + if pool_name not in _CONNECTION_POOLS: + _CONNECTION_POOLS[pool_name] = MySQLConnectionPool(**kwargs) + elif isinstance(_CONNECTION_POOLS[pool_name], MySQLConnectionPool): + # pool_size must be the same + check_size = _CONNECTION_POOLS[pool_name].pool_size + if "pool_size" in kwargs and kwargs["pool_size"] != check_size: + raise PoolError("Size can not be changed for active pools.") + + # Return pooled connection + try: + return _CONNECTION_POOLS[pool_name].get_connection() + except AttributeError: + raise InterfaceError( + f"Failed getting connection from pool '{pool_name}'" + ) from None + + +def _get_failover_connection( + **kwargs: Any, +) -> Union[PooledMySQLConnection, MySQLConnection, CMySQLConnection]: + """Return a MySQL connection and try to failover if needed. + + An InterfaceError is raise when no MySQL is available. ValueError is + raised when the failover server configuration contains an illegal + connection argument. Supported arguments are user, password, host, port, + unix_socket and database. ValueError is also raised when the failover + argument was not provided. + + Returns MySQLConnection instance. + """ + config = kwargs.copy() + try: + failover = config["failover"] + except KeyError: + raise ValueError("failover argument not provided") from None + del config["failover"] + + support_cnx_args = set( + [ + "user", + "password", + "host", + "port", + "unix_socket", + "database", + "pool_name", + "pool_size", + "priority", + ] + ) + + # First check if we can add all use the configuration + priority_count = 0 + for server in failover: + diff = set(server.keys()) - support_cnx_args + if diff: + arg = "s" if len(diff) > 1 else "" + lst = ", ".join(diff) + raise ValueError( + f"Unsupported connection argument {arg} in failover: {lst}" + ) + if hasattr(server, "priority"): + priority_count += 1 + + server["priority"] = server.get("priority", 100) + if server["priority"] < 0 or server["priority"] > 100: + raise InterfaceError( + "Priority value should be in the range of 0 to 100, " + f"got : {server['priority']}" + ) + if not isinstance(server["priority"], int): + raise InterfaceError( + "Priority value should be an integer in the range of 0 to " + f"100, got : {server['priority']}" + ) + + if 0 < priority_count < len(failover): + raise ProgrammingError( + "You must either assign no priority to any " + "of the routers or give a priority for " + "every router" + ) + + server_directory = {} + server_priority_list = [] + for server in sorted(failover, key=lambda x: x["priority"], reverse=True): + if server["priority"] not in server_directory: + server_directory[server["priority"]] = [server] + server_priority_list.append(server["priority"]) + else: + server_directory[server["priority"]].append(server) + + for priority in server_priority_list: + failover_list = server_directory[priority] + for _ in range(len(failover_list)): + last = len(failover_list) - 1 + index = random.randint(0, last) + server = failover_list.pop(index) + new_config = config.copy() + new_config.update(server) + new_config.pop("priority", None) + try: + return connect(**new_config) + except Error: + # If we failed to connect, we try the next server + pass + + raise InterfaceError("Unable to connect to any of the target hosts") + + +def connect( + *args: Any, **kwargs: Any +) -> Union[PooledMySQLConnection, MySQLConnection, CMySQLConnection]: + """Create or get a MySQL connection object. + + In its simpliest form, connect() will open a connection to a + MySQL server and return a MySQLConnection object. + + When any connection pooling arguments are given, for example pool_name + or pool_size, a pool is created or a previously one is used to return + a PooledMySQLConnection. + + Returns MySQLConnection or PooledMySQLConnection. + """ + # DNS SRV + dns_srv = kwargs.pop("dns_srv") if "dns_srv" in kwargs else False + + if not isinstance(dns_srv, bool): + raise InterfaceError("The value of 'dns-srv' must be a boolean") + + if dns_srv: + if not HAVE_DNSPYTHON: + raise InterfaceError( + "MySQL host configuration requested DNS " + "SRV. This requires the Python dnspython " + "module. Please refer to documentation" + ) + if "unix_socket" in kwargs: + raise InterfaceError( + "Using Unix domain sockets with DNS SRV lookup is not allowed" + ) + if "port" in kwargs: + raise InterfaceError( + "Specifying a port number with DNS SRV lookup is not allowed" + ) + if "failover" in kwargs: + raise InterfaceError( + "Specifying multiple hostnames with DNS SRV look up is not allowed" + ) + if "host" not in kwargs: + kwargs["host"] = DEFAULT_CONFIGURATION["host"] + + try: + srv_records = dns.resolver.query(kwargs["host"], "SRV") + except dns.exception.DNSException: + raise InterfaceError( + f"Unable to locate any hosts for '{kwargs['host']}'" + ) from None + + failover = [] + for srv in srv_records: + failover.append( + { + "host": srv.target.to_text(omit_final_dot=True), + "port": srv.port, + "priority": srv.priority, + "weight": srv.weight, + } + ) + + failover.sort(key=lambda x: (x["priority"], -x["weight"])) + kwargs["failover"] = [ + {"host": srv["host"], "port": srv["port"]} for srv in failover + ] + + # Option files + if "read_default_file" in kwargs: + kwargs["option_files"] = kwargs["read_default_file"] + kwargs.pop("read_default_file") + + if "option_files" in kwargs: + new_config = read_option_files(**kwargs) + return connect(**new_config) + + # Failover + if "failover" in kwargs: + return _get_failover_connection(**kwargs) + + # Pooled connections + try: + if any(key in kwargs for key in CNX_POOL_ARGS): + return _get_pooled_connection(**kwargs) + except NameError: + # No pooling + pass + + # Use C Extension by default + use_pure = kwargs.get("use_pure", False) + if "use_pure" in kwargs: + del kwargs["use_pure"] # Remove 'use_pure' from kwargs + if not use_pure and CMySQLConnection is None: + raise ImportError(ERROR_NO_CEXT) + + if CMySQLConnection and not use_pure: + return CMySQLConnection(*args, **kwargs) + return MySQLConnection(*args, **kwargs) + + +def generate_pool_name(**kwargs: Any) -> str: + """Generate a pool name + + This function takes keyword arguments, usually the connection + arguments for MySQLConnection, and tries to generate a name for + a pool. + + Raises PoolError when no name can be generated. + + Returns a string. + """ + parts = [] + for key in ("host", "port", "user", "database"): + try: + parts.append(str(kwargs[key])) + except KeyError: + pass + + if not parts: + raise PoolError("Failed generating pool name; specify pool_name") + + return "_".join(parts) + + +class PooledMySQLConnection: + """Class holding a MySQL Connection in a pool + + PooledMySQLConnection is used by MySQLConnectionPool to return an + instance holding a MySQL connection. It works like a MySQLConnection + except for methods like close() and config(). + + The close()-method will add the connection back to the pool rather + than disconnecting from the MySQL server. + + Configuring the connection have to be done through the MySQLConnectionPool + method set_config(). Using config() on pooled connection will raise a + PoolError. + """ + + def __init__( + self, pool: MySQLConnectionPool, cnx: Union[MySQLConnection, CMySQLConnection] + ) -> None: + """Initialize + + The pool argument must be an instance of MySQLConnectionPoll. cnx + if an instance of MySQLConnection. + """ + if not isinstance(pool, MySQLConnectionPool): + raise AttributeError("pool should be a MySQLConnectionPool") + if not isinstance(cnx, MYSQL_CNX_CLASS): + raise AttributeError("cnx should be a MySQLConnection") + self._cnx_pool: MySQLConnectionPool = pool + self._cnx: Union[MySQLConnection, CMySQLConnection] = cnx + + def __enter__(self) -> PooledMySQLConnection: + return self + + def __exit__( + self, + exc_type: Type[BaseException], + exc_value: BaseException, + traceback: TracebackType, + ) -> None: + self.close() + + def __getattr__(self, attr: Any) -> Any: + """Calls attributes of the MySQLConnection instance""" + return getattr(self._cnx, attr) + + def close(self) -> None: + """Do not close, but add connection back to pool + + The close() method does not close the connection with the + MySQL server. The connection is added back to the pool so it + can be reused. + + When the pool is configured to reset the session, the session + state will be cleared by re-authenticating the user. + """ + try: + cnx = self._cnx + if self._cnx_pool.reset_session: + cnx.reset_session() + finally: + self._cnx_pool.add_connection(cnx) + self._cnx = None + + @staticmethod + def config(**kwargs: Any) -> NoReturn: + """Configuration is done through the pool""" + raise PoolError( + "Configuration for pooled connections should be done through the " + "pool itself" + ) + + @property + def pool_name(self) -> str: + """Return the name of the connection pool""" + return self._cnx_pool.pool_name + + +class MySQLConnectionPool: + """Class defining a pool of MySQL connections""" + + def __init__( + self, + pool_size: int = 5, + pool_name: Optional[str] = None, + pool_reset_session: bool = True, + **kwargs: Any, + ) -> None: + """Initialize + + Initialize a MySQL connection pool with a maximum number of + connections set to pool_size. The rest of the keywords + arguments, kwargs, are configuration arguments for MySQLConnection + instances. + """ + self._pool_size: Optional[int] = None + self._pool_name: Optional[str] = None + self._reset_session = pool_reset_session + self._set_pool_size(pool_size) + self._set_pool_name(pool_name or generate_pool_name(**kwargs)) + self._cnx_config: Dict[str, Any] = {} + self._cnx_queue: queue.Queue[ + Union[MySQLConnection, CMySQLConnection] + ] = queue.Queue(self._pool_size) + self._config_version = uuid4() + + if kwargs: + self.set_config(**kwargs) + cnt = 0 + while cnt < self._pool_size: + self.add_connection() + cnt += 1 + + @property + def pool_name(self) -> str: + """Return the name of the connection pool""" + return self._pool_name + + @property + def pool_size(self) -> int: + """Return number of connections managed by the pool""" + return self._pool_size + + @property + def reset_session(self) -> bool: + """Return whether to reset session""" + return self._reset_session + + def set_config(self, **kwargs: Any) -> None: + """Set the connection configuration for MySQLConnection instances + + This method sets the configuration used for creating MySQLConnection + instances. See MySQLConnection for valid connection arguments. + + Raises PoolError when a connection argument is not valid, missing + or not supported by MySQLConnection. + """ + if not kwargs: + return + + with CONNECTION_POOL_LOCK: + try: + test_cnx = connect() + test_cnx.config(**kwargs) + self._cnx_config = kwargs + self._config_version = uuid4() + except AttributeError as err: + raise PoolError(f"Connection configuration not valid: {err}") from err + + def _set_pool_size(self, pool_size: int) -> None: + """Set the size of the pool + + This method sets the size of the pool but it will not resize the pool. + + Raises an AttributeError when the pool_size is not valid. Invalid size + is 0, negative or higher than pooling.CNX_POOL_MAXSIZE. + """ + if pool_size <= 0 or pool_size > CNX_POOL_MAXSIZE: + raise AttributeError( + "Pool size should be higher than 0 and lower or equal to " + f"{CNX_POOL_MAXSIZE}" + ) + self._pool_size = pool_size + + def _set_pool_name(self, pool_name: str) -> None: + r"""Set the name of the pool. + + This method checks the validity and sets the name of the pool. + + Raises an AttributeError when pool_name contains illegal characters + ([^a-zA-Z0-9._\-*$#]) or is longer than pooling.CNX_POOL_MAXNAMESIZE. + """ + if CNX_POOL_NAMEREGEX.search(pool_name): + raise AttributeError(f"Pool name '{pool_name}' contains illegal characters") + if len(pool_name) > CNX_POOL_MAXNAMESIZE: + raise AttributeError(f"Pool name '{pool_name}' is too long") + self._pool_name = pool_name + + def _queue_connection(self, cnx: Union[MySQLConnection, CMySQLConnection]) -> None: + """Put connection back in the queue + + This method is putting a connection back in the queue. It will not + acquire a lock as the methods using _queue_connection() will have it + set. + + Raises PoolError on errors. + """ + if not isinstance(cnx, MYSQL_CNX_CLASS): + raise PoolError("Connection instance not subclass of MySQLConnection") + + try: + self._cnx_queue.put(cnx, block=False) + except queue.Full as err: + raise PoolError("Failed adding connection; queue is full") from err + + def add_connection( + self, cnx: Optional[Union[MySQLConnection, CMySQLConnection]] = None + ) -> None: + """Add a connection to the pool + + This method instantiates a MySQLConnection using the configuration + passed when initializing the MySQLConnectionPool instance or using + the set_config() method. + If cnx is a MySQLConnection instance, it will be added to the + queue. + + Raises PoolError when no configuration is set, when no more + connection can be added (maximum reached) or when the connection + can not be instantiated. + """ + with CONNECTION_POOL_LOCK: + if not self._cnx_config: + raise PoolError("Connection configuration not available") + + if self._cnx_queue.full(): + raise PoolError("Failed adding connection; queue is full") + + if not cnx: + cnx = connect(**self._cnx_config) # type: ignore[assignment] + try: + if ( + self._reset_session + and self._cnx_config["compress"] + and cnx.get_server_version() < (5, 7, 3) + ): + raise NotSupportedError( + "Pool reset session is not supported with " + "compression for MySQL server version 5.7.2 " + "or earlier" + ) + except KeyError: + pass + + cnx.pool_config_version = self._config_version + else: + if not isinstance(cnx, MYSQL_CNX_CLASS): + raise PoolError( + "Connection instance not subclass of MySQLConnection" + ) + + self._queue_connection(cnx) + + def get_connection(self) -> PooledMySQLConnection: + """Get a connection from the pool + + This method returns an PooledMySQLConnection instance which + has a reference to the pool that created it, and the next available + MySQL connection. + + When the MySQL connection is not connect, a reconnect is attempted. + + Raises PoolError on errors. + + Returns a PooledMySQLConnection instance. + """ + with CONNECTION_POOL_LOCK: + try: + cnx = self._cnx_queue.get(block=False) + except queue.Empty as err: + raise PoolError("Failed getting connection; pool exhausted") from err + + if ( + not cnx.is_connected() + or self._config_version != cnx.pool_config_version + ): + cnx.config(**self._cnx_config) + try: + cnx.reconnect() + except InterfaceError: + # Failed to reconnect, give connection back to pool + self._queue_connection(cnx) + raise + cnx.pool_config_version = self._config_version + + return PooledMySQLConnection(self, cnx) + + def _remove_connections(self) -> int: + """Close all connections + + This method closes all connections. It returns the number + of connections it closed. + + Used mostly for tests. + + Returns int. + """ + with CONNECTION_POOL_LOCK: + cnt = 0 + cnxq = self._cnx_queue + while cnxq.qsize(): + try: + cnx = cnxq.get(block=False) + cnx.disconnect() + cnt += 1 + except queue.Empty: + return cnt + except PoolError: + raise + except Error: + # Any other error when closing means connection is closed + pass + + return cnt diff --git a/virt/lib/python3.9/site-packages/mysql/connector/utils 3.py b/virt/lib/python3.9/site-packages/mysql/connector/utils 3.py new file mode 100644 index 00000000..b477a79b --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysql/connector/utils 3.py @@ -0,0 +1,636 @@ +# Copyright (c) 2009, 2022, Oracle and/or its affiliates. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, as +# published by the Free Software Foundation. +# +# This program is also distributed with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an +# additional permission to link the program and your derivative works +# with the separately licensed software that they have included with +# MySQL. +# +# Without limiting anything contained in the foregoing, this file, +# which is part of MySQL Connector/Python, is also subject to the +# Universal FOSS Exception, version 1.0, a copy of which can be found at +# http://oss.oracle.com/licenses/universal-foss-exception. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License, version 2.0, for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""Utilities.""" + +import os +import platform +import struct +import subprocess +import sys +import unicodedata + +from decimal import Decimal +from functools import lru_cache +from stringprep import ( + in_table_a1, + in_table_b1, + in_table_c3, + in_table_c4, + in_table_c5, + in_table_c6, + in_table_c7, + in_table_c8, + in_table_c9, + in_table_c11, + in_table_c12, + in_table_c21_c22, + in_table_d1, + in_table_d2, +) +from typing import Callable, Dict, List, Optional, Tuple, Type, Union + +from .custom_types import HexLiteral +from .types import StrOrBytes + +__MYSQL_DEBUG__: bool = False + +NUMERIC_TYPES: Tuple[Type[int], Type[float], Type[Decimal], Type[HexLiteral]] = ( + int, + float, + Decimal, + HexLiteral, +) + + +def intread(buf: Union[int, bytes]) -> int: + """Unpacks the given buffer to an integer""" + if isinstance(buf, int): + return buf + length = len(buf) + if length == 1: + return buf[0] + if length <= 4: + tmp = buf + b"\x00" * (4 - length) + return int(struct.unpack("<I", tmp)[0]) + tmp = buf + b"\x00" * (8 - length) + return int(struct.unpack("<Q", tmp)[0]) + + +def int1store(i: int) -> bytearray: + """ + Takes an unsigned byte (1 byte) and packs it as a bytes-object. + + Returns string. + """ + if i < 0 or i > 255: + raise ValueError("int1store requires 0 <= i <= 255") + return bytearray(struct.pack("<B", i)) + + +def int2store(i: int) -> bytearray: + """ + Takes an unsigned short (2 bytes) and packs it as a bytes-object. + + Returns string. + """ + if i < 0 or i > 65535: + raise ValueError("int2store requires 0 <= i <= 65535") + return bytearray(struct.pack("<H", i)) + + +def int3store(i: int) -> bytearray: + """ + Takes an unsigned integer (3 bytes) and packs it as a bytes-object. + + Returns string. + """ + if i < 0 or i > 16777215: + raise ValueError("int3store requires 0 <= i <= 16777215") + return bytearray(struct.pack("<I", i)[0:3]) + + +def int4store(i: int) -> bytearray: + """ + Takes an unsigned integer (4 bytes) and packs it as a bytes-object. + + Returns string. + """ + if i < 0 or i > 4294967295: + raise ValueError("int4store requires 0 <= i <= 4294967295") + return bytearray(struct.pack("<I", i)) + + +def int8store(i: int) -> bytearray: + """ + Takes an unsigned integer (8 bytes) and packs it as string. + + Returns string. + """ + if i < 0 or i > 18446744073709551616: + raise ValueError("int8store requires 0 <= i <= 2^64") + return bytearray(struct.pack("<Q", i)) + + +def intstore(i: int) -> bytearray: + """ + Takes an unsigned integers and packs it as a bytes-object. + + This function uses int1store, int2store, int3store, + int4store or int8store depending on the integer value. + + returns string. + """ + if i < 0 or i > 18446744073709551616: + raise ValueError("intstore requires 0 <= i <= 2^64") + + if i <= 255: + formed_string = int1store + elif i <= 65535: + formed_string = int2store + elif i <= 16777215: + formed_string = int3store + elif i <= 4294967295: + formed_string = int4store + else: + formed_string = int8store + + return formed_string(i) + + +def lc_int(i: int) -> bytes: + """ + Takes an unsigned integer and packs it as bytes, + with the information of how much bytes the encoded int takes. + """ + if i < 0 or i > 18446744073709551616: + raise ValueError("Requires 0 <= i <= 2^64") + + if i < 251: + return bytearray(struct.pack("<B", i)) + if i <= 65535: + return b"\xfc" + bytearray(struct.pack("<H", i)) + if i <= 16777215: + return b"\xfd" + bytearray(struct.pack("<I", i)[0:3]) + + return b"\xfe" + bytearray(struct.pack("<Q", i)) + + +def read_bytes(buf: bytes, size: int) -> Tuple[bytes, bytes]: + """ + Reads bytes from a buffer. + + Returns a tuple with buffer less the read bytes, and the bytes. + """ + res = buf[0:size] + return (buf[size:], res) + + +def read_lc_string(buf: bytes) -> Tuple[bytes, Optional[bytes]]: + """ + Takes a buffer and reads a length coded string from the start. + + This is how Length coded strings work + + If the string is 250 bytes long or smaller, then it looks like this: + + <-- 1b --> + +----------+------------------------- + | length | a string goes here + +----------+------------------------- + + If the string is bigger than 250, then it looks like this: + + <- 1b -><- 2/3/8 -> + +------+-----------+------------------------- + | type | length | a string goes here + +------+-----------+------------------------- + + if type == \xfc: + length is code in next 2 bytes + elif type == \xfd: + length is code in next 3 bytes + elif type == \xfe: + length is code in next 8 bytes + + NULL has a special value. If the buffer starts with \xfb then + it's a NULL and we return None as value. + + Returns a tuple (trucated buffer, bytes). + """ + if buf[0] == 251: # \xfb + # NULL value + return (buf[1:], None) + + length = lsize = 0 + fst = buf[0] + + if fst <= 250: # \xFA + length = fst + return (buf[1 + length :], buf[1 : length + 1]) + if fst == 252: + lsize = 2 + elif fst == 253: + lsize = 3 + elif fst == 254: + lsize = 8 + + length = intread(buf[1 : lsize + 1]) + return (buf[lsize + length + 1 :], buf[lsize + 1 : length + lsize + 1]) + + +def read_lc_string_list(buf: bytes) -> Optional[Tuple[Optional[bytes], ...]]: + """Reads all length encoded strings from the given buffer + + Returns a list of bytes + """ + byteslst: List[Optional[bytes]] = [] + + sizes = {252: 2, 253: 3, 254: 8} + + buf_len = len(buf) + pos = 0 + + while pos < buf_len: + first = buf[pos] + if first == 255: + # Special case when MySQL error 1317 is returned by MySQL. + # We simply return None. + return None + if first == 251: + # NULL value + byteslst.append(None) + pos += 1 + else: + if first <= 250: + length = first + byteslst.append(buf[(pos + 1) : length + (pos + 1)]) + pos += 1 + length + else: + lsize = 0 + try: + lsize = sizes[first] + except KeyError: + return None + length = intread(buf[(pos + 1) : lsize + (pos + 1)]) + byteslst.append(buf[pos + 1 + lsize : length + lsize + (pos + 1)]) + pos += 1 + lsize + length + + return tuple(byteslst) + + +def read_string( + buf: bytes, + end: Optional[bytes] = None, + size: Optional[int] = None, +) -> Tuple[bytes, bytes]: + """ + Reads a string up until a character or for a given size. + + Returns a tuple (trucated buffer, string). + """ + if end is None and size is None: + raise ValueError("read_string() needs either end or size") + + if end is not None: + try: + idx = buf.index(end) + except ValueError as err: + raise ValueError("end byte not present in buffer") from err + return (buf[idx + 1 :], buf[0:idx]) + if size is not None: + return read_bytes(buf, size) + + raise ValueError("read_string() needs either end or size (weird)") + + +def read_int(buf: bytes, size: int) -> Tuple[bytes, int]: + """Read an integer from buffer + + Returns a tuple (truncated buffer, int) + """ + res = intread(buf[0:size]) + return (buf[size:], res) + + +def read_lc_int(buf: bytes) -> Tuple[bytes, Optional[int]]: + """ + Takes a buffer and reads an length code string from the start. + + Returns a tuple with buffer less the integer and the integer read. + """ + if not buf: + raise ValueError("Empty buffer.") + + lcbyte = buf[0] + if lcbyte == 251: + return (buf[1:], None) + if lcbyte < 251: + return (buf[1:], int(lcbyte)) + if lcbyte == 252: + return (buf[3:], struct.unpack("<xH", buf[0:3])[0]) + if lcbyte == 253: + return (buf[4:], struct.unpack("<I", buf[1:4] + b"\x00")[0]) + if lcbyte == 254: + return (buf[9:], struct.unpack("<xQ", buf[0:9])[0]) + raise ValueError("Failed reading length encoded integer") + + +# +# For debugging +# +def _digest_buffer(buf: StrOrBytes) -> str: + """Debug function for showing buffers""" + if not isinstance(buf, str): + return "".join([f"\\x{c:02x}" for c in buf]) + return "".join([f"\\x{ord(c):02x}" for c in buf]) + + +def print_buffer( + abuffer: StrOrBytes, prefix: Optional[str] = None, limit: int = 30 +) -> None: + """Debug function printing output of _digest_buffer()""" + if prefix: + if limit and limit > 0: + digest = _digest_buffer(abuffer[0:limit]) + else: + digest = _digest_buffer(abuffer) + print(prefix + ": " + digest) + else: + print(_digest_buffer(abuffer)) + + +def _parse_os_release() -> Dict[str, str]: + """Parse the contents of /etc/os-release file. + + Returns: + A dictionary containing release information. + """ + distro: Dict[str, str] = {} + os_release_file = os.path.join("/etc", "os-release") + if not os.path.exists(os_release_file): + return distro + with open(os_release_file, encoding="utf-8") as file_obj: + for line in file_obj: + key_value = line.split("=") + if len(key_value) != 2: + continue + key = key_value[0].lower() + value = key_value[1].rstrip("\n").strip('"') + distro[key] = value + return distro + + +def _parse_lsb_release() -> Dict[str, str]: + """Parse the contents of /etc/lsb-release file. + + Returns: + A dictionary containing release information. + """ + distro = {} + lsb_release_file = os.path.join("/etc", "lsb-release") + if os.path.exists(lsb_release_file): + with open(lsb_release_file, encoding="utf-8") as file_obj: + for line in file_obj: + key_value = line.split("=") + if len(key_value) != 2: + continue + key = key_value[0].lower() + value = key_value[1].rstrip("\n").strip('"') + distro[key] = value + return distro + + +def _parse_lsb_release_command() -> Optional[Dict[str, str]]: + """Parse the output of the lsb_release command. + + Returns: + A dictionary containing release information. + """ + distro = {} + with open(os.devnull, "w", encoding="utf-8") as devnull: + try: + stdout = subprocess.check_output(("lsb_release", "-a"), stderr=devnull) + except OSError: + return None + lines = stdout.decode(sys.getfilesystemencoding()).splitlines() + for line in lines: + key_value = line.split(":") + if len(key_value) != 2: + continue + key = key_value[0].replace(" ", "_").lower() + value = key_value[1].strip("\t") + distro[key] = value + return distro + + +def linux_distribution() -> Tuple[str, str, str]: + """Tries to determine the name of the Linux OS distribution name. + + First tries to get information from ``/etc/os-release`` file. + If fails, tries to get the information of ``/etc/lsb-release`` file. + And finally the information of ``lsb-release`` command. + + Returns: + A tuple with (`name`, `version`, `codename`) + """ + distro: Optional[Dict[str, str]] = _parse_lsb_release() + if distro: + return ( + distro.get("distrib_id", ""), + distro.get("distrib_release", ""), + distro.get("distrib_codename", ""), + ) + + distro = _parse_lsb_release_command() + if distro: + return ( + distro.get("distributor_id", ""), + distro.get("release", ""), + distro.get("codename", ""), + ) + + distro = _parse_os_release() + if distro: + return ( + distro.get("name", ""), + distro.get("version_id", ""), + distro.get("version_codename", ""), + ) + + return ("", "", "") + + +def _get_unicode_read_direction(unicode_str: str) -> str: + """Get the readiness direction of the unicode string. + + We assume that the direction is "L-to-R" if the first character does not + indicate the direction is "R-to-L" or an "AL" (Arabic Letter). + """ + if unicode_str and unicodedata.bidirectional(unicode_str[0]) in ( + "R", + "AL", + ): + return "R-to-L" + return "L-to-R" + + +def _get_unicode_direction_rule(unicode_str: str) -> Dict[str, Callable[[str], bool]]: + """ + 1) The characters in section 5.8 MUST be prohibited. + + 2) If a string contains any RandALCat character, the string MUST NOT + contain any LCat character. + + 3) If a string contains any RandALCat character, a RandALCat + character MUST be the first character of the string, and a + RandALCat character MUST be the last character of the string. + """ + read_dir = _get_unicode_read_direction(unicode_str) + + # point 3) + if read_dir == "R-to-L": + if not (in_table_d1(unicode_str[0]) and in_table_d1(unicode_str[-1])): + raise ValueError( + "Invalid unicode Bidirectional sequence, if the " + "first character is RandALCat, the final character" + "must be RandALCat too." + ) + # characters from in_table_d2 are prohibited. + return {"Bidirectional Characters requirement 2 [StringPrep, d2]": in_table_d2} + + # characters from in_table_d1 are prohibited. + return {"Bidirectional Characters requirement 2 [StringPrep, d2]": in_table_d1} + + +def validate_normalized_unicode_string( + normalized_str: str, +) -> Optional[Tuple[str, str]]: + """Check for Prohibited Output according to rfc4013 profile. + + This profile specifies the following characters as prohibited input: + + - Non-ASCII space characters [StringPrep, C.1.2] + - ASCII control characters [StringPrep, C.2.1] + - Non-ASCII control characters [StringPrep, C.2.2] + - Private Use characters [StringPrep, C.3] + - Non-character code points [StringPrep, C.4] + - Surrogate code points [StringPrep, C.5] + - Inappropriate for plain text characters [StringPrep, C.6] + - Inappropriate for canonical representation characters [StringPrep, C.7] + - Change display properties or deprecated characters [StringPrep, C.8] + - Tagging characters [StringPrep, C.9] + + In addition of checking of Bidirectional Characters [StringPrep, Section 6] + and the Unassigned Code Points [StringPrep, A.1]. + + Returns: + A tuple with ("probited character", "breaked_rule") + """ + rules = { + "Space characters that contains the ASCII code points": in_table_c11, + "Space characters non-ASCII code points": in_table_c12, + "Unassigned Code Points [StringPrep, A.1]": in_table_a1, + "Non-ASCII space characters [StringPrep, C.1.2]": in_table_c12, + "ASCII control characters [StringPrep, C.2.1]": in_table_c21_c22, + "Private Use characters [StringPrep, C.3]": in_table_c3, + "Non-character code points [StringPrep, C.4]": in_table_c4, + "Surrogate code points [StringPrep, C.5]": in_table_c5, + "Inappropriate for plain text characters [StringPrep, C.6]": in_table_c6, + "Inappropriate for canonical representation characters [StringPrep, C.7]": in_table_c7, + "Change display properties or deprecated characters [StringPrep, C.8]": in_table_c8, + "Tagging characters [StringPrep, C.9]": in_table_c9, + } + + try: + rules.update(_get_unicode_direction_rule(normalized_str)) + except ValueError as err: + return normalized_str, str(err) + + for char in normalized_str: + for rule, func in rules.items(): + if func(char) and char != " ": + return char, rule + + return None + + +def normalize_unicode_string(a_string: str) -> str: + """normalizes a unicode string according to rfc4013 + + Normalization of a unicode string according to rfc4013: The SASLprep profile + of the "stringprep" algorithm. + + Normalization Unicode equivalence is the specification by the Unicode + character encoding standard that some sequences of code points represent + essentially the same character. + + This method normalizes using the Normalization Form Compatibility + Composition (NFKC), as described in rfc4013 2.2. + + Returns: + Normalized unicode string according to rfc4013. + """ + # Per rfc4013 2.1. Mapping + # non-ASCII space characters [StringPrep, C.1.2] are mapped to ' ' (U+0020) + # "commonly mapped to nothing" characters [StringPrep, B.1] are mapped to '' + nstr_list = [ + " " if in_table_c12(char) else "" if in_table_b1(char) else char + for char in a_string + ] + + nstr = "".join(nstr_list) + + # Per rfc4013 2.2. Use NFKC Normalization Form Compatibility Composition + # Characters are decomposed by compatibility, then recomposed by canonical + # equivalence. + nstr = unicodedata.normalize("NFKC", nstr) + if not nstr: + # Normilization results in empty string. + return "" + + return nstr + + +def init_bytearray( + payload: Union[int, StrOrBytes] = b"", encoding: str = "utf-8" +) -> bytearray: + """Initialize a bytearray from the payload.""" + if isinstance(payload, bytearray): + return payload + if isinstance(payload, int): + return bytearray(payload) + if not isinstance(payload, bytes): + try: + return bytearray(payload.encode(encoding=encoding)) + except AttributeError as err: + raise ValueError("payload must be a str or bytes") from err + + return bytearray(payload) + + +@lru_cache() +def get_platform() -> Dict[str, Union[str, Tuple[str, str]]]: + """Return a dict with the platform arch and OS version.""" + plat: Dict[str, Union[str, Tuple[str, str]]] = {"arch": "", "version": ""} + if os.name == "nt": + if "64" in platform.architecture()[0]: + plat["arch"] = "x86_64" + elif "32" in platform.architecture()[0]: + plat["arch"] = "i386" + else: + plat["arch"] = platform.architecture() + plat["version"] = f"Windows-{platform.win32_ver()[1]}" + else: + plat["arch"] = platform.machine() + if platform.system() == "Darwin": + plat["version"] = f"macOS-{platform.mac_ver()[0]}" + else: + plat["version"] = "-".join(linux_distribution()[0:2]) + + return plat diff --git a/virt/lib/python3.9/site-packages/mysql/opentelemetry/sdk/metrics/_internal/aggregation 3.py b/virt/lib/python3.9/site-packages/mysql/opentelemetry/sdk/metrics/_internal/aggregation 3.py new file mode 100644 index 00000000..3cdd7dac --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysql/opentelemetry/sdk/metrics/_internal/aggregation 3.py @@ -0,0 +1,1034 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# pylint: disable=too-many-lines + +from abc import ABC, abstractmethod +from bisect import bisect_left +from enum import IntEnum +from logging import getLogger +from math import inf +from threading import Lock +from typing import Generic, List, Optional, Sequence, TypeVar + +from mysql.opentelemetry.metrics import ( + Asynchronous, + Counter, + Histogram, + Instrument, + ObservableCounter, + ObservableGauge, + ObservableUpDownCounter, + Synchronous, + UpDownCounter, +) +from mysql.opentelemetry.sdk.metrics._internal.exponential_histogram.buckets import ( + Buckets, +) +from mysql.opentelemetry.sdk.metrics._internal.exponential_histogram.mapping.exponent_mapping import ( + ExponentMapping, +) +from mysql.opentelemetry.sdk.metrics._internal.exponential_histogram.mapping.logarithm_mapping import ( + LogarithmMapping, +) +from mysql.opentelemetry.sdk.metrics._internal.measurement import Measurement +from mysql.opentelemetry.sdk.metrics._internal.point import ( + Buckets as BucketsPoint, + ExponentialHistogramDataPoint, + Gauge, + Histogram as HistogramPoint, + HistogramDataPoint, + NumberDataPoint, + Sum, +) +from mysql.opentelemetry.util.types import Attributes + +_DataPointVarT = TypeVar("_DataPointVarT", NumberDataPoint, HistogramDataPoint) + +_logger = getLogger(__name__) + + +class AggregationTemporality(IntEnum): + """ + The temporality to use when aggregating data. + + Can be one of the following values: + """ + + UNSPECIFIED = 0 + DELTA = 1 + CUMULATIVE = 2 + + +class _Aggregation(ABC, Generic[_DataPointVarT]): + def __init__(self, attributes: Attributes): + self._lock = Lock() + self._attributes = attributes + self._previous_point = None + + @abstractmethod + def aggregate(self, measurement: Measurement) -> None: + pass + + @abstractmethod + def collect( + self, + aggregation_temporality: AggregationTemporality, + collection_start_nano: int, + ) -> Optional[_DataPointVarT]: + pass + + +class _DropAggregation(_Aggregation): + def aggregate(self, measurement: Measurement) -> None: + pass + + def collect( + self, + aggregation_temporality: AggregationTemporality, + collection_start_nano: int, + ) -> Optional[_DataPointVarT]: + pass + + +class _SumAggregation(_Aggregation[Sum]): + def __init__( + self, + attributes: Attributes, + instrument_is_monotonic: bool, + instrument_temporality: AggregationTemporality, + start_time_unix_nano: int, + ): + super().__init__(attributes) + + self._start_time_unix_nano = start_time_unix_nano + self._instrument_temporality = instrument_temporality + self._instrument_is_monotonic = instrument_is_monotonic + + if self._instrument_temporality is AggregationTemporality.DELTA: + self._value = 0 + else: + self._value = None + + def aggregate(self, measurement: Measurement) -> None: + with self._lock: + if self._value is None: + self._value = 0 + self._value = self._value + measurement.value + + def collect( + self, + aggregation_temporality: AggregationTemporality, + collection_start_nano: int, + ) -> Optional[NumberDataPoint]: + """ + Atomically return a point for the current value of the metric and + reset the aggregation value. + """ + if self._instrument_temporality is AggregationTemporality.DELTA: + with self._lock: + value = self._value + start_time_unix_nano = self._start_time_unix_nano + + self._value = 0 + self._start_time_unix_nano = collection_start_nano + + else: + with self._lock: + if self._value is None: + return None + value = self._value + self._value = None + start_time_unix_nano = self._start_time_unix_nano + + current_point = NumberDataPoint( + attributes=self._attributes, + start_time_unix_nano=start_time_unix_nano, + time_unix_nano=collection_start_nano, + value=value, + ) + + if self._previous_point is None or ( + self._instrument_temporality is aggregation_temporality + ): + # Output DELTA for a synchronous instrument + # Output CUMULATIVE for an asynchronous instrument + self._previous_point = current_point + return current_point + + if aggregation_temporality is AggregationTemporality.DELTA: + # Output temporality DELTA for an asynchronous instrument + value = current_point.value - self._previous_point.value + output_start_time_unix_nano = self._previous_point.time_unix_nano + + else: + # Output CUMULATIVE for a synchronous instrument + value = current_point.value + self._previous_point.value + output_start_time_unix_nano = self._previous_point.start_time_unix_nano + + current_point = NumberDataPoint( + attributes=self._attributes, + start_time_unix_nano=output_start_time_unix_nano, + time_unix_nano=current_point.time_unix_nano, + value=value, + ) + + self._previous_point = current_point + return current_point + + +class _LastValueAggregation(_Aggregation[Gauge]): + def __init__(self, attributes: Attributes): + super().__init__(attributes) + self._value = None + + def aggregate(self, measurement: Measurement): + with self._lock: + self._value = measurement.value + + def collect( + self, + aggregation_temporality: AggregationTemporality, + collection_start_nano: int, + ) -> Optional[_DataPointVarT]: + """ + Atomically return a point for the current value of the metric. + """ + with self._lock: + if self._value is None: + return None + value = self._value + self._value = None + + return NumberDataPoint( + attributes=self._attributes, + start_time_unix_nano=0, + time_unix_nano=collection_start_nano, + value=value, + ) + + +class _ExplicitBucketHistogramAggregation(_Aggregation[HistogramPoint]): + def __init__( + self, + attributes: Attributes, + start_time_unix_nano: int, + boundaries: Sequence[float] = ( + 0.0, + 5.0, + 10.0, + 25.0, + 50.0, + 75.0, + 100.0, + 250.0, + 500.0, + 750.0, + 1000.0, + 2500.0, + 5000.0, + 7500.0, + 10000.0, + ), + record_min_max: bool = True, + ): + super().__init__(attributes) + self._boundaries = tuple(boundaries) + self._bucket_counts = self._get_empty_bucket_counts() + self._min = inf + self._max = -inf + self._sum = 0 + self._record_min_max = record_min_max + self._start_time_unix_nano = start_time_unix_nano + # It is assumed that the "natural" aggregation temporality for a + # Histogram instrument is DELTA, like the "natural" aggregation + # temporality for a Counter is DELTA and the "natural" aggregation + # temporality for an ObservableCounter is CUMULATIVE. + self._instrument_temporality = AggregationTemporality.DELTA + + def _get_empty_bucket_counts(self) -> List[int]: + return [0] * (len(self._boundaries) + 1) + + def aggregate(self, measurement: Measurement) -> None: + value = measurement.value + + if self._record_min_max: + self._min = min(self._min, value) + self._max = max(self._max, value) + + self._sum += value + + self._bucket_counts[bisect_left(self._boundaries, value)] += 1 + + def collect( + self, + aggregation_temporality: AggregationTemporality, + collection_start_nano: int, + ) -> Optional[_DataPointVarT]: + """ + Atomically return a point for the current value of the metric. + """ + with self._lock: + if not any(self._bucket_counts): + return None + + bucket_counts = self._bucket_counts + start_time_unix_nano = self._start_time_unix_nano + sum_ = self._sum + max_ = self._max + min_ = self._min + + self._bucket_counts = self._get_empty_bucket_counts() + self._start_time_unix_nano = collection_start_nano + self._sum = 0 + self._min = inf + self._max = -inf + + current_point = HistogramDataPoint( + attributes=self._attributes, + start_time_unix_nano=start_time_unix_nano, + time_unix_nano=collection_start_nano, + count=sum(bucket_counts), + sum=sum_, + bucket_counts=tuple(bucket_counts), + explicit_bounds=self._boundaries, + min=min_, + max=max_, + ) + + if self._previous_point is None or ( + self._instrument_temporality is aggregation_temporality + ): + self._previous_point = current_point + return current_point + + max_ = current_point.max + min_ = current_point.min + + if aggregation_temporality is AggregationTemporality.CUMULATIVE: + start_time_unix_nano = self._previous_point.start_time_unix_nano + sum_ = current_point.sum + self._previous_point.sum + # Only update min/max on delta -> cumulative + max_ = max(current_point.max, self._previous_point.max) + min_ = min(current_point.min, self._previous_point.min) + bucket_counts = [ + curr_count + prev_count + for curr_count, prev_count in zip( + current_point.bucket_counts, + self._previous_point.bucket_counts, + ) + ] + else: + start_time_unix_nano = self._previous_point.time_unix_nano + sum_ = current_point.sum - self._previous_point.sum + bucket_counts = [ + curr_count - prev_count + for curr_count, prev_count in zip( + current_point.bucket_counts, + self._previous_point.bucket_counts, + ) + ] + + current_point = HistogramDataPoint( + attributes=self._attributes, + start_time_unix_nano=start_time_unix_nano, + time_unix_nano=current_point.time_unix_nano, + count=sum(bucket_counts), + sum=sum_, + bucket_counts=tuple(bucket_counts), + explicit_bounds=current_point.explicit_bounds, + min=min_, + max=max_, + ) + self._previous_point = current_point + return current_point + + +# pylint: disable=protected-access +class _ExponentialBucketHistogramAggregation(_Aggregation[HistogramPoint]): + # _min_max_size and _max_max_size are the smallest and largest values + # the max_size parameter may have, respectively. + + # _min_max_size is is the smallest reasonable value which is small enough + # to contain the entire normal floating point range at the minimum scale. + _min_max_size = 2 + + # _max_max_size is an arbitrary limit meant to limit accidental creation of + # giant exponential bucket histograms. + _max_max_size = 16384 + + def __init__( + self, + attributes: Attributes, + start_time_unix_nano: int, + # This is the default maximum number of buckets per positive or + # negative number range. The value 160 is specified by mysql.OpenTelemetry. + # See the derivation here: + # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#exponential-bucket-histogram-aggregation) + max_size: int = 160, + ): + super().__init__(attributes) + # max_size is the maximum capacity of the positive and negative + # buckets. + if max_size < self._min_max_size: + raise ValueError( + f"Buckets max size {max_size} is smaller than " + "minimum max size {self._min_max_size}" + ) + + if max_size > self._max_max_size: + raise ValueError( + f"Buckets max size {max_size} is larger than " + "maximum max size {self._max_max_size}" + ) + + self._max_size = max_size + + # _sum is the sum of all the values aggregated by this aggregator. + self._sum = 0 + + # _count is the count of all calls to aggregate. + self._count = 0 + + # _zero_count is the count of all the calls to aggregate when the value + # to be aggregated is exactly 0. + self._zero_count = 0 + + # _min is the smallest value aggregated by this aggregator. + self._min = inf + + # _max is the smallest value aggregated by this aggregator. + self._max = -inf + + # _positive holds the positive values. + self._positive = Buckets() + + # _negative holds the negative values by their absolute value. + self._negative = Buckets() + + # _mapping corresponds to the current scale, is shared by both the + # positive and negative buckets. + self._mapping = LogarithmMapping(LogarithmMapping._max_scale) + + self._instrument_temporality = AggregationTemporality.DELTA + self._start_time_unix_nano = start_time_unix_nano + + self._previous_scale = None + self._previous_start_time_unix_nano = None + self._previous_sum = None + self._previous_max = None + self._previous_min = None + self._previous_positive = None + self._previous_negative = None + + def aggregate(self, measurement: Measurement) -> None: + # pylint: disable=too-many-branches,too-many-statements, too-many-locals + + with self._lock: + value = measurement.value + + # 0. Set the following attributes: + # _min + # _max + # _count + # _zero_count + # _sum + if value < self._min: + self._min = value + + if value > self._max: + self._max = value + + self._count += 1 + + if value == 0: + self._zero_count += 1 + # No need to do anything else if value is zero, just increment the + # zero count. + return + + self._sum += value + + # 1. Use the positive buckets for positive values and the negative + # buckets for negative values. + if value > 0: + buckets = self._positive + + else: + # Both exponential and logarithm mappings use only positive values + # so the absolute value is used here. + value = -value + buckets = self._negative + + # 2. Compute the index for the value at the current scale. + index = self._mapping.map_to_index(value) + + # IncrementIndexBy starts here + + # 3. Determine if a change of scale is needed. + is_rescaling_needed = False + + if len(buckets) == 0: + buckets.index_start = index + buckets.index_end = index + buckets.index_base = index + + elif ( + index < buckets.index_start + and (buckets.index_end - index) >= self._max_size + ): + is_rescaling_needed = True + low = index + high = buckets.index_end + + elif ( + index > buckets.index_end + and (index - buckets.index_start) >= self._max_size + ): + is_rescaling_needed = True + low = buckets.index_start + high = index + + # 4. Rescale the mapping if needed. + if is_rescaling_needed: + self._downscale( + self._get_scale_change(low, high), + self._positive, + self._negative, + ) + + index = self._mapping.map_to_index(value) + + # 5. If the index is outside + # [buckets.index_start, buckets.index_end] readjust the buckets + # boundaries or add more buckets. + if index < buckets.index_start: + span = buckets.index_end - index + + if span >= len(buckets.counts): + buckets.grow(span + 1, self._max_size) + + buckets.index_start = index + + elif index > buckets.index_end: + span = index - buckets.index_start + + if span >= len(buckets.counts): + buckets.grow(span + 1, self._max_size) + + buckets.index_end = index + + # 6. Compute the index of the bucket to be incremented. + bucket_index = index - buckets.index_base + + if bucket_index < 0: + bucket_index += len(buckets.counts) + + # 7. Increment the bucket. + buckets.increment_bucket(bucket_index) + + def collect( + self, + aggregation_temporality: AggregationTemporality, + collection_start_nano: int, + ) -> Optional[_DataPointVarT]: + """ + Atomically return a point for the current value of the metric. + """ + # pylint: disable=too-many-statements, too-many-locals + + with self._lock: + if self._count == 0: + return None + + current_negative = self._negative + current_positive = self._positive + current_zero_count = self._zero_count + current_count = self._count + current_start_time_unix_nano = self._start_time_unix_nano + current_sum = self._sum + current_max = self._max + if current_max == -inf: + current_max = None + current_min = self._min + if current_min == inf: + current_min = None + + if self._count == self._zero_count: + current_scale = 0 + + else: + current_scale = self._mapping.scale + + self._negative = Buckets() + self._positive = Buckets() + self._start_time_unix_nano = collection_start_nano + self._sum = 0 + self._count = 0 + self._zero_count = 0 + self._min = inf + self._max = -inf + + current_point = ExponentialHistogramDataPoint( + attributes=self._attributes, + start_time_unix_nano=current_start_time_unix_nano, + time_unix_nano=collection_start_nano, + count=current_count, + sum=current_sum, + scale=current_scale, + zero_count=current_zero_count, + positive=BucketsPoint( + offset=current_positive.offset, + bucket_counts=current_positive.counts, + ), + negative=BucketsPoint( + offset=current_negative.offset, + bucket_counts=current_negative.counts, + ), + # FIXME: Find the right value for flags + flags=0, + min=current_min, + max=current_max, + ) + + if self._previous_scale is None or ( + self._instrument_temporality is aggregation_temporality + ): + self._previous_scale = current_scale + self._previous_start_time_unix_nano = current_start_time_unix_nano + self._previous_max = current_max + self._previous_min = current_min + self._previous_sum = current_sum + self._previous_positive = current_positive + self._previous_negative = current_negative + + return current_point + + min_scale = min(self._previous_scale, current_scale) + + low_positive, high_positive = self._get_low_high_previous_current( + self._previous_positive, current_positive, min_scale + ) + low_negative, high_negative = self._get_low_high_previous_current( + self._previous_negative, current_negative, min_scale + ) + + min_scale = min( + min_scale - self._get_scale_change(low_positive, high_positive), + min_scale - self._get_scale_change(low_negative, high_negative), + ) + + # FIXME Go implementation checks if the histogram (not the mapping + # but the histogram) has a count larger than zero, if not, scale + # (the histogram scale) would be zero. See exponential.go 191 + self._downscale( + self._mapping.scale - min_scale, + self._previous_positive, + self._previous_negative, + ) + + if aggregation_temporality is AggregationTemporality.CUMULATIVE: + start_time_unix_nano = self._previous_start_time_unix_nano + sum_ = current_sum + self._previous_sum + # Only update min/max on delta -> cumulative + max_ = max(current_max, self._previous_max) + min_ = min(current_min, self._previous_min) + + self._merge( + self._previous_positive, + current_positive, + current_scale, + min_scale, + aggregation_temporality, + ) + self._merge( + self._previous_negative, + current_negative, + current_scale, + min_scale, + aggregation_temporality, + ) + + else: + start_time_unix_nano = self._previous_start_time_unix_nano + sum_ = current_sum - self._previous_sum + max_ = current_max + min_ = current_min + + self._merge( + self._previous_positive, + current_positive, + current_scale, + min_scale, + aggregation_temporality, + ) + self._merge( + self._previous_negative, + current_negative, + current_scale, + min_scale, + aggregation_temporality, + ) + + current_point = ExponentialHistogramDataPoint( + attributes=self._attributes, + start_time_unix_nano=start_time_unix_nano, + time_unix_nano=collection_start_nano, + count=current_count, + sum=sum_, + scale=current_scale, + zero_count=current_zero_count, + positive=BucketsPoint( + offset=current_positive.offset, + bucket_counts=current_positive.counts, + ), + negative=BucketsPoint( + offset=current_negative.offset, + bucket_counts=current_negative.counts, + ), + # FIXME: Find the right value for flags + flags=0, + min=min_, + max=max_, + ) + + self._previous_scale = current_scale + self._previous_positive = current_positive + self._previous_negative = current_negative + self._previous_start_time_unix_nano = current_start_time_unix_nano + self._previous_sum = current_sum + + return current_point + + def _get_low_high_previous_current( + self, previous_point_buckets, current_point_buckets, min_scale + ): + (previous_point_low, previous_point_high) = self._get_low_high( + previous_point_buckets, min_scale + ) + (current_point_low, current_point_high) = self._get_low_high( + current_point_buckets, min_scale + ) + + if current_point_low > current_point_high: + low = previous_point_low + high = previous_point_high + + elif previous_point_low > previous_point_high: + low = current_point_low + high = current_point_high + + else: + low = min(previous_point_low, current_point_low) + high = max(previous_point_high, current_point_high) + + return low, high + + def _get_low_high(self, buckets, min_scale): + if buckets.counts == [0]: + return 0, -1 + + shift = self._mapping._scale - min_scale + + return buckets.index_start >> shift, buckets.index_end >> shift + + def _get_scale_change(self, low, high): + change = 0 + + while high - low >= self._max_size: + high = high >> 1 + low = low >> 1 + + change += 1 + + return change + + def _downscale(self, change: int, positive, negative): + if change == 0: + return + + if change < 0: + raise Exception("Invalid change of scale") + + new_scale = self._mapping.scale - change + + positive.downscale(change) + negative.downscale(change) + + if new_scale <= 0: + mapping = ExponentMapping(new_scale) + else: + mapping = LogarithmMapping(new_scale) + + self._mapping = mapping + + def _merge( + self, + previous_buckets, + current_buckets, + current_scale, + min_scale, + aggregation_temporality, + ): + current_change = current_scale - min_scale + + for current_bucket_index, current_bucket in enumerate(current_buckets.counts): + if current_bucket == 0: + continue + + # Not considering the case where len(previous_buckets) == 0. This + # would not happen because self._previous_point is only assigned to + # an ExponentialHistogramDataPoint object if self._count != 0. + + index = (current_buckets.offset + current_bucket_index) >> current_change + + if index < previous_buckets.index_start: + span = previous_buckets.index_end - index + + if span >= self._max_size: + raise Exception("Incorrect merge scale") + + if span >= len(previous_buckets.counts): + previous_buckets.grow(span + 1, self._max_size) + + previous_buckets.index_start = index + + if index > previous_buckets.index_end: + span = index - previous_buckets.index_end + + if span >= self._max_size: + raise Exception("Incorrect merge scale") + + if span >= len(previous_buckets.counts): + previous_buckets.grow(span + 1, self._max_size) + + previous_buckets.index_end = index + + bucket_index = index - previous_buckets.index_base + + if bucket_index < 0: + bucket_index += len(previous_buckets.counts) + + if aggregation_temporality is AggregationTemporality.DELTA: + current_bucket = -current_bucket + + previous_buckets.increment_bucket(bucket_index, increment=current_bucket) + + +class Aggregation(ABC): + """ + Base class for all aggregation types. + """ + + @abstractmethod + def _create_aggregation( + self, + instrument: Instrument, + attributes: Attributes, + start_time_unix_nano: int, + ) -> _Aggregation: + """Creates an aggregation""" + + +class DefaultAggregation(Aggregation): + """ + The default aggregation to be used in a `View`. + + This aggregation will create an actual aggregation depending on the + instrument type, as specified next: + + ==================================================== ==================================== + Instrument Aggregation + ==================================================== ==================================== + `mysql.opentelemetry.sdk.metrics.Counter` `SumAggregation` + `mysql.opentelemetry.sdk.metrics.UpDownCounter` `SumAggregation` + `mysql.opentelemetry.sdk.metrics.ObservableCounter` `SumAggregation` + `mysql.opentelemetry.sdk.metrics.ObservableUpDownCounter` `SumAggregation` + `mysql.opentelemetry.sdk.metrics.Histogram` `ExplicitBucketHistogramAggregation` + `mysql.opentelemetry.sdk.metrics.ObservableGauge` `LastValueAggregation` + ==================================================== ==================================== + """ + + def _create_aggregation( + self, + instrument: Instrument, + attributes: Attributes, + start_time_unix_nano: int, + ) -> _Aggregation: + # pylint: disable=too-many-return-statements + if isinstance(instrument, Counter): + return _SumAggregation( + attributes, + instrument_is_monotonic=True, + instrument_temporality=AggregationTemporality.DELTA, + start_time_unix_nano=start_time_unix_nano, + ) + if isinstance(instrument, UpDownCounter): + return _SumAggregation( + attributes, + instrument_is_monotonic=False, + instrument_temporality=AggregationTemporality.DELTA, + start_time_unix_nano=start_time_unix_nano, + ) + + if isinstance(instrument, ObservableCounter): + return _SumAggregation( + attributes, + instrument_is_monotonic=True, + instrument_temporality=AggregationTemporality.CUMULATIVE, + start_time_unix_nano=start_time_unix_nano, + ) + + if isinstance(instrument, ObservableUpDownCounter): + return _SumAggregation( + attributes, + instrument_is_monotonic=False, + instrument_temporality=AggregationTemporality.CUMULATIVE, + start_time_unix_nano=start_time_unix_nano, + ) + + if isinstance(instrument, Histogram): + return _ExplicitBucketHistogramAggregation(attributes, start_time_unix_nano) + + if isinstance(instrument, ObservableGauge): + return _LastValueAggregation(attributes) + + raise Exception(f"Invalid instrument type {type(instrument)} found") + + +class ExponentialBucketHistogramAggregation(Aggregation): + def __init__( + self, + max_size: int = 160, + ): + self._max_size = max_size + + def _create_aggregation( + self, + instrument: Instrument, + attributes: Attributes, + start_time_unix_nano: int, + ) -> _Aggregation: + return _ExponentialBucketHistogramAggregation( + attributes, + start_time_unix_nano, + max_size=self._max_size, + ) + + +class ExplicitBucketHistogramAggregation(Aggregation): + """This aggregation informs the SDK to collect: + + - Count of Measurement values falling within explicit bucket boundaries. + - Arithmetic sum of Measurement values in population. This SHOULD NOT be collected when used with instruments that record negative measurements, e.g. UpDownCounter or ObservableGauge. + - Min (optional) Measurement value in population. + - Max (optional) Measurement value in population. + + + Args: + boundaries: Array of increasing values representing explicit bucket boundary values. + record_min_max: Whether to record min and max. + """ + + def __init__( + self, + boundaries: Sequence[float] = ( + 0.0, + 5.0, + 10.0, + 25.0, + 50.0, + 75.0, + 100.0, + 250.0, + 500.0, + 750.0, + 1000.0, + 2500.0, + 5000.0, + 7500.0, + 10000.0, + ), + record_min_max: bool = True, + ) -> None: + self._boundaries = boundaries + self._record_min_max = record_min_max + + def _create_aggregation( + self, + instrument: Instrument, + attributes: Attributes, + start_time_unix_nano: int, + ) -> _Aggregation: + return _ExplicitBucketHistogramAggregation( + attributes, + start_time_unix_nano, + self._boundaries, + self._record_min_max, + ) + + +class SumAggregation(Aggregation): + """This aggregation informs the SDK to collect: + + - The arithmetic sum of Measurement values. + """ + + def _create_aggregation( + self, + instrument: Instrument, + attributes: Attributes, + start_time_unix_nano: int, + ) -> _Aggregation: + temporality = AggregationTemporality.UNSPECIFIED + if isinstance(instrument, Synchronous): + temporality = AggregationTemporality.DELTA + elif isinstance(instrument, Asynchronous): + temporality = AggregationTemporality.CUMULATIVE + + return _SumAggregation( + attributes, + isinstance(instrument, (Counter, ObservableCounter)), + temporality, + start_time_unix_nano, + ) + + +class LastValueAggregation(Aggregation): + """ + This aggregation informs the SDK to collect: + + - The last Measurement. + - The timestamp of the last Measurement. + """ + + def _create_aggregation( + self, + instrument: Instrument, + attributes: Attributes, + start_time_unix_nano: int, + ) -> _Aggregation: + return _LastValueAggregation(attributes) + + +class DropAggregation(Aggregation): + """Using this aggregation will make all measurements be ignored.""" + + def _create_aggregation( + self, + instrument: Instrument, + attributes: Attributes, + start_time_unix_nano: int, + ) -> _Aggregation: + return _DropAggregation(attributes) diff --git a/virt/lib/python3.9/site-packages/mysql/opentelemetry/sdk/trace/__init__ 3.py b/virt/lib/python3.9/site-packages/mysql/opentelemetry/sdk/trace/__init__ 3.py new file mode 100644 index 00000000..11c63e6f --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysql/opentelemetry/sdk/trace/__init__ 3.py @@ -0,0 +1,1206 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# pylint: disable=too-many-lines +import abc +import atexit +import concurrent.futures +import json +import logging +import threading +import traceback +import typing + +from collections import OrderedDict +from contextlib import contextmanager +from os import environ +from time import time_ns +from types import MappingProxyType, TracebackType +from typing import Any, Callable, Dict, Iterator, Optional, Sequence, Tuple, Type, Union +from warnings import filterwarnings + +from deprecated import deprecated +from mysql.opentelemetry import context as context_api, trace as trace_api +from mysql.opentelemetry.attributes import BoundedAttributes +from mysql.opentelemetry.sdk import util +from mysql.opentelemetry.sdk.environment_variables import ( + OTEL_ATTRIBUTE_COUNT_LIMIT, + OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, + OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT, + OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, + OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, + OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT, + OTEL_SPAN_EVENT_COUNT_LIMIT, + OTEL_SPAN_LINK_COUNT_LIMIT, +) +from mysql.opentelemetry.sdk.resources import Resource +from mysql.opentelemetry.sdk.trace import sampling +from mysql.opentelemetry.sdk.trace.id_generator import IdGenerator, RandomIdGenerator +from mysql.opentelemetry.sdk.util import BoundedList +from mysql.opentelemetry.sdk.util.instrumentation import ( + InstrumentationInfo, + InstrumentationScope, +) +from mysql.opentelemetry.trace import SpanContext +from mysql.opentelemetry.trace.status import Status, StatusCode +from mysql.opentelemetry.util import types + +logger = logging.getLogger(__name__) + +_DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT = 128 +_DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT = 128 +_DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT = 128 +_DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT = 128 +_DEFAULT_OTEL_SPAN_EVENT_COUNT_LIMIT = 128 +_DEFAULT_OTEL_SPAN_LINK_COUNT_LIMIT = 128 + + +_ENV_VALUE_UNSET = "" + + +class SpanProcessor: + """Interface which allows hooks for SDK's `Span` start and end method + invocations. + + Span processors can be registered directly using + :func:`TracerProvider.add_span_processor` and they are invoked + in the same order as they were registered. + """ + + def on_start( + self, + span: "Span", + parent_context: Optional[context_api.Context] = None, + ) -> None: + """Called when a :class:`mysql.opentelemetry.trace.Span` is started. + + This method is called synchronously on the thread that starts the + span, therefore it should not block or throw an exception. + + Args: + span: The :class:`mysql.opentelemetry.trace.Span` that just started. + parent_context: The parent context of the span that just started. + """ + + def on_end(self, span: "ReadableSpan") -> None: + """Called when a :class:`mysql.opentelemetry.trace.Span` is ended. + + This method is called synchronously on the thread that ends the + span, therefore it should not block or throw an exception. + + Args: + span: The :class:`mysql.opentelemetry.trace.Span` that just ended. + """ + + def shutdown(self) -> None: + """Called when a :class:`mysql.opentelemetry.sdk.trace.TracerProvider` is shutdown.""" + + def force_flush(self, timeout_millis: int = 30000) -> bool: + """Export all ended spans to the configured Exporter that have not yet + been exported. + + Args: + timeout_millis: The maximum amount of time to wait for spans to be + exported. + + Returns: + False if the timeout is exceeded, True otherwise. + """ + + +# Temporary fix until https://github.com/PyCQA/pylint/issues/4098 is resolved +# pylint:disable=no-member +class SynchronousMultiSpanProcessor(SpanProcessor): + """Implementation of class:`SpanProcessor` that forwards all received + events to a list of span processors sequentially. + + The underlying span processors are called in sequential order as they were + added. + """ + + def __init__(self): + # use a tuple to avoid race conditions when adding a new span and + # iterating through it on "on_start" and "on_end". + self._span_processors = () # type: Tuple[SpanProcessor, ...] + self._lock = threading.Lock() + + def add_span_processor(self, span_processor: SpanProcessor) -> None: + """Adds a SpanProcessor to the list handled by this instance.""" + with self._lock: + self._span_processors += (span_processor,) + + def on_start( + self, + span: "Span", + parent_context: Optional[context_api.Context] = None, + ) -> None: + for sp in self._span_processors: + sp.on_start(span, parent_context=parent_context) + + def on_end(self, span: "ReadableSpan") -> None: + for sp in self._span_processors: + sp.on_end(span) + + def shutdown(self) -> None: + """Sequentially shuts down all underlying span processors.""" + for sp in self._span_processors: + sp.shutdown() + + def force_flush(self, timeout_millis: int = 30000) -> bool: + """Sequentially calls force_flush on all underlying + :class:`SpanProcessor` + + Args: + timeout_millis: The maximum amount of time over all span processors + to wait for spans to be exported. In case the first n span + processors exceeded the timeout followup span processors will be + skipped. + + Returns: + True if all span processors flushed their spans within the + given timeout, False otherwise. + """ + deadline_ns = time_ns() + timeout_millis * 1000000 + for sp in self._span_processors: + current_time_ns = time_ns() + if current_time_ns >= deadline_ns: + return False + + if not sp.force_flush((deadline_ns - current_time_ns) // 1000000): + return False + + return True + + +class ConcurrentMultiSpanProcessor(SpanProcessor): + """Implementation of :class:`SpanProcessor` that forwards all received + events to a list of span processors in parallel. + + Calls to the underlying span processors are forwarded in parallel by + submitting them to a thread pool executor and waiting until each span + processor finished its work. + + Args: + num_threads: The number of threads managed by the thread pool executor + and thus defining how many span processors can work in parallel. + """ + + def __init__(self, num_threads: int = 2): + # use a tuple to avoid race conditions when adding a new span and + # iterating through it on "on_start" and "on_end". + self._span_processors = () # type: Tuple[SpanProcessor, ...] + self._lock = threading.Lock() + self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) + + def add_span_processor(self, span_processor: SpanProcessor) -> None: + """Adds a SpanProcessor to the list handled by this instance.""" + with self._lock: + self._span_processors += (span_processor,) + + def _submit_and_await( + self, + func: Callable[[SpanProcessor], Callable[..., None]], + *args: Any, + **kwargs: Any, + ): + futures = [] + for sp in self._span_processors: + future = self._executor.submit(func(sp), *args, **kwargs) + futures.append(future) + for future in futures: + future.result() + + def on_start( + self, + span: "Span", + parent_context: Optional[context_api.Context] = None, + ) -> None: + self._submit_and_await( + lambda sp: sp.on_start, span, parent_context=parent_context + ) + + def on_end(self, span: "ReadableSpan") -> None: + self._submit_and_await(lambda sp: sp.on_end, span) + + def shutdown(self) -> None: + """Shuts down all underlying span processors in parallel.""" + self._submit_and_await(lambda sp: sp.shutdown) + + def force_flush(self, timeout_millis: int = 30000) -> bool: + """Calls force_flush on all underlying span processors in parallel. + + Args: + timeout_millis: The maximum amount of time to wait for spans to be + exported. + + Returns: + True if all span processors flushed their spans within the given + timeout, False otherwise. + """ + futures = [] + for sp in self._span_processors: # type: SpanProcessor + future = self._executor.submit(sp.force_flush, timeout_millis) + futures.append(future) + + timeout_sec = timeout_millis / 1e3 + done_futures, not_done_futures = concurrent.futures.wait(futures, timeout_sec) + if not_done_futures: + return False + + for future in done_futures: + if not future.result(): + return False + + return True + + +class EventBase(abc.ABC): + def __init__(self, name: str, timestamp: Optional[int] = None) -> None: + self._name = name + if timestamp is None: + self._timestamp = time_ns() + else: + self._timestamp = timestamp + + @property + def name(self) -> str: + return self._name + + @property + def timestamp(self) -> int: + return self._timestamp + + @property + @abc.abstractmethod + def attributes(self) -> types.Attributes: + pass + + +class Event(EventBase): + """A text annotation with a set of attributes. The attributes of an event + are immutable. + + Args: + name: Name of the event. + attributes: Attributes of the event. + timestamp: Timestamp of the event. If `None` it will filled + automatically. + """ + + def __init__( + self, + name: str, + attributes: types.Attributes = None, + timestamp: Optional[int] = None, + limit: Optional[int] = _DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, + ) -> None: + super().__init__(name, timestamp) + self._attributes = attributes + + @property + def attributes(self) -> types.Attributes: + return self._attributes + + +def _check_span_ended(func): + def wrapper(self, *args, **kwargs): + already_ended = False + with self._lock: # pylint: disable=protected-access + if self._end_time is None: # pylint: disable=protected-access + func(self, *args, **kwargs) + else: + already_ended = True + + if already_ended: + logger.warning("Tried calling %s on an ended span.", func.__name__) + + return wrapper + + +class ReadableSpan: + """Provides read-only access to span attributes""" + + def __init__( + self, + name: str = None, + context: trace_api.SpanContext = None, + parent: Optional[trace_api.SpanContext] = None, + resource: Resource = Resource.create({}), + attributes: types.Attributes = None, + events: Sequence[Event] = (), + links: Sequence[trace_api.Link] = (), + kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL, + instrumentation_info: InstrumentationInfo = None, + status: Status = Status(StatusCode.UNSET), + start_time: Optional[int] = None, + end_time: Optional[int] = None, + instrumentation_scope: InstrumentationScope = None, + ) -> None: + self._name = name + self._context = context + self._kind = kind + self._instrumentation_info = instrumentation_info + self._instrumentation_scope = instrumentation_scope + self._parent = parent + self._start_time = start_time + self._end_time = end_time + self._attributes = attributes + self._events = events + self._links = links + self._resource = resource + self._status = status + + @property + def dropped_attributes(self) -> int: + if self._attributes: + return self._attributes.dropped + return 0 + + @property + def dropped_events(self) -> int: + if self._events: + return self._events.dropped + return 0 + + @property + def dropped_links(self) -> int: + if self._links: + return self._links.dropped + return 0 + + @property + def name(self) -> str: + return self._name + + def get_span_context(self): + return self._context + + @property + def context(self): + return self._context + + @property + def kind(self) -> trace_api.SpanKind: + return self._kind + + @property + def parent(self) -> Optional[trace_api.SpanContext]: + return self._parent + + @property + def start_time(self) -> Optional[int]: + return self._start_time + + @property + def end_time(self) -> Optional[int]: + return self._end_time + + @property + def status(self) -> trace_api.Status: + return self._status + + @property + def attributes(self) -> types.Attributes: + return MappingProxyType(self._attributes) + + @property + def events(self) -> Sequence[Event]: + return tuple(event for event in self._events) + + @property + def links(self) -> Sequence[trace_api.Link]: + return tuple(link for link in self._links) + + @property + def resource(self) -> Resource: + return self._resource + + @property + @deprecated(version="1.11.1", reason="You should use instrumentation_scope") + def instrumentation_info(self) -> InstrumentationInfo: + return self._instrumentation_info + + @property + def instrumentation_scope(self) -> InstrumentationScope: + return self._instrumentation_scope + + def to_json(self, indent=4): + parent_id = None + if self.parent is not None: + if isinstance(self.parent, Span): + ctx = self.parent.context + parent_id = f"0x{trace_api.format_span_id(ctx.span_id)}" + elif isinstance(self.parent, SpanContext): + parent_id = f"0x{trace_api.format_span_id(self.parent.span_id)}" + + start_time = None + if self._start_time: + start_time = util.ns_to_iso_str(self._start_time) + + end_time = None + if self._end_time: + end_time = util.ns_to_iso_str(self._end_time) + + if self._status is not None: + status = OrderedDict() + status["status_code"] = str(self._status.status_code.name) + if self._status.description: + status["description"] = self._status.description + + f_span = OrderedDict() + + f_span["name"] = self._name + f_span["context"] = self._format_context(self._context) + f_span["kind"] = str(self.kind) + f_span["parent_id"] = parent_id + f_span["start_time"] = start_time + f_span["end_time"] = end_time + if self._status is not None: + f_span["status"] = status + f_span["attributes"] = self._format_attributes(self._attributes) + f_span["events"] = self._format_events(self._events) + f_span["links"] = self._format_links(self._links) + f_span["resource"] = json.loads(self.resource.to_json()) + + return json.dumps(f_span, indent=indent) + + @staticmethod + def _format_context(context): + x_ctx = OrderedDict() + x_ctx["trace_id"] = f"0x{trace_api.format_trace_id(context.trace_id)}" + x_ctx["span_id"] = f"0x{trace_api.format_span_id(context.span_id)}" + x_ctx["trace_state"] = repr(context.trace_state) + return x_ctx + + @staticmethod + def _format_attributes(attributes): + if isinstance(attributes, BoundedAttributes): + return attributes._dict # pylint: disable=protected-access + if isinstance(attributes, MappingProxyType): + return attributes.copy() + return attributes + + @staticmethod + def _format_events(events): + f_events = [] + for event in events: + f_event = OrderedDict() + f_event["name"] = event.name + f_event["timestamp"] = util.ns_to_iso_str(event.timestamp) + f_event[ + "attributes" + ] = Span._format_attributes( # pylint: disable=protected-access + event.attributes + ) + f_events.append(f_event) + return f_events + + @staticmethod + def _format_links(links): + f_links = [] + for link in links: + f_link = OrderedDict() + f_link[ + "context" + ] = Span._format_context( # pylint: disable=protected-access + link.context + ) + f_link[ + "attributes" + ] = Span._format_attributes( # pylint: disable=protected-access + link.attributes + ) + f_links.append(f_link) + return f_links + + +class SpanLimits: + """The limits that should be enforce on recorded data such as events, links, attributes etc. + + This class does not enforce any limits itself. It only provides an a way read limits from env, + default values and from user provided arguments. + + All limit arguments must be either a non-negative integer, ``None`` or ``SpanLimits.UNSET``. + + - All limit arguments are optional. + - If a limit argument is not set, the class will try to read its value from the corresponding + environment variable. + - If the environment variable is not set, the default value, if any, will be used. + + Limit precedence: + + - If a model specific limit is set, it will be used. + - Else if the corresponding global limit is set, it will be used. + - Else if the model specific limit has a default value, the default value will be used. + - Else if the global limit has a default value, the default value will be used. + + Args: + max_attributes: Maximum number of attributes that can be added to a span, event, and link. + Environment variable: OTEL_ATTRIBUTE_COUNT_LIMIT + Default: {_DEFAULT_ATTRIBUTE_COUNT_LIMIT} + max_events: Maximum number of events that can be added to a Span. + Environment variable: OTEL_SPAN_EVENT_COUNT_LIMIT + Default: {_DEFAULT_SPAN_EVENT_COUNT_LIMIT} + max_links: Maximum number of links that can be added to a Span. + Environment variable: OTEL_SPAN_LINK_COUNT_LIMIT + Default: {_DEFAULT_SPAN_LINK_COUNT_LIMIT} + max_span_attributes: Maximum number of attributes that can be added to a Span. + Environment variable: OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT + Default: {_DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT} + max_event_attributes: Maximum number of attributes that can be added to an Event. + Default: {_DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT} + max_link_attributes: Maximum number of attributes that can be added to a Link. + Default: {_DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT} + max_attribute_length: Maximum length an attribute value can have. Values longer than + the specified length will be truncated. + max_span_attribute_length: Maximum length a span attribute value can have. Values longer than + the specified length will be truncated. + """ + + UNSET = -1 + + def __init__( + self, + max_attributes: Optional[int] = None, + max_events: Optional[int] = None, + max_links: Optional[int] = None, + max_span_attributes: Optional[int] = None, + max_event_attributes: Optional[int] = None, + max_link_attributes: Optional[int] = None, + max_attribute_length: Optional[int] = None, + max_span_attribute_length: Optional[int] = None, + ): + # span events and links count + self.max_events = self._from_env_if_absent( + max_events, + OTEL_SPAN_EVENT_COUNT_LIMIT, + _DEFAULT_OTEL_SPAN_EVENT_COUNT_LIMIT, + ) + self.max_links = self._from_env_if_absent( + max_links, + OTEL_SPAN_LINK_COUNT_LIMIT, + _DEFAULT_OTEL_SPAN_LINK_COUNT_LIMIT, + ) + + # attribute count + global_max_attributes = self._from_env_if_absent( + max_attributes, OTEL_ATTRIBUTE_COUNT_LIMIT + ) + self.max_attributes = ( + global_max_attributes + if global_max_attributes is not None + else _DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT + ) + + self.max_span_attributes = self._from_env_if_absent( + max_span_attributes, + OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, + global_max_attributes + if global_max_attributes is not None + else _DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, + ) + self.max_event_attributes = self._from_env_if_absent( + max_event_attributes, + OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT, + global_max_attributes + if global_max_attributes is not None + else _DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT, + ) + self.max_link_attributes = self._from_env_if_absent( + max_link_attributes, + OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, + global_max_attributes + if global_max_attributes is not None + else _DEFAULT_OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, + ) + + # attribute length + self.max_attribute_length = self._from_env_if_absent( + max_attribute_length, + OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, + ) + self.max_span_attribute_length = self._from_env_if_absent( + max_span_attribute_length, + OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT, + # use global attribute length limit as default + self.max_attribute_length, + ) + + def __repr__(self): + return f"{type(self).__name__}(max_span_attributes={self.max_span_attributes}, max_events_attributes={self.max_event_attributes}, max_link_attributes={self.max_link_attributes}, max_attributes={self.max_attributes}, max_events={self.max_events}, max_links={self.max_links}, max_attribute_length={self.max_attribute_length})" + + @classmethod + def _from_env_if_absent( + cls, value: Optional[int], env_var: str, default: Optional[int] = None + ) -> Optional[int]: + if value == cls.UNSET: + return None + + err_msg = "{0} must be a non-negative integer but got {}" + + # if no value is provided for the limit, try to load it from env + if value is None: + # return default value if env var is not set + if env_var not in environ: + return default + + str_value = environ.get(env_var, "").strip().lower() + if str_value == _ENV_VALUE_UNSET: + return None + + try: + value = int(str_value) + except ValueError: + raise ValueError(err_msg.format(env_var, str_value)) + + if value < 0: + raise ValueError(err_msg.format(env_var, value)) + return value + + +_UnsetLimits = SpanLimits( + max_attributes=SpanLimits.UNSET, + max_events=SpanLimits.UNSET, + max_links=SpanLimits.UNSET, + max_span_attributes=SpanLimits.UNSET, + max_event_attributes=SpanLimits.UNSET, + max_link_attributes=SpanLimits.UNSET, + max_attribute_length=SpanLimits.UNSET, + max_span_attribute_length=SpanLimits.UNSET, +) + +# not removed for backward compat. please use SpanLimits instead. +SPAN_ATTRIBUTE_COUNT_LIMIT = ( + SpanLimits._from_env_if_absent( # pylint: disable=protected-access + None, + OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, + _DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, + ) +) + + +class Span(trace_api.Span, ReadableSpan): + """See `mysql.opentelemetry.trace.Span`. + + Users should create `Span` objects via the `Tracer` instead of this + constructor. + + Args: + name: The name of the operation this span represents + context: The immutable span context + parent: This span's parent's `mysql.opentelemetry.trace.SpanContext`, or + None if this is a root span + sampler: The sampler used to create this span + trace_config: TODO + resource: Entity producing telemetry + attributes: The span's attributes to be exported + events: Timestamped events to be exported + links: Links to other spans to be exported + span_processor: `SpanProcessor` to invoke when starting and ending + this `Span`. + limits: `SpanLimits` instance that was passed to the `TracerProvider` + """ + + def __new__(cls, *args, **kwargs): + if cls is Span: + raise TypeError("Span must be instantiated via a tracer.") + return super().__new__(cls) + + # pylint: disable=too-many-locals + def __init__( + self, + name: str, + context: trace_api.SpanContext, + parent: Optional[trace_api.SpanContext] = None, + sampler: Optional[sampling.Sampler] = None, + trace_config: None = None, # TODO + resource: Resource = Resource.create({}), + attributes: types.Attributes = None, + events: Sequence[Event] = None, + links: Sequence[trace_api.Link] = (), + kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL, + span_processor: SpanProcessor = SpanProcessor(), + instrumentation_info: InstrumentationInfo = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + limits=_UnsetLimits, + instrumentation_scope: InstrumentationScope = None, + ) -> None: + super().__init__( + name=name, + context=context, + parent=parent, + kind=kind, + resource=resource, + instrumentation_info=instrumentation_info, + instrumentation_scope=instrumentation_scope, + ) + self._sampler = sampler + self._trace_config = trace_config + self._record_exception = record_exception + self._set_status_on_exception = set_status_on_exception + self._span_processor = span_processor + self._limits = limits + self._lock = threading.Lock() + self._attributes = BoundedAttributes( + self._limits.max_span_attributes, + attributes, + immutable=False, + max_value_len=self._limits.max_span_attribute_length, + ) + self._events = self._new_events() + if events: + for event in events: + event._attributes = BoundedAttributes( + self._limits.max_event_attributes, + event.attributes, + max_value_len=self._limits.max_attribute_length, + ) + self._events.append(event) + + if links is None: + self._links = self._new_links() + else: + for link in links: + link._attributes = BoundedAttributes( + self._limits.max_link_attributes, + link.attributes, + max_value_len=self._limits.max_attribute_length, + ) + self._links = BoundedList.from_seq(self._limits.max_links, links) + + def __repr__(self): + return f'{type(self).__name__}(name="{self._name}", context={self._context})' + + def _new_events(self): + return BoundedList(self._limits.max_events) + + def _new_links(self): + return BoundedList(self._limits.max_links) + + def get_span_context(self): + return self._context + + def set_attributes(self, attributes: Dict[str, types.AttributeValue]) -> None: + with self._lock: + if self._end_time is not None: + logger.warning("Setting attribute on ended span.") + return + + for key, value in attributes.items(): + self._attributes[key] = value + + def set_attribute(self, key: str, value: types.AttributeValue) -> None: + return self.set_attributes({key: value}) + + @_check_span_ended + def _add_event(self, event: EventBase) -> None: + self._events.append(event) + + def add_event( + self, + name: str, + attributes: types.Attributes = None, + timestamp: Optional[int] = None, + ) -> None: + attributes = BoundedAttributes( + self._limits.max_event_attributes, + attributes, + max_value_len=self._limits.max_attribute_length, + ) + self._add_event( + Event( + name=name, + attributes=attributes, + timestamp=timestamp, + ) + ) + + def _readable_span(self) -> ReadableSpan: + return ReadableSpan( + name=self._name, + context=self._context, + parent=self._parent, + resource=self._resource, + attributes=self._attributes, + events=self._events, + links=self._links, + kind=self.kind, + status=self._status, + start_time=self._start_time, + end_time=self._end_time, + instrumentation_info=self._instrumentation_info, + instrumentation_scope=self._instrumentation_scope, + ) + + def start( + self, + start_time: Optional[int] = None, + parent_context: Optional[context_api.Context] = None, + ) -> None: + with self._lock: + if self._start_time is not None: + logger.warning("Calling start() on a started span.") + return + self._start_time = start_time if start_time is not None else time_ns() + + self._span_processor.on_start(self, parent_context=parent_context) + + def end(self, end_time: Optional[int] = None) -> None: + with self._lock: + if self._start_time is None: + raise RuntimeError("Calling end() on a not started span.") + if self._end_time is not None: + logger.warning("Calling end() on an ended span.") + return + + self._end_time = end_time if end_time is not None else time_ns() + + self._span_processor.on_end(self._readable_span()) + + @_check_span_ended + def update_name(self, name: str) -> None: + self._name = name + + def is_recording(self) -> bool: + return self._end_time is None + + @_check_span_ended + def set_status( + self, + status: typing.Union[Status, StatusCode], + description: typing.Optional[str] = None, + ) -> None: + # Ignore future calls if status is already set to OK + # Ignore calls to set to StatusCode.UNSET + if isinstance(status, Status): + if ( + self._status + and self._status.status_code is StatusCode.OK + or status.status_code is StatusCode.UNSET + ): + return + if description is not None: + logger.warning( + "Description %s ignored. Use either `Status` or `(StatusCode, Description)`", + description, + ) + self._status = status + elif isinstance(status, StatusCode): + if ( + self._status + and self._status.status_code is StatusCode.OK + or status is StatusCode.UNSET + ): + return + self._status = Status(status, description) + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + """Ends context manager and calls `end` on the `Span`.""" + if exc_val is not None and self.is_recording(): + # Record the exception as an event + # pylint:disable=protected-access + if self._record_exception: + self.record_exception(exception=exc_val, escaped=True) + # Records status if span is used as context manager + # i.e. with tracer.start_span() as span: + if self._set_status_on_exception: + self.set_status( + Status( + status_code=StatusCode.ERROR, + description=f"{exc_type.__name__}: {exc_val}", + ) + ) + + super().__exit__(exc_type, exc_val, exc_tb) + + def record_exception( + self, + exception: Exception, + attributes: types.Attributes = None, + timestamp: Optional[int] = None, + escaped: bool = False, + ) -> None: + """Records an exception as a span event.""" + try: + stacktrace = traceback.format_exc() + except Exception: # pylint: disable=broad-except + # workaround for python 3.4, format_exc can raise + # an AttributeError if the __context__ on + # an exception is None + stacktrace = "Exception occurred on stacktrace formatting" + _attributes = { + "exception.type": exception.__class__.__name__, + "exception.message": str(exception), + "exception.stacktrace": stacktrace, + "exception.escaped": str(escaped), + } + if attributes: + _attributes.update(attributes) + self.add_event(name="exception", attributes=_attributes, timestamp=timestamp) + + +class _Span(Span): + """Protected implementation of `mysql.opentelemetry.trace.Span`. + + This constructor exists to prevent the instantiation of the `Span` class + by other mechanisms than through the `Tracer`. + """ + + +class Tracer(trace_api.Tracer): + """See `mysql.opentelemetry.trace.Tracer`.""" + + def __init__( + self, + sampler: sampling.Sampler, + resource: Resource, + span_processor: Union[ + SynchronousMultiSpanProcessor, ConcurrentMultiSpanProcessor + ], + id_generator: IdGenerator, + instrumentation_info: InstrumentationInfo, + span_limits: SpanLimits, + instrumentation_scope: InstrumentationScope, + ) -> None: + self.sampler = sampler + self.resource = resource + self.span_processor = span_processor + self.id_generator = id_generator + self.instrumentation_info = instrumentation_info + self._span_limits = span_limits + self._instrumentation_scope = instrumentation_scope + + @contextmanager + def start_as_current_span( + self, + name: str, + context: Optional[context_api.Context] = None, + kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL, + attributes: types.Attributes = None, + links: Sequence[trace_api.Link] = (), + start_time: Optional[int] = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + end_on_exit: bool = True, + ) -> Iterator[trace_api.Span]: + span = self.start_span( + name=name, + context=context, + kind=kind, + attributes=attributes, + links=links, + start_time=start_time, + record_exception=record_exception, + set_status_on_exception=set_status_on_exception, + ) + with trace_api.use_span( + span, + end_on_exit=end_on_exit, + record_exception=record_exception, + set_status_on_exception=set_status_on_exception, + ) as span_context: + yield span_context + + def start_span( # pylint: disable=too-many-locals + self, + name: str, + context: Optional[context_api.Context] = None, + kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL, + attributes: types.Attributes = None, + links: Sequence[trace_api.Link] = (), + start_time: Optional[int] = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + ) -> trace_api.Span: + parent_span_context = trace_api.get_current_span(context).get_span_context() + + if parent_span_context is not None and not isinstance( + parent_span_context, trace_api.SpanContext + ): + raise TypeError("parent_span_context must be a SpanContext or None.") + + # is_valid determines root span + if parent_span_context is None or not parent_span_context.is_valid: + parent_span_context = None + trace_id = self.id_generator.generate_trace_id() + else: + trace_id = parent_span_context.trace_id + + # The sampler decides whether to create a real or no-op span at the + # time of span creation. No-op spans do not record events, and are not + # exported. + # The sampler may also add attributes to the newly-created span, e.g. + # to include information about the sampling result. + # The sampler may also modify the parent span context's tracestate + sampling_result = self.sampler.should_sample( + context, trace_id, name, kind, attributes, links + ) + + trace_flags = ( + trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED) + if sampling_result.decision.is_sampled() + else trace_api.TraceFlags(trace_api.TraceFlags.DEFAULT) + ) + span_context = trace_api.SpanContext( + trace_id, + self.id_generator.generate_span_id(), + is_remote=False, + trace_flags=trace_flags, + trace_state=sampling_result.trace_state, + ) + + # Only record if is_recording() is true + if sampling_result.decision.is_recording(): + # pylint:disable=protected-access + span = _Span( + name=name, + context=span_context, + parent=parent_span_context, + sampler=self.sampler, + resource=self.resource, + attributes=sampling_result.attributes.copy(), + span_processor=self.span_processor, + kind=kind, + links=links, + instrumentation_info=self.instrumentation_info, + record_exception=record_exception, + set_status_on_exception=set_status_on_exception, + limits=self._span_limits, + instrumentation_scope=self._instrumentation_scope, + ) + span.start(start_time=start_time, parent_context=context) + else: + span = trace_api.NonRecordingSpan(context=span_context) + return span + + +class TracerProvider(trace_api.TracerProvider): + """See `mysql.opentelemetry.trace.TracerProvider`.""" + + def __init__( + self, + sampler: sampling.Sampler = None, + resource: Resource = Resource.create({}), + shutdown_on_exit: bool = True, + active_span_processor: Union[ + SynchronousMultiSpanProcessor, ConcurrentMultiSpanProcessor + ] = None, + id_generator: IdGenerator = None, + span_limits: SpanLimits = None, + ): + self._active_span_processor = ( + active_span_processor or SynchronousMultiSpanProcessor() + ) + if id_generator is None: + self.id_generator = RandomIdGenerator() + else: + self.id_generator = id_generator + self._resource = resource + if not sampler: + sampler = sampling._get_from_env_or_default() + self.sampler = sampler + self._span_limits = span_limits or SpanLimits() + self._atexit_handler = None + + if shutdown_on_exit: + self._atexit_handler = atexit.register(self.shutdown) + + @property + def resource(self) -> Resource: + return self._resource + + def get_tracer( + self, + instrumenting_module_name: str, + instrumenting_library_version: typing.Optional[str] = None, + schema_url: typing.Optional[str] = None, + ) -> "trace_api.Tracer": + if not instrumenting_module_name: # Reject empty strings too. + instrumenting_module_name = "" + logger.error("get_tracer called with missing module name.") + if instrumenting_library_version is None: + instrumenting_library_version = "" + + filterwarnings( + "ignore", + message=( + r"Call to deprecated method __init__. \(You should use " + r"InstrumentationScope\) -- Deprecated since version 1.11.1." + ), + category=DeprecationWarning, + module="mysql.opentelemetry.sdk.trace", + ) + + instrumentation_info = InstrumentationInfo( + instrumenting_module_name, + instrumenting_library_version, + schema_url, + ) + + return Tracer( + self.sampler, + self.resource, + self._active_span_processor, + self.id_generator, + instrumentation_info, + self._span_limits, + InstrumentationScope( + instrumenting_module_name, + instrumenting_library_version, + schema_url, + ), + ) + + def add_span_processor(self, span_processor: SpanProcessor) -> None: + """Registers a new :class:`SpanProcessor` for this `TracerProvider`. + + The span processors are invoked in the same order they are registered. + """ + + # no lock here because add_span_processor is thread safe for both + # SynchronousMultiSpanProcessor and ConcurrentMultiSpanProcessor. + self._active_span_processor.add_span_processor(span_processor) + + def shutdown(self): + """Shut down the span processors added to the tracer provider.""" + self._active_span_processor.shutdown() + if self._atexit_handler is not None: + atexit.unregister(self._atexit_handler) + self._atexit_handler = None + + def force_flush(self, timeout_millis: int = 30000) -> bool: + """Requests the active span processor to process all spans that have not + yet been processed. + + By default force flush is called sequentially on all added span + processors. This means that span processors further back in the list + have less time to flush their spans. + To have span processors flush their spans in parallel it is possible to + initialize the tracer provider with an instance of + `ConcurrentMultiSpanProcessor` at the cost of using multiple threads. + + Args: + timeout_millis: The maximum amount of time to wait for spans to be + processed. + + Returns: + False if the timeout is exceeded, True otherwise. + """ + return self._active_span_processor.force_flush(timeout_millis) diff --git a/virt/lib/python3.9/site-packages/mysql/opentelemetry/sdk/trace/export/__init__ 3.py b/virt/lib/python3.9/site-packages/mysql/opentelemetry/sdk/trace/export/__init__ 3.py new file mode 100644 index 00000000..e619d54b --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysql/opentelemetry/sdk/trace/export/__init__ 3.py @@ -0,0 +1,506 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import collections +import logging +import os +import sys +import threading +import typing + +from enum import Enum +from os import environ, linesep +from time import time_ns +from typing import Optional + +from mysql.opentelemetry.context import ( + _SUPPRESS_INSTRUMENTATION_KEY, + Context, + attach, + detach, + set_value, +) +from mysql.opentelemetry.sdk.environment_variables import ( + OTEL_BSP_EXPORT_TIMEOUT, + OTEL_BSP_MAX_EXPORT_BATCH_SIZE, + OTEL_BSP_MAX_QUEUE_SIZE, + OTEL_BSP_SCHEDULE_DELAY, +) +from mysql.opentelemetry.sdk.trace import ReadableSpan, Span, SpanProcessor +from mysql.opentelemetry.util._once import Once + +_DEFAULT_SCHEDULE_DELAY_MILLIS = 5000 +_DEFAULT_MAX_EXPORT_BATCH_SIZE = 512 +_DEFAULT_EXPORT_TIMEOUT_MILLIS = 30000 +_DEFAULT_MAX_QUEUE_SIZE = 2048 +_ENV_VAR_INT_VALUE_ERROR_MESSAGE = ( + "Unable to parse value for %s as integer. Defaulting to %s." +) + +logger = logging.getLogger(__name__) + + +class SpanExportResult(Enum): + SUCCESS = 0 + FAILURE = 1 + + +class SpanExporter: + """Interface for exporting spans. + + Interface to be implemented by services that want to export spans recorded + in their own format. + + To export data this MUST be registered to the :class`mysql.opentelemetry.sdk.trace.Tracer` using a + `SimpleSpanProcessor` or a `BatchSpanProcessor`. + """ + + def export(self, spans: typing.Sequence[ReadableSpan]) -> "SpanExportResult": + """Exports a batch of telemetry data. + + Args: + spans: The list of `mysql.opentelemetry.trace.Span` objects to be exported + + Returns: + The result of the export + """ + + def shutdown(self) -> None: + """Shuts down the exporter. + + Called when the SDK is shut down. + """ + + def force_flush(self, timeout_millis: int = 30000) -> bool: + """Hint to ensure that the export of any spans the exporter has received + prior to the call to ForceFlush SHOULD be completed as soon as possible, preferably + before returning from this method. + """ + + +class SimpleSpanProcessor(SpanProcessor): + """Simple SpanProcessor implementation. + + SimpleSpanProcessor is an implementation of `SpanProcessor` that + passes ended spans directly to the configured `SpanExporter`. + """ + + def __init__(self, span_exporter: SpanExporter): + self.span_exporter = span_exporter + + def on_start( + self, span: Span, parent_context: typing.Optional[Context] = None + ) -> None: + pass + + def on_end(self, span: ReadableSpan) -> None: + if not span.context.trace_flags.sampled: + return + token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True)) + try: + self.span_exporter.export((span,)) + # pylint: disable=broad-except + except Exception: + logger.exception("Exception while exporting Span.") + detach(token) + + def shutdown(self) -> None: + self.span_exporter.shutdown() + + def force_flush(self, timeout_millis: int = 30000) -> bool: + # pylint: disable=unused-argument + return True + + +class _FlushRequest: + """Represents a request for the BatchSpanProcessor to flush spans.""" + + __slots__ = ["event", "num_spans"] + + def __init__(self): + self.event = threading.Event() + self.num_spans = 0 + + +_BSP_RESET_ONCE = Once() + + +class BatchSpanProcessor(SpanProcessor): + """Batch span processor implementation. + + `BatchSpanProcessor` is an implementation of `SpanProcessor` that + batches ended spans and pushes them to the configured `SpanExporter`. + + `BatchSpanProcessor` is configurable with the following environment + variables which correspond to constructor parameters: + + - :envvar:`OTEL_BSP_SCHEDULE_DELAY` + - :envvar:`OTEL_BSP_MAX_QUEUE_SIZE` + - :envvar:`OTEL_BSP_MAX_EXPORT_BATCH_SIZE` + - :envvar:`OTEL_BSP_EXPORT_TIMEOUT` + """ + + def __init__( + self, + span_exporter: SpanExporter, + max_queue_size: int = None, + schedule_delay_millis: float = None, + max_export_batch_size: int = None, + export_timeout_millis: float = None, + ): + if max_queue_size is None: + max_queue_size = BatchSpanProcessor._default_max_queue_size() + + if schedule_delay_millis is None: + schedule_delay_millis = BatchSpanProcessor._default_schedule_delay_millis() + + if max_export_batch_size is None: + max_export_batch_size = BatchSpanProcessor._default_max_export_batch_size() + + if export_timeout_millis is None: + export_timeout_millis = BatchSpanProcessor._default_export_timeout_millis() + + BatchSpanProcessor._validate_arguments( + max_queue_size, schedule_delay_millis, max_export_batch_size + ) + + self.span_exporter = span_exporter + self.queue = collections.deque([], max_queue_size) # type: typing.Deque[Span] + self.worker_thread = threading.Thread( + name="OtelBatchSpanProcessor", target=self.worker, daemon=True + ) + self.condition = threading.Condition(threading.Lock()) + self._flush_request = None # type: typing.Optional[_FlushRequest] + self.schedule_delay_millis = schedule_delay_millis + self.max_export_batch_size = max_export_batch_size + self.max_queue_size = max_queue_size + self.export_timeout_millis = export_timeout_millis + self.done = False + # flag that indicates that spans are being dropped + self._spans_dropped = False + # precallocated list to send spans to exporter + self.spans_list = [ + None + ] * self.max_export_batch_size # type: typing.List[typing.Optional[Span]] + self.worker_thread.start() + # Only available in *nix since py37. + if hasattr(os, "register_at_fork"): + os.register_at_fork( + after_in_child=self._at_fork_reinit + ) # pylint: disable=protected-access + self._pid = os.getpid() + + def on_start( + self, span: Span, parent_context: typing.Optional[Context] = None + ) -> None: + pass + + def on_end(self, span: ReadableSpan) -> None: + if self.done: + logger.warning("Already shutdown, dropping span.") + return + if not span.context.trace_flags.sampled: + return + if self._pid != os.getpid(): + _BSP_RESET_ONCE.do_once(self._at_fork_reinit) + + if len(self.queue) == self.max_queue_size: + if not self._spans_dropped: + logger.warning("Queue is full, likely spans will be dropped.") + self._spans_dropped = True + + self.queue.appendleft(span) + + if len(self.queue) >= self.max_export_batch_size: + with self.condition: + self.condition.notify() + + def _at_fork_reinit(self): + self.condition = threading.Condition(threading.Lock()) + self.queue.clear() + + # worker_thread is local to a process, only the thread that issued fork continues + # to exist. A new worker thread must be started in child process. + self.worker_thread = threading.Thread( + name="OtelBatchSpanProcessor", target=self.worker, daemon=True + ) + self.worker_thread.start() + self._pid = os.getpid() + + def worker(self): + timeout = self.schedule_delay_millis / 1e3 + flush_request = None # type: typing.Optional[_FlushRequest] + while not self.done: + with self.condition: + if self.done: + # done flag may have changed, avoid waiting + break + flush_request = self._get_and_unset_flush_request() + if ( + len(self.queue) < self.max_export_batch_size + and flush_request is None + ): + self.condition.wait(timeout) + flush_request = self._get_and_unset_flush_request() + if not self.queue: + # spurious notification, let's wait again, reset timeout + timeout = self.schedule_delay_millis / 1e3 + self._notify_flush_request_finished(flush_request) + flush_request = None + continue + if self.done: + # missing spans will be sent when calling flush + break + + # subtract the duration of this export call to the next timeout + start = time_ns() + self._export(flush_request) + end = time_ns() + duration = (end - start) / 1e9 + timeout = self.schedule_delay_millis / 1e3 - duration + + self._notify_flush_request_finished(flush_request) + flush_request = None + + # there might have been a new flush request while export was running + # and before the done flag switched to true + with self.condition: + shutdown_flush_request = self._get_and_unset_flush_request() + + # be sure that all spans are sent + self._drain_queue() + self._notify_flush_request_finished(flush_request) + self._notify_flush_request_finished(shutdown_flush_request) + + def _get_and_unset_flush_request( + self, + ) -> typing.Optional[_FlushRequest]: + """Returns the current flush request and makes it invisible to the + worker thread for subsequent calls. + """ + flush_request = self._flush_request + self._flush_request = None + if flush_request is not None: + flush_request.num_spans = len(self.queue) + return flush_request + + @staticmethod + def _notify_flush_request_finished( + flush_request: typing.Optional[_FlushRequest], + ): + """Notifies the flush initiator(s) waiting on the given request/event + that the flush operation was finished. + """ + if flush_request is not None: + flush_request.event.set() + + def _get_or_create_flush_request(self) -> _FlushRequest: + """Either returns the current active flush event or creates a new one. + + The flush event will be visible and read by the worker thread before an + export operation starts. Callers of a flush operation may wait on the + returned event to be notified when the flush/export operation was + finished. + + This method is not thread-safe, i.e. callers need to take care about + synchronization/locking. + """ + if self._flush_request is None: + self._flush_request = _FlushRequest() + return self._flush_request + + def _export(self, flush_request: typing.Optional[_FlushRequest]): + """Exports spans considering the given flush_request. + + In case of a given flush_requests spans are exported in batches until + the number of exported spans reached or exceeded the number of spans in + the flush request. + In no flush_request was given at most max_export_batch_size spans are + exported. + """ + if not flush_request: + self._export_batch() + return + + num_spans = flush_request.num_spans + while self.queue: + num_exported = self._export_batch() + num_spans -= num_exported + + if num_spans <= 0: + break + + def _export_batch(self) -> int: + """Exports at most max_export_batch_size spans and returns the number of + exported spans. + """ + idx = 0 + # currently only a single thread acts as consumer, so queue.pop() will + # not raise an exception + while idx < self.max_export_batch_size and self.queue: + self.spans_list[idx] = self.queue.pop() + idx += 1 + token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True)) + try: + # Ignore type b/c the Optional[None]+slicing is too "clever" + # for mypy + self.span_exporter.export(self.spans_list[:idx]) # type: ignore + except Exception: # pylint: disable=broad-except + logger.exception("Exception while exporting Span batch.") + detach(token) + + # clean up list + for index in range(idx): + self.spans_list[index] = None + return idx + + def _drain_queue(self): + """Export all elements until queue is empty. + + Can only be called from the worker thread context because it invokes + `export` that is not thread safe. + """ + while self.queue: + self._export_batch() + + def force_flush(self, timeout_millis: int = None) -> bool: + if timeout_millis is None: + timeout_millis = self.export_timeout_millis + + if self.done: + logger.warning("Already shutdown, ignoring call to force_flush().") + return True + + with self.condition: + flush_request = self._get_or_create_flush_request() + # signal the worker thread to flush and wait for it to finish + self.condition.notify_all() + + # wait for token to be processed + ret = flush_request.event.wait(timeout_millis / 1e3) + if not ret: + logger.warning("Timeout was exceeded in force_flush().") + return ret + + def shutdown(self) -> None: + # signal the worker thread to finish and then wait for it + self.done = True + with self.condition: + self.condition.notify_all() + self.worker_thread.join() + self.span_exporter.shutdown() + + @staticmethod + def _default_max_queue_size(): + try: + return int(environ.get(OTEL_BSP_MAX_QUEUE_SIZE, _DEFAULT_MAX_QUEUE_SIZE)) + except ValueError: + logger.exception( + _ENV_VAR_INT_VALUE_ERROR_MESSAGE, + OTEL_BSP_MAX_QUEUE_SIZE, + _DEFAULT_MAX_QUEUE_SIZE, + ) + return _DEFAULT_MAX_QUEUE_SIZE + + @staticmethod + def _default_schedule_delay_millis(): + try: + return int( + environ.get(OTEL_BSP_SCHEDULE_DELAY, _DEFAULT_SCHEDULE_DELAY_MILLIS) + ) + except ValueError: + logger.exception( + _ENV_VAR_INT_VALUE_ERROR_MESSAGE, + OTEL_BSP_SCHEDULE_DELAY, + _DEFAULT_SCHEDULE_DELAY_MILLIS, + ) + return _DEFAULT_SCHEDULE_DELAY_MILLIS + + @staticmethod + def _default_max_export_batch_size(): + try: + return int( + environ.get( + OTEL_BSP_MAX_EXPORT_BATCH_SIZE, + _DEFAULT_MAX_EXPORT_BATCH_SIZE, + ) + ) + except ValueError: + logger.exception( + _ENV_VAR_INT_VALUE_ERROR_MESSAGE, + OTEL_BSP_MAX_EXPORT_BATCH_SIZE, + _DEFAULT_MAX_EXPORT_BATCH_SIZE, + ) + return _DEFAULT_MAX_EXPORT_BATCH_SIZE + + @staticmethod + def _default_export_timeout_millis(): + try: + return int( + environ.get(OTEL_BSP_EXPORT_TIMEOUT, _DEFAULT_EXPORT_TIMEOUT_MILLIS) + ) + except ValueError: + logger.exception( + _ENV_VAR_INT_VALUE_ERROR_MESSAGE, + OTEL_BSP_EXPORT_TIMEOUT, + _DEFAULT_EXPORT_TIMEOUT_MILLIS, + ) + return _DEFAULT_EXPORT_TIMEOUT_MILLIS + + @staticmethod + def _validate_arguments( + max_queue_size, schedule_delay_millis, max_export_batch_size + ): + if max_queue_size <= 0: + raise ValueError("max_queue_size must be a positive integer.") + + if schedule_delay_millis <= 0: + raise ValueError("schedule_delay_millis must be positive.") + + if max_export_batch_size <= 0: + raise ValueError("max_export_batch_size must be a positive integer.") + + if max_export_batch_size > max_queue_size: + raise ValueError( + "max_export_batch_size must be less than or equal to max_queue_size." + ) + + +class ConsoleSpanExporter(SpanExporter): + """Implementation of :class:`SpanExporter` that prints spans to the + console. + + This class can be used for diagnostic purposes. It prints the exported + spans to the console STDOUT. + """ + + def __init__( + self, + service_name: Optional[str] = None, + out: typing.IO = sys.stdout, + formatter: typing.Callable[[ReadableSpan], str] = lambda span: span.to_json() + + linesep, + ): + self.out = out + self.formatter = formatter + self.service_name = service_name + + def export(self, spans: typing.Sequence[ReadableSpan]) -> SpanExportResult: + for span in spans: + self.out.write(self.formatter(span)) + self.out.flush() + return SpanExportResult.SUCCESS + + def force_flush(self, timeout_millis: int = 30000) -> bool: + return True diff --git a/virt/lib/python3.9/site-packages/mysql/opentelemetry/trace/__init__ 3.py b/virt/lib/python3.9/site-packages/mysql/opentelemetry/trace/__init__ 3.py new file mode 100644 index 00000000..f68c4048 --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysql/opentelemetry/trace/__init__ 3.py @@ -0,0 +1,623 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +The OpenTelemetry tracing API describes the classes used to generate +distributed traces. + +The :class:`.Tracer` class controls access to the execution context, and +manages span creation. Each operation in a trace is represented by a +:class:`.Span`, which records the start, end time, and metadata associated with +the operation. + +This module provides abstract (i.e. unimplemented) classes required for +tracing, and a concrete no-op :class:`.NonRecordingSpan` that allows applications +to use the API package alone without a supporting implementation. + +To get a tracer, you need to provide the package name from which you are +calling the tracer APIs to OpenTelemetry by calling `TracerProvider.get_tracer` +with the calling module name and the version of your package. + +The tracer supports creating spans that are "attached" or "detached" from the +context. New spans are "attached" to the context in that they are +created as children of the currently active span, and the newly-created span +can optionally become the new active span:: + + from opentelemetry import trace + + tracer = trace.get_tracer(__name__) + + # Create a new root span, set it as the current span in context + with tracer.start_as_current_span("parent"): + # Attach a new child and update the current span + with tracer.start_as_current_span("child"): + do_work(): + # Close child span, set parent as current + # Close parent span, set default span as current + +When creating a span that's "detached" from the context the active span doesn't +change, and the caller is responsible for managing the span's lifetime:: + + # Explicit parent span assignment is done via the Context + from mysql.opentelemetry.trace import set_span_in_context + + context = set_span_in_context(parent) + child = tracer.start_span("child", context=context) + + try: + do_work(span=child) + finally: + child.end() + +Applications should generally use a single global TracerProvider, and use +either implicit or explicit context propagation consistently throughout. + +.. versionadded:: 0.1.0 +.. versionchanged:: 0.3.0 + `TracerProvider` was introduced and the global ``tracer`` getter was + replaced by ``tracer_provider``. +.. versionchanged:: 0.5.0 + ``tracer_provider`` was replaced by `get_tracer_provider`, + ``set_preferred_tracer_provider_implementation`` was replaced by + `set_tracer_provider`. +""" + +import os +import typing + +from abc import ABC, abstractmethod +from contextlib import contextmanager +from enum import Enum +from logging import getLogger +from typing import Iterator, Optional, Sequence, cast + +from deprecated import deprecated +from mysql.opentelemetry import context as context_api +from mysql.opentelemetry.attributes import BoundedAttributes # type: ignore +from mysql.opentelemetry.context.context import Context +from mysql.opentelemetry.environment_variables import OTEL_PYTHON_TRACER_PROVIDER +from mysql.opentelemetry.trace.propagation import ( + _SPAN_KEY, + get_current_span, + set_span_in_context, +) +from mysql.opentelemetry.trace.span import ( + DEFAULT_TRACE_OPTIONS, + DEFAULT_TRACE_STATE, + INVALID_SPAN, + INVALID_SPAN_CONTEXT, + INVALID_SPAN_ID, + INVALID_TRACE_ID, + NonRecordingSpan, + Span, + SpanContext, + TraceFlags, + TraceState, + format_span_id, + format_trace_id, +) +from mysql.opentelemetry.trace.status import Status, StatusCode +from mysql.opentelemetry.util import types +from mysql.opentelemetry.util._once import Once +from mysql.opentelemetry.util._providers import _load_provider + +logger = getLogger(__name__) + + +class _LinkBase(ABC): + def __init__(self, context: "SpanContext") -> None: + self._context = context + + @property + def context(self) -> "SpanContext": + return self._context + + @property + @abstractmethod + def attributes(self) -> types.Attributes: + pass + + +class Link(_LinkBase): + """A link to a `Span`. The attributes of a Link are immutable. + + Args: + context: `SpanContext` of the `Span` to link to. + attributes: Link's attributes. + """ + + def __init__( + self, + context: "SpanContext", + attributes: types.Attributes = None, + ) -> None: + super().__init__(context) + self._attributes = BoundedAttributes( + attributes=attributes + ) # type: types.Attributes + + @property + def attributes(self) -> types.Attributes: + return self._attributes + + +_Links = Optional[Sequence[Link]] + + +class SpanKind(Enum): + """Specifies additional details on how this span relates to its parent span. + + Note that this enumeration is experimental and likely to change. See + https://github.com/open-telemetry/opentelemetry-specification/pull/226. + """ + + #: Default value. Indicates that the span is used internally in the + # application. + INTERNAL = 0 + + #: Indicates that the span describes an operation that handles a remote + # request. + SERVER = 1 + + #: Indicates that the span describes a request to some remote service. + CLIENT = 2 + + #: Indicates that the span describes a producer sending a message to a + #: broker. Unlike client and server, there is usually no direct critical + #: path latency relationship between producer and consumer spans. + PRODUCER = 3 + + #: Indicates that the span describes a consumer receiving a message from a + #: broker. Unlike client and server, there is usually no direct critical + #: path latency relationship between producer and consumer spans. + CONSUMER = 4 + + +class TracerProvider(ABC): + @abstractmethod + def get_tracer( + self, + instrumenting_module_name: str, + instrumenting_library_version: typing.Optional[str] = None, + schema_url: typing.Optional[str] = None, + ) -> "Tracer": + """Returns a `Tracer` for use by the given instrumentation library. + + For any two calls it is undefined whether the same or different + `Tracer` instances are returned, even for different library names. + + This function may return different `Tracer` types (e.g. a no-op tracer + vs. a functional tracer). + + Args: + instrumenting_module_name: The uniquely identifiable name for instrumentation + scope, such as instrumentation library, package, module or class name. + ``__name__`` may not be used as this can result in + different tracer names if the tracers are in different files. + It is better to use a fixed string that can be imported where + needed and used consistently as the name of the tracer. + + This should *not* be the name of the module that is + instrumented but the name of the module doing the instrumentation. + E.g., instead of ``"requests"``, use + ``"mysql.opentelemetry.instrumentation.requests"``. + + instrumenting_library_version: Optional. The version string of the + instrumenting library. Usually this should be the same as + ``importlib.metadata.version(instrumenting_library_name)``. + + schema_url: Optional. Specifies the Schema URL of the emitted telemetry. + """ + + +class NoOpTracerProvider(TracerProvider): + """The default TracerProvider, used when no implementation is available. + + All operations are no-op. + """ + + def get_tracer( + self, + instrumenting_module_name: str, + instrumenting_library_version: typing.Optional[str] = None, + schema_url: typing.Optional[str] = None, + ) -> "Tracer": + # pylint:disable=no-self-use,unused-argument + return NoOpTracer() + + +@deprecated(version="1.9.0", reason="You should use NoOpTracerProvider") # type: ignore +class _DefaultTracerProvider(NoOpTracerProvider): + """The default TracerProvider, used when no implementation is available. + + All operations are no-op. + """ + + +class ProxyTracerProvider(TracerProvider): + def get_tracer( + self, + instrumenting_module_name: str, + instrumenting_library_version: typing.Optional[str] = None, + schema_url: typing.Optional[str] = None, + ) -> "Tracer": + if _TRACER_PROVIDER: + return _TRACER_PROVIDER.get_tracer( + instrumenting_module_name, + instrumenting_library_version, + schema_url, + ) + return ProxyTracer( + instrumenting_module_name, + instrumenting_library_version, + schema_url, + ) + + +class Tracer(ABC): + """Handles span creation and in-process context propagation. + + This class provides methods for manipulating the context, creating spans, + and controlling spans' lifecycles. + """ + + @abstractmethod + def start_span( + self, + name: str, + context: Optional[Context] = None, + kind: SpanKind = SpanKind.INTERNAL, + attributes: types.Attributes = None, + links: _Links = None, + start_time: Optional[int] = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + ) -> "Span": + """Starts a span. + + Create a new span. Start the span without setting it as the current + span in the context. To start the span and use the context in a single + method, see :meth:`start_as_current_span`. + + By default the current span in the context will be used as parent, but an + explicit context can also be specified, by passing in a `Context` containing + a current `Span`. If there is no current span in the global `Context` or in + the specified context, the created span will be a root span. + + The span can be used as a context manager. On exiting the context manager, + the span's end() method will be called. + + Example:: + + # trace.get_current_span() will be used as the implicit parent. + # If none is found, the created span will be a root instance. + with tracer.start_span("one") as child: + child.add_event("child's event") + + Args: + name: The name of the span to be created. + context: An optional Context containing the span's parent. Defaults to the + global context. + kind: The span's kind (relationship to parent). Note that is + meaningful even if there is no parent. + attributes: The span's attributes. + links: Links span to other spans + start_time: Sets the start time of a span + record_exception: Whether to record any exceptions raised within the + context as error event on the span. + set_status_on_exception: Only relevant if the returned span is used + in a with/context manager. Defines whether the span status will + be automatically set to ERROR when an uncaught exception is + raised in the span with block. The span status won't be set by + this mechanism if it was previously set manually. + + Returns: + The newly-created span. + """ + + @contextmanager + @abstractmethod + def start_as_current_span( + self, + name: str, + context: Optional[Context] = None, + kind: SpanKind = SpanKind.INTERNAL, + attributes: types.Attributes = None, + links: _Links = None, + start_time: Optional[int] = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + end_on_exit: bool = True, + ) -> Iterator["Span"]: + """Context manager for creating a new span and set it + as the current span in this tracer's context. + + Exiting the context manager will call the span's end method, + as well as return the current span to its previous value by + returning to the previous context. + + Example:: + + with tracer.start_as_current_span("one") as parent: + parent.add_event("parent's event") + with tracer.start_as_current_span("two") as child: + child.add_event("child's event") + trace.get_current_span() # returns child + trace.get_current_span() # returns parent + trace.get_current_span() # returns previously active span + + This is a convenience method for creating spans attached to the + tracer's context. Applications that need more control over the span + lifetime should use :meth:`start_span` instead. For example:: + + with tracer.start_as_current_span(name) as span: + do_work() + + is equivalent to:: + + span = tracer.start_span(name) + with mysql.opentelemetry.trace.use_span(span, end_on_exit=True): + do_work() + + This can also be used as a decorator:: + + @tracer.start_as_current_span("name") + def function(): + ... + + function() + + Args: + name: The name of the span to be created. + context: An optional Context containing the span's parent. Defaults to the + global context. + kind: The span's kind (relationship to parent). Note that is + meaningful even if there is no parent. + attributes: The span's attributes. + links: Links span to other spans + start_time: Sets the start time of a span + record_exception: Whether to record any exceptions raised within the + context as error event on the span. + set_status_on_exception: Only relevant if the returned span is used + in a with/context manager. Defines whether the span status will + be automatically set to ERROR when an uncaught exception is + raised in the span with block. The span status won't be set by + this mechanism if it was previously set manually. + end_on_exit: Whether to end the span automatically when leaving the + context manager. + + Yields: + The newly-created span. + """ + + +class ProxyTracer(Tracer): + # pylint: disable=W0222,signature-differs + def __init__( + self, + instrumenting_module_name: str, + instrumenting_library_version: typing.Optional[str] = None, + schema_url: typing.Optional[str] = None, + ): + self._instrumenting_module_name = instrumenting_module_name + self._instrumenting_library_version = instrumenting_library_version + self._schema_url = schema_url + self._real_tracer: Optional[Tracer] = None + self._noop_tracer = NoOpTracer() + + @property + def _tracer(self) -> Tracer: + if self._real_tracer: + return self._real_tracer + + if _TRACER_PROVIDER: + self._real_tracer = _TRACER_PROVIDER.get_tracer( + self._instrumenting_module_name, + self._instrumenting_library_version, + self._schema_url, + ) + return self._real_tracer + return self._noop_tracer + + def start_span(self, *args, **kwargs) -> Span: # type: ignore + return self._tracer.start_span(*args, **kwargs) # type: ignore + + @contextmanager # type: ignore + def start_as_current_span(self, *args, **kwargs) -> Iterator[Span]: # type: ignore + with self._tracer.start_as_current_span(*args, **kwargs) as span: # type: ignore + yield span + + +class NoOpTracer(Tracer): + """The default Tracer, used when no Tracer implementation is available. + + All operations are no-op. + """ + + def start_span( + self, + name: str, + context: Optional[Context] = None, + kind: SpanKind = SpanKind.INTERNAL, + attributes: types.Attributes = None, + links: _Links = None, + start_time: Optional[int] = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + ) -> "Span": + # pylint: disable=unused-argument,no-self-use + return INVALID_SPAN + + @contextmanager + def start_as_current_span( + self, + name: str, + context: Optional[Context] = None, + kind: SpanKind = SpanKind.INTERNAL, + attributes: types.Attributes = None, + links: _Links = None, + start_time: Optional[int] = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + end_on_exit: bool = True, + ) -> Iterator["Span"]: + # pylint: disable=unused-argument,no-self-use + yield INVALID_SPAN + + +@deprecated(version="1.9.0", reason="You should use NoOpTracer") # type: ignore +class _DefaultTracer(NoOpTracer): + """The default Tracer, used when no Tracer implementation is available. + + All operations are no-op. + """ + + +_TRACER_PROVIDER_SET_ONCE = Once() +_TRACER_PROVIDER: Optional[TracerProvider] = None +_PROXY_TRACER_PROVIDER = ProxyTracerProvider() + + +def get_tracer( + instrumenting_module_name: str, + instrumenting_library_version: typing.Optional[str] = None, + tracer_provider: Optional[TracerProvider] = None, + schema_url: typing.Optional[str] = None, +) -> "Tracer": + """Returns a `Tracer` for use by the given instrumentation library. + + This function is a convenience wrapper for + mysql.opentelemetry.trace.TracerProvider.get_tracer. + + If tracer_provider is omitted the current configured one is used. + """ + if tracer_provider is None: + tracer_provider = get_tracer_provider() + return tracer_provider.get_tracer( + instrumenting_module_name, instrumenting_library_version, schema_url + ) + + +def _set_tracer_provider(tracer_provider: TracerProvider, log: bool) -> None: + def set_tp() -> None: + global _TRACER_PROVIDER # pylint: disable=global-statement + _TRACER_PROVIDER = tracer_provider + + did_set = _TRACER_PROVIDER_SET_ONCE.do_once(set_tp) + + if log and not did_set: + logger.warning("Overriding of current TracerProvider is not allowed") + + +def set_tracer_provider(tracer_provider: TracerProvider) -> None: + """Sets the current global :class:`~.TracerProvider` object. + + This can only be done once, a warning will be logged if any further attempt + is made. + """ + _set_tracer_provider(tracer_provider, log=True) + + +def get_tracer_provider() -> TracerProvider: + """Gets the current global :class:`~.TracerProvider` object.""" + if _TRACER_PROVIDER is None: + # if a global tracer provider has not been set either via code or env + # vars, return a proxy tracer provider + if OTEL_PYTHON_TRACER_PROVIDER not in os.environ: + return _PROXY_TRACER_PROVIDER + + tracer_provider: TracerProvider = _load_provider( + OTEL_PYTHON_TRACER_PROVIDER, "tracer_provider" + ) + _set_tracer_provider(tracer_provider, log=False) + # _TRACER_PROVIDER will have been set by one thread + return cast("TracerProvider", _TRACER_PROVIDER) + + +@contextmanager +def use_span( + span: Span, + end_on_exit: bool = False, + record_exception: bool = True, + set_status_on_exception: bool = True, +) -> Iterator[Span]: + """Takes a non-active span and activates it in the current context. + + Args: + span: The span that should be activated in the current context. + end_on_exit: Whether to end the span automatically when leaving the + context manager scope. + record_exception: Whether to record any exceptions raised within the + context as error event on the span. + set_status_on_exception: Only relevant if the returned span is used + in a with/context manager. Defines whether the span status will + be automatically set to ERROR when an uncaught exception is + raised in the span with block. The span status won't be set by + this mechanism if it was previously set manually. + """ + try: + token = context_api.attach(context_api.set_value(_SPAN_KEY, span)) + try: + yield span + finally: + context_api.detach(token) + + except Exception as exc: # pylint: disable=broad-except + if isinstance(span, Span) and span.is_recording(): + # Record the exception as an event + if record_exception: + span.record_exception(exc) + + # Set status in case exception was raised + if set_status_on_exception: + span.set_status( + Status( + status_code=StatusCode.ERROR, + description=f"{type(exc).__name__}: {exc}", + ) + ) + raise + + finally: + if end_on_exit: + span.end() + + +__all__ = [ + "DEFAULT_TRACE_OPTIONS", + "DEFAULT_TRACE_STATE", + "INVALID_SPAN", + "INVALID_SPAN_CONTEXT", + "INVALID_SPAN_ID", + "INVALID_TRACE_ID", + "NonRecordingSpan", + "Link", + "Span", + "SpanContext", + "SpanKind", + "TraceFlags", + "TraceState", + "TracerProvider", + "Tracer", + "format_span_id", + "format_trace_id", + "get_current_span", + "get_tracer", + "get_tracer_provider", + "set_tracer_provider", + "set_span_in_context", + "use_span", + "Status", + "StatusCode", +] diff --git a/virt/lib/python3.9/site-packages/mysql_connector_python-8.1.0.dist-info/LICENSE 3.txt b/virt/lib/python3.9/site-packages/mysql_connector_python-8.1.0.dist-info/LICENSE 3.txt new file mode 100644 index 00000000..ae53e779 --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysql_connector_python-8.1.0.dist-info/LICENSE 3.txt @@ -0,0 +1,4054 @@ +Licensing Information User Manual + +MySQL Connector/Python 8.1 (and later) + __________________________________________________________________ + +Introduction + + This License Information User Manual contains Oracle's product license + and other licensing information, including licensing information for + third-party software which may be included in this distribution of + MySQL Connector/Python 8.1 (and later). + + Last updated: June 2023 + +Licensing Information + + This is a release of MySQL Connector/Python 8.1 (and later), brought to + you by the MySQL team at Oracle. This software is released under + version 2 of the GNU General Public License (GPLv2), as set forth + below, with the following additional permissions: + + This distribution of MySQL Connector/Python 8.1 (and later) is + distributed with certain software (including but not limited to + OpenSSL) that is licensed under separate terms, as designated in a + particular file or component or in the license documentation. Without + limiting your rights under the GPLv2, the authors of MySQL hereby grant + you an additional permission to link the program and your derivative + works with the separately licensed software that they have included + with the program. + + Without limiting the foregoing grant of rights under the GPLv2 and + additional permission as to separately licensed software, this + Connector is also subject to the Universal FOSS Exception, version 1.0, + a copy of which is reproduced below and can also be found along with + its FAQ at http://oss.oracle.com/licenses/universal-foss-exception. + + Copyright (c) 2012, 2023, Oracle and/or its affiliates. + +Election of GPLv2 + + For the avoidance of doubt, except that if any license choice other + than GPL or LGPL is available it will apply instead, Oracle elects to + use only the General Public License version 2 (GPLv2) at this time for + any software where a choice of GPL license versions is made available + with the language indicating that GPLv2 or any later version may be + used, or where a choice of which version of the GPL is applied is + otherwise unspecified. + +GNU General Public License Version 2.0, June 1991 + +The following applies to all products licensed under the GNU General +Public License, Version 2.0: You may not use the identified files +except in compliance with the GNU General Public License, Version +2.0 (the "License.") You may obtain a copy of the License at +http://www.gnu.org/licenses/gpl-2.0.txt. A copy of the license is +also reproduced below. Unless required by applicable law or agreed +to in writing, software distributed under the License is distributed +on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +either express or implied. See the License for the specific language +governing permissions and limitations under the License. + + + ====================================================================== + ====================================================================== + + +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Everyone is permitted to copy and distribute verbatim +copies of this license document, but changing it is not +allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, +and (2) offer you this license which gives you legal permission to +copy, distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, +we want its recipients to know that what they have is not the original, +so that any problems introduced by others will not reflect on the +original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software + interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as +a special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new +versions of the General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Program does not specify a +version number of this License, you may choose any version ever +published by the Free Software Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the +author to ask for permission. For software which is copyrighted by the +Free Software Foundation, write to the Free Software Foundation; we +sometimes make exceptions for this. Our decision will be guided by the +two goals of preserving the free status of all derivatives of our free +software and of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS +WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details + type 'show w'. This is free software, and you are welcome + to redistribute it under certain conditions; type 'show c' + for details. + +The hypothetical commands 'show w' and 'show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than 'show w' and +'show c'; they could even be mouse-clicks or menu items--whatever +suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program 'Gnomovision' (which makes passes at compilers) written + by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, +you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use +the GNU Lesser General Public License instead of this License. + + ====================================================================== + ====================================================================== + +The Universal FOSS Exception, Version 1.0 + + In addition to the rights set forth in the other license(s) included in + the distribution for this software, data, and/or documentation + (collectively the "Software", and such licenses collectively with this + additional permission the "Software License"), the copyright holders + wish to facilitate interoperability with other software, data, and/or + documentation distributed with complete corresponding source under a + license that is OSI-approved and/or categorized by the FSF as free + (collectively "Other FOSS"). We therefore hereby grant the following + additional permission with respect to the use and distribution of the + Software with Other FOSS, and the constants, function signatures, data + structures and other invocation methods used to run or interact with + each of them (as to each, such software's "Interfaces"): + + i. The Software's Interfaces may, to the extent permitted by the + license of the Other FOSS, be copied into, used and distributed in + the Other FOSS in order to enable interoperability, without + requiring a change to the license of the Other FOSS other than as + to any Interfaces of the Software embedded therein. The Software's + Interfaces remain at all times under the Software License, + including without limitation as used in the Other FOSS (which upon + any such use also then contains a portion of the Software under the + Software License). + + ii. The Other FOSS's Interfaces may, to the extent permitted by the + license of the Other FOSS, be copied into, used and distributed in + the Software in order to enable interoperability, without requiring + that such Interfaces be licensed under the terms of the Software + License or otherwise altering their original terms, if this does + not require any portion of the Software other than such Interfaces + to be licensed under the terms other than the Software License. + + iii. If only Interfaces and no other code is copied between the + Software and the Other FOSS in either direction, the use and/or + distribution of the Software with the Other FOSS shall not be + deemed to require that the Other FOSS be licensed under the license + of the Software, other than as to any Interfaces of the Software + copied into the Other FOSS. This includes, by way of example and + without limitation, statically or dynamically linking the Software + together with Other FOSS after enabling interoperability using the + Interfaces of one or both, and distributing the resulting + combination under different licenses for the respective portions + thereof. + + For avoidance of doubt, a license which is OSI-approved or + categorized by the FSF as free, includes, for the purpose of this + permission, such licenses with additional permissions, and any + license that has previously been so approved or categorized as + free, even if now deprecated or otherwise no longer recognized as + approved or free. Nothing in this additional permission grants any + right to distribute any portion of the Software on terms other than + those of the Software License or grants any additional permission + of any kind for use or distribution of the Software in conjunction + with software other than Other FOSS. + + ====================================================================== + ====================================================================== + +Licenses for Third-Party Components + + The following sections contain licensing information for libraries that + may be included with this product. We are thankful to all individuals + that have created these. Standard licenses referenced herein are + detailed in the Standard Licenses section. + +Cyrus SASL + + * Copyright (c) 1998-2003 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any other legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 +* tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +The following files + +./lib + saslint.h auxprop.c canonusr.c checkpw.c client.c common.c config.c + external.c saslutil.c server.c seterror.c dlopen.c +./plugins + scram.c gssapi.c +./common + plugin_common.c + +have a license header similar to the above with the following copyright: +/* + * Copyright (c) 1998-2016 Carnegie Mellon University. All rights reserved. + * + +./lib/md5.c includes the following license header: +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm +*/ + +/* Function names changed to avoid namespace collisions: Rob Siemborski */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. +*/ + + ====================================================================== + ====================================================================== + +DNSPython + +DNSPython License + +Copyright (C) Dnspython Contributors + +Permission to use, copy, modify, and/or distribute this software for +any purpose with or without fee is hereby granted, provided that the +above copyright notice and this permission notice appear in all +copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +Copyright (C) 2001-2017 Nominum, Inc. +Copyright (C) Google Inc. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose with or without fee is hereby granted, +provided that the above copyright notice and this permission notice +appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ====================================================================== + ====================================================================== + +Google Protocol Buffers + +Copyright 2008 Google 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 Google 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 +OWNER 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. + +Code generated by the Protocol Buffer compiler is owned by the owner +of the input file used when generating it. This code is not +standalone and requires a support library to be linked with it. This +support library is itself covered by the above license. + + ====================================================================== + ====================================================================== + +GSSAPI + +Copyright (c) 2014, The Python GSSAPI Team + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE +FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING +OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ====================================================================== + ====================================================================== + +Kerberos5 + +Kerberos5 + +Copyright (C) 1985-2019 by the Massachusetts Institute of Technology. + +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. + +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. + +Downloading of this software may constitute an export of cryptographic +software from the United States of America that is subject to the +United States Export Administration Regulations (EAR), 15 CFR 730-774. +Additional laws or regulations may apply. It is the responsibility of +the person or entity contemplating export to comply with all +applicable export laws and regulations, including obtaining any +required license from the U.S. government. + +The U.S. government prohibits export of encryption source code to +certain countries and individuals, including, but not limited to, the +countries of Cuba, Iran, North Korea, Sudan, Syria, and residents and +nationals of those countries. + +Documentation components of this software distribution are licensed +under a Creative Commons Attribution-ShareAlike 3.0 Unported License. +(http://creativecommons.org/licenses/by-sa/3.0/) + +Individual source code files are copyright MIT, Cygnus Support, +Novell, OpenVision Technologies, Oracle, Red Hat, Sun Microsystems, +FundsXpress, and others. + +Project Athena, Athena, Athena MUSE, Discuss, Hesiod, Kerberos, Moira, +and Zephyr are trademarks of the Massachusetts Institute of Technology +(MIT). No commercial use of these trademarks may be made without +prior written permission of MIT. + +"Commercial use" means use of a name in a product or other for-profit +manner. It does NOT prevent a commercial firm from referring to the +MIT trademarks in order to convey information (although in doing so, +recognition of their trademark status should be given). + +====================================================================== + +The following copyright and permission notice applies to the +OpenVision Kerberos Administration system located in "kadmin/create", +"kadmin/dbutil", "kadmin/passwd", "kadmin/server", "lib/kadm5", and +portions of "lib/rpc": + + Copyright, OpenVision Technologies, Inc., 1993-1996, All Rights + Reserved + + WARNING: Retrieving the OpenVision Kerberos Administration system + source code, as described below, indicates your acceptance of the + following terms. If you do not agree to the following terms, do + not retrieve the OpenVision Kerberos administration system. + + You may freely use and distribute the Source Code and Object Code + compiled from it, with or without modification, but this Source + Code is provided to you "AS IS" EXCLUSIVE OF ANY WARRANTY, + INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY OR + FITNESS FOR A PARTICULAR PURPOSE, OR ANY OTHER WARRANTY, WHETHER + EXPRESS OR IMPLIED. IN NO EVENT WILL OPENVISION HAVE ANY LIABILITY + FOR ANY LOST PROFITS, LOSS OF DATA OR COSTS OF PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES, OR FOR ANY SPECIAL, INDIRECT, OR + CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, INCLUDING, + WITHOUT LIMITATION, THOSE RESULTING FROM THE USE OF THE SOURCE + CODE, OR THE FAILURE OF THE SOURCE CODE TO PERFORM, OR FOR ANY + OTHER REASON. + + OpenVision retains all copyrights in the donated Source Code. + OpenVision also retains copyright to derivative works of the Source + Code, whether created by OpenVision or by a third party. The + OpenVision copyright notice must be preserved if derivative works + are made based on the donated Source Code. + + OpenVision Technologies, Inc. has donated this Kerberos + Administration system to MIT for inclusion in the standard Kerberos + 5 distribution. This donation underscores our commitment to + continuing Kerberos technology development and our gratitude for + the valuable work which has been performed by MIT and the Kerberos + community. + +====================================================================== + + Portions contributed by Matt Crawford "crawdad@fnal.gov" were work +performed at Fermi National Accelerator Laboratory, which is + operated by Universities Research Association, Inc., under contract + DE-AC02-76CHO3000 with the U.S. Department of Energy. + +====================================================================== + +Portions of "src/lib/crypto" have the following copyright: + + Copyright (C) 1998 by the FundsXpress, INC. + + All rights reserved. + + Export of this software from the United States of America may + require a specific license from the United States Government. + It is the responsibility of any person or organization + contemplating export to obtain such a license before exporting. + + WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + distribute this software and its documentation for any purpose and + without fee is hereby granted, provided that the above copyright + notice appear in all copies and that both that copyright notice and + this permission notice appear in supporting documentation, and that + the name of FundsXpress. not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. FundsXpress makes no representations + about the suitability of this software for any purpose. It is + provided "as is" without express or implied warranty. + + THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +====================================================================== + +The implementation of the AES encryption algorithm in +"src/lib/crypto/builtin/aes" has the following copyright: + + Copyright (C) 2001, Dr Brian Gladman "brg@gladman.uk.net", Worcester, UK. + All rights reserved. + + LICENSE TERMS + + The free distribution and use of this software in both source and + binary form is allowed (with or without changes) provided that: + + 1. distributions of this source code include the above copyright + notice, this list of conditions and the following disclaimer; + + 2. distributions in binary form include the above copyright notice, + this list of conditions and the following disclaimer in the + documentation and/or other associated materials; + + 3. the copyright holder's name is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explcit or implied + warranties in respect of any properties, including, but not limited + to, correctness and fitness for purpose. + +====================================================================== + +Portions contributed by Red Hat, including the pre-authentication +plug-in framework and the NSS crypto implementation, contain the +following copyright: + + Copyright (C) 2006 Red Hat, Inc. + Portions copyright (C) 2006 Massachusetts Institute of Technology + 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 Red Hat, 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 OWNER 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. + +====================================================================== + +The bundled verto source code is subject to the following license: + + Copyright 2011 Red Hat, Inc. + + 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. + +====================================================================== + +The MS-KKDCP client implementation has the following copyright: + + Copyright 2013,2014 Red Hat, Inc. + + 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. + + 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 OWNER 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. + +====================================================================== + +The implementations of GSSAPI mechglue in GSSAPI-SPNEGO in +"src/lib/gssapi", including the following files: + + lib/gssapi/generic/gssapi_err_generic.et + lib/gssapi/mechglue/g_accept_sec_context.c + lib/gssapi/mechglue/g_acquire_cred.c + lib/gssapi/mechglue/g_canon_name.c + lib/gssapi/mechglue/g_compare_name.c + lib/gssapi/mechglue/g_context_time.c + lib/gssapi/mechglue/g_delete_sec_context.c + lib/gssapi/mechglue/g_dsp_name.c + lib/gssapi/mechglue/g_dsp_status.c + lib/gssapi/mechglue/g_dup_name.c + lib/gssapi/mechglue/g_exp_sec_context.c + lib/gssapi/mechglue/g_export_name.c + lib/gssapi/mechglue/g_glue.c + lib/gssapi/mechglue/g_imp_name.c + lib/gssapi/mechglue/g_imp_sec_context.c + lib/gssapi/mechglue/g_init_sec_context.c + lib/gssapi/mechglue/g_initialize.c + lib/gssapi/mechglue/g_inquire_context.c + lib/gssapi/mechglue/g_inquire_cred.c + lib/gssapi/mechglue/g_inquire_names.c + lib/gssapi/mechglue/g_process_context.c + lib/gssapi/mechglue/g_rel_buffer.c + lib/gssapi/mechglue/g_rel_cred.c + lib/gssapi/mechglue/g_rel_name.c + lib/gssapi/mechglue/g_rel_oid_set.c + lib/gssapi/mechglue/g_seal.c + lib/gssapi/mechglue/g_sign.c + lib/gssapi/mechglue/g_store_cred.c + lib/gssapi/mechglue/g_unseal.c + lib/gssapi/mechglue/g_userok.c + lib/gssapi/mechglue/g_utils.c + lib/gssapi/mechglue/g_verify.c + lib/gssapi/mechglue/gssd_pname_to_uid.c + lib/gssapi/mechglue/mglueP.h + lib/gssapi/mechglue/oid_ops.c + lib/gssapi/spnego/gssapiP_spnego.h + lib/gssapi/spnego/spnego_mech.c + +and the initial implementation of incremental propagation, including +the following new or changed files: + + include/iprop_hdr.h + kadmin/server/ipropd_svc.c + lib/kdb/iprop.x + lib/kdb/kdb_convert.c + lib/kdb/kdb_log.c + lib/kdb/kdb_log.h + lib/krb5/error_tables/kdb5_err.et + slave/kpropd_rpc.c + slave/kproplog.c + +are subject to the following license: + + Copyright (C) 2004 Sun Microsystems, Inc. + + 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. + +====================================================================== + +Kerberos V5 includes documentation and software developed at the +University of California at Berkeley, which includes this copyright +notice: + + Copyright (C) 1983 Regents of the University of California. + 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 University 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 REGENTS 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 REGENTS + 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. + +====================================================================== + +Portions contributed by Novell, Inc., including the LDAP database +backend, are subject to the following license: + + Copyright (C) 2004-2005, Novell, 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. + + * The copyright holder's name is not 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 OWNER 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. + +====================================================================== + +Portions funded by Sandia National Laboratory and developed by the +University of Michigan's Center for Information Technology +Integration, including the PKINIT implementation, are subject to the +following license: + + COPYRIGHT (C) 2006-2007 + THE REGENTS OF THE UNIVERSITY OF MICHIGAN + ALL RIGHTS RESERVED + + Permission is granted to use, copy, create derivative works and + redistribute this software and such derivative works for any + purpose, so long as the name of The University of Michigan is not + used in any advertising or publicity pertaining to the use of + distribution of this software without specific, written prior + authorization. If the above copyright notice or any other + identification of the University of Michigan is included in any + copy of any portion of this software, then the disclaimer below + must also be included. + + THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE + UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND + WITHOUT WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER + EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE FOR + ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR + IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR + IS HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +====================================================================== + +The pkcs11.h file included in the PKINIT code has the following +license: + + Copyright 2006 g10 Code GmbH + Copyright 2006 Andreas Jellinghaus + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. + +====================================================================== + +Portions contributed by Apple Inc. are subject to the following +license: + + Copyright 2004-2008 Apple Inc. All Rights Reserved. + + Export of this software from the United States of America may + require a specific license from the United States Government. + It is the responsibility of any person or organization + contemplating export to obtain such a license before exporting. + + WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + distribute this software and its documentation for any purpose and + without fee is hereby granted, provided that the above copyright + notice appear in all copies and that both that copyright notice and + this permission notice appear in supporting documentation, and that + the name of Apple Inc. not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. Apple Inc. makes no representations + about the suitability of this software for any purpose. It is + provided "as is" without express or implied warranty. + + THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +====================================================================== + +The implementations of UTF-8 string handling in src/util/support and +src/lib/krb5/unicode are subject to the following copyright and +permission notice: + + The OpenLDAP Public License + Version 2.8, 17 August 2003 + + Redistribution and use of this software and associated + documentation ("Software"), with or without modification, are + permitted provided that the following conditions are met: + + 1. Redistributions in source form must retain copyright statements + and notices, + + 2. Redistributions in binary form must reproduce applicable + copyright statements and notices, this list of conditions, and + the following disclaimer in the documentation and/or other + materials provided with the distribution, and + + 3. Redistributions must contain a verbatim copy of this document. + + The OpenLDAP Foundation may revise this license from time to time. + Each revision is distinguished by a version number. You may use + this Software under terms of this license revision or under the + terms of any subsequent revision of the license. + + THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS + CONTRIBUTORS "AS IS" AND ANY EXPRESSED 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 OPENLDAP FOUNDATION, ITS + CONTRIBUTORS, OR THE AUTHOR(S) OR OWNER(S) OF THE SOFTWARE 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. + + The names of the authors and copyright holders must not be used in + advertising or otherwise to promote the sale, use or other dealing + in this Software without specific, written prior permission. Title + to copyright in this Software shall at all times remain with + copyright holders. + + OpenLDAP is a registered trademark of the OpenLDAP Foundation. + + Copyright 1999-2003 The OpenLDAP Foundation, Redwood City, + California, USA. All Rights Reserved. Permission to copy and + distribute verbatim copies of this document is granted. + +Marked test programs in src/lib/krb5/krb have the following copyright: + + + Copyright (C) 2006 Kungliga Tekniska Högskolan + (Royal Institute of Technology, Stockholm, Sweden). + 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 KTH 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 KTH AND ITS 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 KTH OR ITS + 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. + +====================================================================== + +The KCM Mach RPC definition file used on macOS has the following +copyright: + + Copyright (C) 2009 Kungliga Tekniska Högskolan + (Royal Institute of Technology, Stockholm, Sweden). + All rights reserved. + + Portions Copyright (C) 2009 Apple 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: + + 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 Institute 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 INSTITUTE 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 INSTITUTE + 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. + +====================================================================== + +Portions of the RPC implementation in src/lib/rpc and +src/include/gssrpc have the following copyright and permission notice: + + Copyright (C) 2010, Oracle America, 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: + + 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 "Oracle America, 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. + +====================================================================== + + Copyright (C) 2006,2007,2009 NTT (Nippon Telegraph and Telephone + Corporation). 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 as + the first lines of this file unmodified. + + 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. + + THIS SOFTWARE IS PROVIDED BY NTT "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 NTT 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. + +====================================================================== + + Copyright 2000 by Carnegie Mellon University + + All Rights Reserved + + Permission to use, copy, modify, and distribute this software and + its documentation for any purpose and without fee is hereby + granted, provided that the above copyright notice appear in all + copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + Carnegie Mellon University not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. + + CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + SOFTWARE. + +====================================================================== + + Copyright (C) 2002 Naval Research Laboratory (NRL/CCS) + + Permission to use, copy, modify and distribute this software and + its documentation is hereby granted, provided that both the + copyright notice and this permission notice appear in all copies of + the software, derivative works or modified versions, and any + portions thereof. + + NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND + DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER + RESULTING FROM THE USE OF THIS SOFTWARE. + +====================================================================== + +Portions extracted from Internet RFCs have the following copyright +notice: + + Copyright (C) The Internet Society (2006). + + This document is subject to the rights, licenses and restrictions + contained in BCP 78, and except as set forth therein, the authors + retain all their rights. + + This document and the information contained herein are provided on + an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE + REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND + THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT + THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR + ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A + PARTICULAR PURPOSE. + +====================================================================== + + Copyright (C) 1991, 1992, 1994 by Cygnus Support. + + Permission to use, copy, modify, and distribute this software and + its documentation for any purpose and without fee is hereby + granted, provided that the above copyright notice appear in all + copies and that both that copyright notice and this permission + notice appear in supporting documentation. Cygnus Support makes no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + +====================================================================== + + Copyright (C) 2006 Secure Endpoints Inc. + + 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. + +====================================================================== + +Portions of the implementation of the Fortuna-like PRNG are subject to +the following notice: + + + Copyright (C) 2005 Marko Kreen + 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. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + + Copyright (C) 1994 by the University of Southern California + + EXPORT OF THIS SOFTWARE from the United States of America may + require a specific license from the United States Government. It + is the responsibility of any person or organization + contemplating export to obtain such a license before exporting. + + WITHIN THAT CONSTRAINT, permission to copy, modify, and distribute + this software and its documentation in source and binary forms is + hereby granted, provided that any documentation or other materials + related to such distribution or use acknowledge that the software + was developed by the University of Southern California. + + DISCLAIMER OF WARRANTY. THIS SOFTWARE IS PROVIDED "AS IS". The + University of Southern California MAKES NO REPRESENTATIONS OR + WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not + limitation, the University of Southern California MAKES NO + REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY + PARTICULAR PURPOSE. The University of Southern California shall not + be held liable for any liability nor for any direct, indirect, or + consequential damages with respect to any claim by the user or + distributor of the ksu software. + +====================================================================== + + Copyright (C) 1995 + The President and Fellows of Harvard University + + This code is derived from software contributed to Harvard by Jeremy + Rassen. + + 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. All advertising materials mentioning features or use of this + software must display the following acknowledgement: + + This product includes software developed by the University of + California, Berkeley and its contributors. + + 4. Neither the name of the University 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 REGENTS 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 REGENTS + 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. + +====================================================================== + + Copyright (C) 2008 by the Massachusetts Institute of Technology. + Copyright 1995 by Richard P. Basch. All Rights Reserved. + Copyright 1995 by Lehman Brothers, Inc. All Rights Reserved. + + Export of this software from the United States of America may + require a specific license from the United States Government. It + is the responsibility of any person or organization + contemplating export to obtain such a license before exporting. + + WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + distribute this software and its documentation for any purpose and + without fee is hereby granted, provided that the above copyright + notice appear in all copies and that both that copyright notice and + this permission notice appear in supporting documentation, and that + the name of Richard P. Basch, Lehman Brothers and M.I.T. not be + used in advertising or publicity pertaining to distribution of the + software without specific, written prior permission. Richard P. + Basch, Lehman Brothers and M.I.T. make no representations about the + suitability of this software for any purpose. It is provided "as + is" without express or implied warranty. + +====================================================================== + +The following notice applies to "src/lib/krb5/krb/strptime.c" and +"src/include/k5-queue.h". + + Copyright (C) 1997, 1998 The NetBSD Foundation, Inc. + All rights reserved. + + This code was contributed to The NetBSD Foundation by Klaus Klein. + + 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. All advertising materials mentioning features or use of this + software must display the following acknowledgement: + + This product includes software developed by the NetBSD + Foundation, Inc. and its contributors. + + 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + +====================================================================== + +The following notice applies to Unicode library files in +"src/lib/krb5/unicode": + + Copyright 1997, 1998, 1999 Computing Research Labs, + New Mexico State University + + 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 COMPUTING RESEARCH LAB OR + NEW MEXICO STATE UNIVERSITY 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. + +====================================================================== + +The following notice applies to "src/util/support/strlcpy.c": + + Copyright (C) 1998 Todd C. Miller "Todd.Miller@courtesan.com" + + Permission to use, copy, modify, and distribute this software for + any purpose with or without fee is hereby granted, provided that + the above copyright notice and this permission notice appear in all + copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +====================================================================== + +The following notice applies to "src/util/profile/argv_parse.c" and +"src/util/profile/argv_parse.h": + + Copyright 1999 by Theodore Ts'o. + + Permission to use, copy, modify, and distribute this software for + any purpose with or without fee is hereby granted, provided that + the above copyright notice and this permission notice appear in all + copies. THE SOFTWARE IS PROVIDED "AS IS" AND THEODORE TS'O (THE + AUTHOR) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. (Isn't + it sick that the U.S. culture of lawsuit-happy lawyers requires + this kind of disclaimer?) + +====================================================================== + +The following notice applies to SWIG-generated code in +"src/util/profile/profile_tcl.c": + + Copyright (C) 1999-2000, The University of Chicago + + This file may be freely redistributed without license or fee + provided this copyright message remains intact. + +====================================================================== + +The following notice applies to portiions of "src/lib/rpc" and +"src/include/gssrpc": + + Copyright (C) 2000 The Regents of the University of Michigan. All + rights reserved. + + Copyright (C) 2000 Dug Song "dugsong@UMICH.EDU". All rights + reserved, all wrongs reversed. + + 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 University 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 "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 REGENTS 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. + +Implementations of the MD4 algorithm are subject to the following +notice: + + Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD4 Message Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD4 Message Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. + +====================================================================== + +Implementations of the MD5 algorithm are subject to the following +notice: + + Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD5 Message- Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD5 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. + +====================================================================== + +The following notice applies to +"src/lib/crypto/crypto_tests/t_mddriver.c": + + Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All + rights reserved. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" without + express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. + +====================================================================== + +Portions of "src/lib/krb5" are subject to the following notice: + + Copyright (C) 1994 CyberSAFE Corporation. + Copyright 1990,1991,2007,2008 by the Massachusetts Institute of +Technology. + All Rights Reserved. + + Export of this software from the United States of America may + require a specific license from the United States Government. It + is the responsibility of any person or organization + contemplating export to obtain such a license before exporting. + + WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + distribute this software and its documentation for any purpose and + without fee is hereby granted, provided that the above copyright + notice appear in all copies and that both that copyright notice and + this permission notice appear in supporting documentation, and that + the name of M.I.T. not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. Furthermore if you modify this software + you must label your software as modified software and not + distribute it in such a fashion that it might be confused with the + original M.I.T. software. Neither M.I.T., the Open Computing + Security Group, nor CyberSAFE Corporation make any representations + about the suitability of this software for any purpose. It is + provided "as is" without express or implied warranty. + +====================================================================== + +Portions contributed by PADL Software are subject to the following +license: + + Copyright (c) 2011, PADL Software Pty Ltd. 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 PADL Software 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 PADL SOFTWARE 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 PADL SOFTWARE + 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. + +====================================================================== + +The bundled libev source code is subject to the following license: + + All files in libev are Copyright (C)2007,2008,2009 Marc Alexander + Lehmann. + + 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. + + 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 OWNER 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. + + Alternatively, the contents of this package may be used under the + terms of the GNU General Public License ("GPL") version 2 or any + later version, in which case the provisions of the GPL are + applicable instead of the above. If you wish to allow the use of + your version of this package only under the terms of the GPL and + not to allow others to use your version of this file under the BSD + license, indicate your decision by deleting the provisions above + and replace them with the notice and other provisions required by + the GPL in this and the other files of this package. If you do not + delete the provisions above, a recipient may use your version of + this file under either the BSD or the GPL. + +====================================================================== + +Files copied from the Intel AESNI Sample Library are subject to the +following license: + + Copyright (C) 2010, Intel Corporation + 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 Intel Corporation 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 OWNER 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. + +====================================================================== + +The following notice applies to +"src/ccapi/common/win/OldCC/autolock.hxx": + + Copyright (C) 1998 by Danilo Almeida. 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. + + 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. + + ====================================================================== + ====================================================================== + +LibFIDO + +Copyright (c) 2018-2021 Yubico AB. 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. + +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. + +4th Party +========= + +libcbor +------- + +MIT License + +Copyright (c) 2014-2017 Pavel Kalvoda + +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. + +------------------------------------------------------------------------------ +---- +zlib +---- + +Copyright notice: + + (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + +If you use the zlib library in a product, we would appreciate *not* receiving +lengthy legal documents to sign. The sources are provided for free but +without warranty of any kind. The library has been entirely written by +Jean-loup Gailly and Mark Adler; it does not include third-party code. + +If you redistribute modified sources, we would appreciate that you include in +the file ChangeLog history information documenting your changes. Please read +the FAQ for more information on the distribution of modified source versions. + +------------------------------------------------------------------------------ +OpenSSL (See its own license section) +------------------------------------------------------------------------------ + + ====================================================================== + ====================================================================== + +OpenSSL 3.0 + +You may be receiving a copy of OpenSSL 3.0 as part of this product in +object code form. +The terms of the Oracle license do NOT apply to OpenSSL 3.0. +OpenSSL 3.0 is licensed under the Apache 2.0 license, separate from +the Oracle product. +If you do not wish to install this library, you may remove it, but +the Oracle program might not operate properly or at all without it. + +/* + * Copyright 2003-2022 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html +*/ + +See Apache License v2.0, January 2004 in the +'Standard Licenses' section. + + ====================================================================== + ====================================================================== + +opentelemetry-python + +You may be receiving a copy of the opentelemetry-python library with this +MySQL product. +The terms of the Oracle license do NOT apply to the opentelemetry-python +library; it is licensed under the following license, separately from the +Oracle programs you receive. +If you do not wish to install this program, you may delete its files but the +Oracle program might not operate properly or at all without it. + + Copyright The OpenTelemetry Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +See Apache License v2.0, January 2004 in the 'Standard Licenses' section. + +4th party dependencies +====================== + +Typing Extensions +----------------- + +A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see https://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations, which became +Zope Corporation. In 2001, the Python Software Foundation (PSF, see +https://www.python.org/psf/) was formed, a non-profit organization +created specifically to own Python-related Intellectual Property. +Zope Corporation was a sponsoring member of the PSF. + +All Python releases are Open Source (see https://opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2 and above 2.1.1 2001-now PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +Python software and documentation are licensed under the +Python Software Foundation License Version 2. + +Starting with Python 3.8.6, examples, recipes, and other code in +the documentation are dual licensed under the PSF License Version 2 +and the Zero-Clause BSD license. + +Some software incorporated into Python is under different licenses. +The licenses are listed with code falling under that license. + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF +hereby grants Licensee a nonexclusive, royalty-free, world-wide license +to reproduce, analyze, test, perform and/or display publicly, prepare +derivative works, distribute, and otherwise use Python alone or in any +derivative version, provided, however, that PSF's License Agreement and +PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, +2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, +2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; All +Rights Reserved" are retained in Python alone or in any derivative +version prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION +---------------------------------------------------------------------- + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +------------------------------------------------------------------------------ +-- + +Deprecated +---------- +The MIT License (MIT) + +Copyright (c) 2017 Laurent LAPORTE + +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. + +------------------------------------------------------------------------------ +-- + +Importlib Metadata +------------------ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +See Apache License v2.0, January 2004 in the 'Standard Licenses' section. + +------------------------------------------------------------------------------ +-- + +wrapt (used by Deprecated ) +----- + +Copyright (c) 2013-2023, Graham Dumpleton +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. + +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. + +------------------------------------------------------------------------------ + +zipp (used by Importlib Metadata) +---- + +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. + +============================================================================== + + ====================================================================== + ====================================================================== + +Oracle OCI Python SDK + +Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. +This software is dual-licensed to you under the Universal Permissive License +(UPL) 1.0 or Apache License 2.0. See below for license terms. You may +choose either license. +____________________________ +The Universal Permissive License (UPL), Version 1.0 +Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. +Subject to the condition set forth below, permission is hereby granted to any +person obtaining a copy of this software, associated documentation and/or +data (collectively the "Software"), free of charge and under any and all +copyright rights in the Software, and any and all patent rights owned or +freely licensable by each licensor hereunder covering either (i) the +unmodified Software as contributed to or provided by such licensor, or (ii) +the Larger Works (as defined below), to deal in both +(a) the Software, and +(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +one is included with the Software (each a "Larger Work" to which the Software +is contributed by such licensors), +without restriction, including without limitation the rights to copy, create +derivative works of, display, perform, and distribute the Software and make, +use, sell, offer for sale, import, export, have made, and have sold the +Software and the Larger Work(s), and to sublicense the foregoing rights on +either these or other terms. +This license is subject to the following condition: +The above copyright notice and either this complete permission notice or at a +minimum a reference to the UPL must 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. + +The Apache Software License, Version 2.0 +Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); You may not +use this product except in compliance with the License. You may obtain a +copy of the License at http://www.apache.org/licenses/LICENSE-2.0. A copy of +the license is also reproduced below. Unless required by applicable law or +agreed to in writing, software distributed under the License is distributed +on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing +permissions and limitations under the License. + +Apache License v2.0, January 2004 + +Oracle's use of OCI Python SDK in MySQL Community Edition is solely under the +UPL + + ====================================================================== + ====================================================================== + +Python FIDO + +This project, with the exception of the files mentioned below, is licensed +under the BSD 2-clause license. +See the _COPYING_ file for the full license text. + +This project contains source code from pyu2f +(https://github.com/google/pyu2f) +which is licensed under the Apache License, version 2.0. +These files are located in `fido2/hid/`. +See http://www.apache.org/licenses/LICENSE-2.0, +or the _COPYING.APLv2_ file for the full license text. + +This project also bundles the public suffix list (https://publicsuffix.org) +which is licensed under the Mozilla Public License, version 2.0. +This file is stored as `fido2/public_suffix_list.dat`. +See https://mozilla.org/MPL/2.0/, +or the _COPYING.MPLv2_ file for the full license text. + +COPYING +------- +Copyright (c) 2018 Yubico AB +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. + +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 +OWNER 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. + +pyu2f +----- +A copy of the Apache License v2.0, January 2004 license can be found +in the 'Standard Licenses' section. + +public suffix list +------------------ +A copy of the Mozilla Public License 2.0 license can be found +in the 'Standard Licenses' section. + +Additional 4th party dependencies +--------------------------------- + +pyscard-2.0.3 +============= +Copyright 2001-2012 gemalto +Author: Jean-Daniel Aussel, mailto:jean-daniel.aussel@gemalto.com + +Copyright 2007-2018 Ludovic Rousseau ludovic.rousseau@free.fr + +This file is part of pyscard. + +pyscard is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +pyscard is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with pyscard; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +A copy of the GNU Lesser General Public License v2.1, February 1999 license can +be found +in the 'Standard Licenses' section. + +cryptography-37.0.2 +=================== +LICENSE File +------------ +This software is made available under the terms of *either* of the licenses +found in LICENSE.APACHE or LICENSE.BSD. Contributions to cryptography are +made under the terms of *both* these licenses. + +The code used in the OS random engine is derived from CPython, and is +licensed under the terms of the PSF License Agreement. + +LICENSE.BSD +----------- +Copyright (c) Individual contributors. +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 PyCA Cryptography 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 OWNER +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. + +LICENSE.APACHE +-------------- +A copy of the Apache License v2.0, January 2004 license can be found +in the 'Standard Licenses' section. + +LICENSE.PSF +----------- +1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), + and the Individual or Organization ("Licensee") accessing and otherwise + using Python 2.7.12 software in source or binary form and its associated + documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby + grants Licensee a nonexclusive, royalty-free, world-wide license to + reproduce, analyze, test, perform and/or display publicly, prepare + derivative works, distribute, and otherwise use Python 2.7.12 alone or + in any derivative version, provided, however, that PSF's License Agreement + and PSF's notice of copyright, i.e., "Copyright (c) 2001-2016 Python Software + Foundation; All Rights Reserved" are retained in Python 2.7.12 alone or in + any derivative version prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on or + incorporates Python 2.7.12 or any part thereof, and wants to make the + derivative work available to others as provided herein, then Licensee + hereby agrees to include in any such work a brief summary of the changes + made to Python 2.7.12. + +4. PSF is making Python 2.7.12 available to Licensee on an "AS IS" basis. + PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF + EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION + OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT + THE USE OF PYTHON 2.7.12 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 2.7.12 + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT + OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.7.12, OR ANY + DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material breach + of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any + relationship of agency, partnership, or joint venture between PSF and + Licensee. This License Agreement does not grant permission to use PSF + trademarks or trade name in a trademark sense to endorse or promote + products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python 2.7.12, Licensee agrees + to be bound by the terms and conditions of this License Agreement. + + ====================================================================== + ====================================================================== + +python-lz4 + +Copyright (c) 2012-2013, Steeve Morin +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 Steeve Morin 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. + +============================================================ + +Additional 4th party: +py3c +------ +from pyc3.h file in code directory: + +/* +The MIT License (MIT) + +Copyright (c) 2015, Red Hat, Inc. and/or its affiliates + +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. +*/ + +------------------------------------------------------------------------------ + +lz4 +--- +from lz4.h file in lz4libs directory + +/* + * LZ4 - Fast LZ compression algorithm + * Header File + * Copyright (C) 2011-present, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + 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. + + 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 + OWNER 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. + + You can contact the author at : + - LZ4 homepage : http://www.lz4.org + - LZ4 source repository : https://github.com/lz4/lz4 +*/ + +** Future 4th party +(https://files.pythonhosted.org/packages/90/52/e20466b85000a181e1e144fd8305caf +2cf475e2f9674e797b222f8105f5f/future-0.17.1.tar.gz) +Copyright (c) 2013-2018 Python Charmers Pty Ltd, Australia +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. + + ====================================================================== + ====================================================================== + +Standard Licenses + +Apache License v2.0, January 2004 + +The following applies to all products licensed under the Apache 2.0 +License: You may not use the identified files except in compliance +with the Apache License, Version 2.0 (the "License.") You may obtain a +copy of the License at http://www.apache.org/licenses/LICENSE-2.0. A +copy of the license is also reproduced below. Unless required by +applicable law or agreed to in writing, software distributed under the +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for +the specific language governing permissions and limitations under the +License. + +Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the +copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other +entities that control, are controlled by, or are under common control +with that entity. For the purposes of this definition, "control" means +(i) the power, direct or indirect, to cause the direction or +management of such entity, whether by contract or otherwise, or (ii) +ownership of fifty percent (50%) or more of the outstanding shares, or +(iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but not +limited to compiled object code, generated documentation, and +conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object +form, made available under the License, as indicated by a copyright +notice that is included in or attached to the work (an example is +provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the +purposes of this License, Derivative Works shall not include works +that remain separable from, or merely link (or bind by name) to the +interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the +original version of the Work and any modifications or additions to +that Work or Derivative Works thereof, that is intentionally submitted +to Licensor for inclusion in the Work by the copyright owner or by an +individual or Legal Entity authorized to submit on behalf of the +copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent to +the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control +systems, and issue tracking systems that are managed by, or on behalf +of, the Licensor for the purpose of discussing and improving the Work, +but excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, publicly +display, publicly perform, sublicense, and distribute the Work and +such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except +as stated in this section) patent license to make, have made, use, +offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such +Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work to +which such Contribution(s) was submitted. If You institute patent +litigation against any entity (including a cross-claim or counterclaim +in a lawsuit) alleging that the Work or a Contribution incorporated +within the Work constitutes direct or contributory patent +infringement, then any patent licenses granted to You under this +License for that Work shall terminate as of the date such litigation +is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work +or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You meet +the following conditions: + +(a) You must give any other recipients of the Work or Derivative Works +a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works that +You distribute, all copyright, patent, trademark, and attribution +notices from the Source form of the Work, excluding those notices that +do not pertain to any part of the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained + +within such NOTICE file, excluding those notices that do not pertain +to any part of the Derivative Works, in at least one of the following +places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided +along with the Derivative Works; or, within a display generated by the +Derivative Works, if and wherever such third-party notices normally +appear. The contents of the NOTICE file are for informational purposes +only and do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside or as +an addendum to the NOTICE text from the Work, provided that such +additional attribution notices cannot be construed as modifying the +License. + +You may add Your own copyright statement to Your modifications and may +provide additional or different license terms and conditions for use, +reproduction, or distribution of Your modifications, or for any such +Derivative Works as a whole, provided Your use, reproduction, and +distribution of the Work otherwise complies with the conditions stated +in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work by +You to the Licensor shall be under the terms and conditions of this +License, without any additional terms or conditions. Notwithstanding +the above, nothing herein shall supersede or modify the terms of any +separate license agreement you may have executed with Licensor +regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed +to in writing, Licensor provides the Work (and each Contributor +provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied, including, without +limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, +MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely +responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your +exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, unless +required by applicable law (such as deliberate and grossly negligent +acts) or agreed to in writing, shall any Contributor be liable to You +for damages, including any direct, indirect, special, incidental, or +consequential damages of any character arising as a result of this +License or out of the use or inability to use the Work (including but +not limited to damages for loss of goodwill, work stoppage, computer +failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility +of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, and +charge a fee for, acceptance of support, warranty, indemnity, or other +liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only on +Your own behalf and on Your sole responsibility, not on behalf of any +other Contributor, and only if You agree to indemnify, defend, and +hold each Contributor harmless for any liability incurred by, or +claims asserted against, such Contributor by reason of your accepting +any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included +on the same "printed page" as the copyright notice for easier identification +within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied. See the License for the specific language governing permissions +and limitations under the License. + + ====================================================================== + ====================================================================== + +Mozilla Public License 2.0 + + MOZILLA PUBLIC LICENSE + Version 2.0 + --------------- +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + + + ====================================================================== + ====================================================================== + +GNU Lesser General Public License v2.1, February 1999 + +The following applies to all products licensed under the +GNU Lesser General Public License, Version 2.1: You may +not use the identified files except in compliance with +the GNU Lesser General Public License, Version 2.1 (the +"License"). You may obtain a copy of the License at +http://www.gnu.org/licenses/lgpl-2.1.html. A copy of the +license is also reproduced below. Unless required by +applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +or implied. See the License for the specific language governing +permissions and limitations under the License. + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs +must be allowed to use the library. A more frequent case is that +a free library does the same job as widely used non-free libraries. +In this case, there is little to gain by limiting the free library +to free software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended +to apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + ====================================================================== + ====================================================================== + +Written Offer for Source Code + + For any software that you receive from Oracle in binary form which is + licensed under an open source license that gives you the right to + receive the source code for that binary, you can obtain a copy of the + applicable source code by visiting + http://www.oracle.com/goto/opensourcecode. If the source code for the + binary was not provided to you with the binary, you can also receive a + copy of the source code on physical media by submitting a written + request to the address listed below or by sending an email to Oracle + using the following link: + http://www.oracle.com/goto/opensourcecode/request. + + Oracle America, Inc. + Attn: Senior Vice President + Development and Engineering Legal + 500 Oracle Parkway, 10th Floor + Redwood Shores, CA 94065 + + Your request should include: + + * The name of the binary for which you are requesting the source code + + * The name and version number of the Oracle product containing the + binary + + * The date you received the Oracle product + + * Your name + + * Your company name (if applicable) + + * Your return mailing address and email, and + + * A telephone number in the event we need to reach you. + + + We may charge you a fee to cover the cost of physical media and + processing. + + Your request must be sent + + a. within three (3) years of the date you received the Oracle product + that included the binary that is the subject of your request, or + + b. in the case of code licensed under the GPL v3 for as long as Oracle + offers spare parts or customer support for that product model. diff --git a/virt/lib/python3.9/site-packages/mysqlx/errorcode 3.py b/virt/lib/python3.9/site-packages/mysqlx/errorcode 3.py new file mode 100644 index 00000000..1923dee7 --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysqlx/errorcode 3.py @@ -0,0 +1,1877 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, as +# published by the Free Software Foundation. +# +# This program is also distributed with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an +# additional permission to link the program and your derivative works +# with the separately licensed software that they have included with +# MySQL. +# +# Without limiting anything contained in the foregoing, this file, +# which is part of MySQL Connector/Python, is also subject to the +# Universal FOSS Exception, version 1.0, a copy of which can be found at +# http://oss.oracle.com/licenses/universal-foss-exception. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License, version 2.0, for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""This module contains the MySQL Server and Client error codes.""" + +# This file was auto-generated. +_GENERATED_ON = "2021-08-11" +_MYSQL_VERSION = (8, 0, 27) + +# Start MySQL Errors +OBSOLETE_ER_HASHCHK = 1000 +OBSOLETE_ER_NISAMCHK = 1001 +ER_NO = 1002 +ER_YES = 1003 +ER_CANT_CREATE_FILE = 1004 +ER_CANT_CREATE_TABLE = 1005 +ER_CANT_CREATE_DB = 1006 +ER_DB_CREATE_EXISTS = 1007 +ER_DB_DROP_EXISTS = 1008 +OBSOLETE_ER_DB_DROP_DELETE = 1009 +ER_DB_DROP_RMDIR = 1010 +OBSOLETE_ER_CANT_DELETE_FILE = 1011 +ER_CANT_FIND_SYSTEM_REC = 1012 +ER_CANT_GET_STAT = 1013 +OBSOLETE_ER_CANT_GET_WD = 1014 +ER_CANT_LOCK = 1015 +ER_CANT_OPEN_FILE = 1016 +ER_FILE_NOT_FOUND = 1017 +ER_CANT_READ_DIR = 1018 +OBSOLETE_ER_CANT_SET_WD = 1019 +ER_CHECKREAD = 1020 +OBSOLETE_ER_DISK_FULL = 1021 +ER_DUP_KEY = 1022 +OBSOLETE_ER_ERROR_ON_CLOSE = 1023 +ER_ERROR_ON_READ = 1024 +ER_ERROR_ON_RENAME = 1025 +ER_ERROR_ON_WRITE = 1026 +ER_FILE_USED = 1027 +OBSOLETE_ER_FILSORT_ABORT = 1028 +OBSOLETE_ER_FORM_NOT_FOUND = 1029 +ER_GET_ERRNO = 1030 +ER_ILLEGAL_HA = 1031 +ER_KEY_NOT_FOUND = 1032 +ER_NOT_FORM_FILE = 1033 +ER_NOT_KEYFILE = 1034 +ER_OLD_KEYFILE = 1035 +ER_OPEN_AS_READONLY = 1036 +ER_OUTOFMEMORY = 1037 +ER_OUT_OF_SORTMEMORY = 1038 +OBSOLETE_ER_UNEXPECTED_EOF = 1039 +ER_CON_COUNT_ERROR = 1040 +ER_OUT_OF_RESOURCES = 1041 +ER_BAD_HOST_ERROR = 1042 +ER_HANDSHAKE_ERROR = 1043 +ER_DBACCESS_DENIED_ERROR = 1044 +ER_ACCESS_DENIED_ERROR = 1045 +ER_NO_DB_ERROR = 1046 +ER_UNKNOWN_COM_ERROR = 1047 +ER_BAD_NULL_ERROR = 1048 +ER_BAD_DB_ERROR = 1049 +ER_TABLE_EXISTS_ERROR = 1050 +ER_BAD_TABLE_ERROR = 1051 +ER_NON_UNIQ_ERROR = 1052 +ER_SERVER_SHUTDOWN = 1053 +ER_BAD_FIELD_ERROR = 1054 +ER_WRONG_FIELD_WITH_GROUP = 1055 +ER_WRONG_GROUP_FIELD = 1056 +ER_WRONG_SUM_SELECT = 1057 +ER_WRONG_VALUE_COUNT = 1058 +ER_TOO_LONG_IDENT = 1059 +ER_DUP_FIELDNAME = 1060 +ER_DUP_KEYNAME = 1061 +ER_DUP_ENTRY = 1062 +ER_WRONG_FIELD_SPEC = 1063 +ER_PARSE_ERROR = 1064 +ER_EMPTY_QUERY = 1065 +ER_NONUNIQ_TABLE = 1066 +ER_INVALID_DEFAULT = 1067 +ER_MULTIPLE_PRI_KEY = 1068 +ER_TOO_MANY_KEYS = 1069 +ER_TOO_MANY_KEY_PARTS = 1070 +ER_TOO_LONG_KEY = 1071 +ER_KEY_COLUMN_DOES_NOT_EXITS = 1072 +ER_BLOB_USED_AS_KEY = 1073 +ER_TOO_BIG_FIELDLENGTH = 1074 +ER_WRONG_AUTO_KEY = 1075 +ER_READY = 1076 +OBSOLETE_ER_NORMAL_SHUTDOWN = 1077 +OBSOLETE_ER_GOT_SIGNAL = 1078 +ER_SHUTDOWN_COMPLETE = 1079 +ER_FORCING_CLOSE = 1080 +ER_IPSOCK_ERROR = 1081 +ER_NO_SUCH_INDEX = 1082 +ER_WRONG_FIELD_TERMINATORS = 1083 +ER_BLOBS_AND_NO_TERMINATED = 1084 +ER_TEXTFILE_NOT_READABLE = 1085 +ER_FILE_EXISTS_ERROR = 1086 +ER_LOAD_INFO = 1087 +ER_ALTER_INFO = 1088 +ER_WRONG_SUB_KEY = 1089 +ER_CANT_REMOVE_ALL_FIELDS = 1090 +ER_CANT_DROP_FIELD_OR_KEY = 1091 +ER_INSERT_INFO = 1092 +ER_UPDATE_TABLE_USED = 1093 +ER_NO_SUCH_THREAD = 1094 +ER_KILL_DENIED_ERROR = 1095 +ER_NO_TABLES_USED = 1096 +ER_TOO_BIG_SET = 1097 +ER_NO_UNIQUE_LOGFILE = 1098 +ER_TABLE_NOT_LOCKED_FOR_WRITE = 1099 +ER_TABLE_NOT_LOCKED = 1100 +ER_BLOB_CANT_HAVE_DEFAULT = 1101 +ER_WRONG_DB_NAME = 1102 +ER_WRONG_TABLE_NAME = 1103 +ER_TOO_BIG_SELECT = 1104 +ER_UNKNOWN_ERROR = 1105 +ER_UNKNOWN_PROCEDURE = 1106 +ER_WRONG_PARAMCOUNT_TO_PROCEDURE = 1107 +ER_WRONG_PARAMETERS_TO_PROCEDURE = 1108 +ER_UNKNOWN_TABLE = 1109 +ER_FIELD_SPECIFIED_TWICE = 1110 +ER_INVALID_GROUP_FUNC_USE = 1111 +ER_UNSUPPORTED_EXTENSION = 1112 +ER_TABLE_MUST_HAVE_COLUMNS = 1113 +ER_RECORD_FILE_FULL = 1114 +ER_UNKNOWN_CHARACTER_SET = 1115 +ER_TOO_MANY_TABLES = 1116 +ER_TOO_MANY_FIELDS = 1117 +ER_TOO_BIG_ROWSIZE = 1118 +ER_STACK_OVERRUN = 1119 +ER_WRONG_OUTER_JOIN_UNUSED = 1120 +ER_NULL_COLUMN_IN_INDEX = 1121 +ER_CANT_FIND_UDF = 1122 +ER_CANT_INITIALIZE_UDF = 1123 +ER_UDF_NO_PATHS = 1124 +ER_UDF_EXISTS = 1125 +ER_CANT_OPEN_LIBRARY = 1126 +ER_CANT_FIND_DL_ENTRY = 1127 +ER_FUNCTION_NOT_DEFINED = 1128 +ER_HOST_IS_BLOCKED = 1129 +ER_HOST_NOT_PRIVILEGED = 1130 +ER_PASSWORD_ANONYMOUS_USER = 1131 +ER_PASSWORD_NOT_ALLOWED = 1132 +ER_PASSWORD_NO_MATCH = 1133 +ER_UPDATE_INFO = 1134 +ER_CANT_CREATE_THREAD = 1135 +ER_WRONG_VALUE_COUNT_ON_ROW = 1136 +ER_CANT_REOPEN_TABLE = 1137 +ER_INVALID_USE_OF_NULL = 1138 +ER_REGEXP_ERROR = 1139 +ER_MIX_OF_GROUP_FUNC_AND_FIELDS = 1140 +ER_NONEXISTING_GRANT = 1141 +ER_TABLEACCESS_DENIED_ERROR = 1142 +ER_COLUMNACCESS_DENIED_ERROR = 1143 +ER_ILLEGAL_GRANT_FOR_TABLE = 1144 +ER_GRANT_WRONG_HOST_OR_USER = 1145 +ER_NO_SUCH_TABLE = 1146 +ER_NONEXISTING_TABLE_GRANT = 1147 +ER_NOT_ALLOWED_COMMAND = 1148 +ER_SYNTAX_ERROR = 1149 +OBSOLETE_ER_UNUSED1 = 1150 +OBSOLETE_ER_UNUSED2 = 1151 +ER_ABORTING_CONNECTION = 1152 +ER_NET_PACKET_TOO_LARGE = 1153 +ER_NET_READ_ERROR_FROM_PIPE = 1154 +ER_NET_FCNTL_ERROR = 1155 +ER_NET_PACKETS_OUT_OF_ORDER = 1156 +ER_NET_UNCOMPRESS_ERROR = 1157 +ER_NET_READ_ERROR = 1158 +ER_NET_READ_INTERRUPTED = 1159 +ER_NET_ERROR_ON_WRITE = 1160 +ER_NET_WRITE_INTERRUPTED = 1161 +ER_TOO_LONG_STRING = 1162 +ER_TABLE_CANT_HANDLE_BLOB = 1163 +ER_TABLE_CANT_HANDLE_AUTO_INCREMENT = 1164 +OBSOLETE_ER_UNUSED3 = 1165 +ER_WRONG_COLUMN_NAME = 1166 +ER_WRONG_KEY_COLUMN = 1167 +ER_WRONG_MRG_TABLE = 1168 +ER_DUP_UNIQUE = 1169 +ER_BLOB_KEY_WITHOUT_LENGTH = 1170 +ER_PRIMARY_CANT_HAVE_NULL = 1171 +ER_TOO_MANY_ROWS = 1172 +ER_REQUIRES_PRIMARY_KEY = 1173 +OBSOLETE_ER_NO_RAID_COMPILED = 1174 +ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE = 1175 +ER_KEY_DOES_NOT_EXITS = 1176 +ER_CHECK_NO_SUCH_TABLE = 1177 +ER_CHECK_NOT_IMPLEMENTED = 1178 +ER_CANT_DO_THIS_DURING_AN_TRANSACTION = 1179 +ER_ERROR_DURING_COMMIT = 1180 +ER_ERROR_DURING_ROLLBACK = 1181 +ER_ERROR_DURING_FLUSH_LOGS = 1182 +OBSOLETE_ER_ERROR_DURING_CHECKPOINT = 1183 +ER_NEW_ABORTING_CONNECTION = 1184 +OBSOLETE_ER_DUMP_NOT_IMPLEMENTED = 1185 +OBSOLETE_ER_FLUSH_MASTER_BINLOG_CLOSED = 1186 +OBSOLETE_ER_INDEX_REBUILD = 1187 +ER_MASTER = 1188 +ER_MASTER_NET_READ = 1189 +ER_MASTER_NET_WRITE = 1190 +ER_FT_MATCHING_KEY_NOT_FOUND = 1191 +ER_LOCK_OR_ACTIVE_TRANSACTION = 1192 +ER_UNKNOWN_SYSTEM_VARIABLE = 1193 +ER_CRASHED_ON_USAGE = 1194 +ER_CRASHED_ON_REPAIR = 1195 +ER_WARNING_NOT_COMPLETE_ROLLBACK = 1196 +ER_TRANS_CACHE_FULL = 1197 +OBSOLETE_ER_SLAVE_MUST_STOP = 1198 +ER_SLAVE_NOT_RUNNING = 1199 +ER_BAD_SLAVE = 1200 +ER_MASTER_INFO = 1201 +ER_SLAVE_THREAD = 1202 +ER_TOO_MANY_USER_CONNECTIONS = 1203 +ER_SET_CONSTANTS_ONLY = 1204 +ER_LOCK_WAIT_TIMEOUT = 1205 +ER_LOCK_TABLE_FULL = 1206 +ER_READ_ONLY_TRANSACTION = 1207 +OBSOLETE_ER_DROP_DB_WITH_READ_LOCK = 1208 +OBSOLETE_ER_CREATE_DB_WITH_READ_LOCK = 1209 +ER_WRONG_ARGUMENTS = 1210 +ER_NO_PERMISSION_TO_CREATE_USER = 1211 +OBSOLETE_ER_UNION_TABLES_IN_DIFFERENT_DIR = 1212 +ER_LOCK_DEADLOCK = 1213 +ER_TABLE_CANT_HANDLE_FT = 1214 +ER_CANNOT_ADD_FOREIGN = 1215 +ER_NO_REFERENCED_ROW = 1216 +ER_ROW_IS_REFERENCED = 1217 +ER_CONNECT_TO_MASTER = 1218 +OBSOLETE_ER_QUERY_ON_MASTER = 1219 +ER_ERROR_WHEN_EXECUTING_COMMAND = 1220 +ER_WRONG_USAGE = 1221 +ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT = 1222 +ER_CANT_UPDATE_WITH_READLOCK = 1223 +ER_MIXING_NOT_ALLOWED = 1224 +ER_DUP_ARGUMENT = 1225 +ER_USER_LIMIT_REACHED = 1226 +ER_SPECIFIC_ACCESS_DENIED_ERROR = 1227 +ER_LOCAL_VARIABLE = 1228 +ER_GLOBAL_VARIABLE = 1229 +ER_NO_DEFAULT = 1230 +ER_WRONG_VALUE_FOR_VAR = 1231 +ER_WRONG_TYPE_FOR_VAR = 1232 +ER_VAR_CANT_BE_READ = 1233 +ER_CANT_USE_OPTION_HERE = 1234 +ER_NOT_SUPPORTED_YET = 1235 +ER_MASTER_FATAL_ERROR_READING_BINLOG = 1236 +ER_SLAVE_IGNORED_TABLE = 1237 +ER_INCORRECT_GLOBAL_LOCAL_VAR = 1238 +ER_WRONG_FK_DEF = 1239 +ER_KEY_REF_DO_NOT_MATCH_TABLE_REF = 1240 +ER_OPERAND_COLUMNS = 1241 +ER_SUBQUERY_NO_1_ROW = 1242 +ER_UNKNOWN_STMT_HANDLER = 1243 +ER_CORRUPT_HELP_DB = 1244 +OBSOLETE_ER_CYCLIC_REFERENCE = 1245 +ER_AUTO_CONVERT = 1246 +ER_ILLEGAL_REFERENCE = 1247 +ER_DERIVED_MUST_HAVE_ALIAS = 1248 +ER_SELECT_REDUCED = 1249 +ER_TABLENAME_NOT_ALLOWED_HERE = 1250 +ER_NOT_SUPPORTED_AUTH_MODE = 1251 +ER_SPATIAL_CANT_HAVE_NULL = 1252 +ER_COLLATION_CHARSET_MISMATCH = 1253 +OBSOLETE_ER_SLAVE_WAS_RUNNING = 1254 +OBSOLETE_ER_SLAVE_WAS_NOT_RUNNING = 1255 +ER_TOO_BIG_FOR_UNCOMPRESS = 1256 +ER_ZLIB_Z_MEM_ERROR = 1257 +ER_ZLIB_Z_BUF_ERROR = 1258 +ER_ZLIB_Z_DATA_ERROR = 1259 +ER_CUT_VALUE_GROUP_CONCAT = 1260 +ER_WARN_TOO_FEW_RECORDS = 1261 +ER_WARN_TOO_MANY_RECORDS = 1262 +ER_WARN_NULL_TO_NOTNULL = 1263 +ER_WARN_DATA_OUT_OF_RANGE = 1264 +WARN_DATA_TRUNCATED = 1265 +ER_WARN_USING_OTHER_HANDLER = 1266 +ER_CANT_AGGREGATE_2COLLATIONS = 1267 +OBSOLETE_ER_DROP_USER = 1268 +ER_REVOKE_GRANTS = 1269 +ER_CANT_AGGREGATE_3COLLATIONS = 1270 +ER_CANT_AGGREGATE_NCOLLATIONS = 1271 +ER_VARIABLE_IS_NOT_STRUCT = 1272 +ER_UNKNOWN_COLLATION = 1273 +ER_SLAVE_IGNORED_SSL_PARAMS = 1274 +OBSOLETE_ER_SERVER_IS_IN_SECURE_AUTH_MODE = 1275 +ER_WARN_FIELD_RESOLVED = 1276 +ER_BAD_SLAVE_UNTIL_COND = 1277 +ER_MISSING_SKIP_SLAVE = 1278 +ER_UNTIL_COND_IGNORED = 1279 +ER_WRONG_NAME_FOR_INDEX = 1280 +ER_WRONG_NAME_FOR_CATALOG = 1281 +OBSOLETE_ER_WARN_QC_RESIZE = 1282 +ER_BAD_FT_COLUMN = 1283 +ER_UNKNOWN_KEY_CACHE = 1284 +ER_WARN_HOSTNAME_WONT_WORK = 1285 +ER_UNKNOWN_STORAGE_ENGINE = 1286 +ER_WARN_DEPRECATED_SYNTAX = 1287 +ER_NON_UPDATABLE_TABLE = 1288 +ER_FEATURE_DISABLED = 1289 +ER_OPTION_PREVENTS_STATEMENT = 1290 +ER_DUPLICATED_VALUE_IN_TYPE = 1291 +ER_TRUNCATED_WRONG_VALUE = 1292 +OBSOLETE_ER_TOO_MUCH_AUTO_TIMESTAMP_COLS = 1293 +ER_INVALID_ON_UPDATE = 1294 +ER_UNSUPPORTED_PS = 1295 +ER_GET_ERRMSG = 1296 +ER_GET_TEMPORARY_ERRMSG = 1297 +ER_UNKNOWN_TIME_ZONE = 1298 +ER_WARN_INVALID_TIMESTAMP = 1299 +ER_INVALID_CHARACTER_STRING = 1300 +ER_WARN_ALLOWED_PACKET_OVERFLOWED = 1301 +ER_CONFLICTING_DECLARATIONS = 1302 +ER_SP_NO_RECURSIVE_CREATE = 1303 +ER_SP_ALREADY_EXISTS = 1304 +ER_SP_DOES_NOT_EXIST = 1305 +ER_SP_DROP_FAILED = 1306 +ER_SP_STORE_FAILED = 1307 +ER_SP_LILABEL_MISMATCH = 1308 +ER_SP_LABEL_REDEFINE = 1309 +ER_SP_LABEL_MISMATCH = 1310 +ER_SP_UNINIT_VAR = 1311 +ER_SP_BADSELECT = 1312 +ER_SP_BADRETURN = 1313 +ER_SP_BADSTATEMENT = 1314 +ER_UPDATE_LOG_DEPRECATED_IGNORED = 1315 +ER_UPDATE_LOG_DEPRECATED_TRANSLATED = 1316 +ER_QUERY_INTERRUPTED = 1317 +ER_SP_WRONG_NO_OF_ARGS = 1318 +ER_SP_COND_MISMATCH = 1319 +ER_SP_NORETURN = 1320 +ER_SP_NORETURNEND = 1321 +ER_SP_BAD_CURSOR_QUERY = 1322 +ER_SP_BAD_CURSOR_SELECT = 1323 +ER_SP_CURSOR_MISMATCH = 1324 +ER_SP_CURSOR_ALREADY_OPEN = 1325 +ER_SP_CURSOR_NOT_OPEN = 1326 +ER_SP_UNDECLARED_VAR = 1327 +ER_SP_WRONG_NO_OF_FETCH_ARGS = 1328 +ER_SP_FETCH_NO_DATA = 1329 +ER_SP_DUP_PARAM = 1330 +ER_SP_DUP_VAR = 1331 +ER_SP_DUP_COND = 1332 +ER_SP_DUP_CURS = 1333 +ER_SP_CANT_ALTER = 1334 +ER_SP_SUBSELECT_NYI = 1335 +ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG = 1336 +ER_SP_VARCOND_AFTER_CURSHNDLR = 1337 +ER_SP_CURSOR_AFTER_HANDLER = 1338 +ER_SP_CASE_NOT_FOUND = 1339 +ER_FPARSER_TOO_BIG_FILE = 1340 +ER_FPARSER_BAD_HEADER = 1341 +ER_FPARSER_EOF_IN_COMMENT = 1342 +ER_FPARSER_ERROR_IN_PARAMETER = 1343 +ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER = 1344 +ER_VIEW_NO_EXPLAIN = 1345 +OBSOLETE_ER_FRM_UNKNOWN_TYPE = 1346 +ER_WRONG_OBJECT = 1347 +ER_NONUPDATEABLE_COLUMN = 1348 +OBSOLETE_ER_VIEW_SELECT_DERIVED_UNUSED = 1349 +ER_VIEW_SELECT_CLAUSE = 1350 +ER_VIEW_SELECT_VARIABLE = 1351 +ER_VIEW_SELECT_TMPTABLE = 1352 +ER_VIEW_WRONG_LIST = 1353 +ER_WARN_VIEW_MERGE = 1354 +ER_WARN_VIEW_WITHOUT_KEY = 1355 +ER_VIEW_INVALID = 1356 +ER_SP_NO_DROP_SP = 1357 +OBSOLETE_ER_SP_GOTO_IN_HNDLR = 1358 +ER_TRG_ALREADY_EXISTS = 1359 +ER_TRG_DOES_NOT_EXIST = 1360 +ER_TRG_ON_VIEW_OR_TEMP_TABLE = 1361 +ER_TRG_CANT_CHANGE_ROW = 1362 +ER_TRG_NO_SUCH_ROW_IN_TRG = 1363 +ER_NO_DEFAULT_FOR_FIELD = 1364 +ER_DIVISION_BY_ZERO = 1365 +ER_TRUNCATED_WRONG_VALUE_FOR_FIELD = 1366 +ER_ILLEGAL_VALUE_FOR_TYPE = 1367 +ER_VIEW_NONUPD_CHECK = 1368 +ER_VIEW_CHECK_FAILED = 1369 +ER_PROCACCESS_DENIED_ERROR = 1370 +ER_RELAY_LOG_FAIL = 1371 +OBSOLETE_ER_PASSWD_LENGTH = 1372 +ER_UNKNOWN_TARGET_BINLOG = 1373 +ER_IO_ERR_LOG_INDEX_READ = 1374 +ER_BINLOG_PURGE_PROHIBITED = 1375 +ER_FSEEK_FAIL = 1376 +ER_BINLOG_PURGE_FATAL_ERR = 1377 +ER_LOG_IN_USE = 1378 +ER_LOG_PURGE_UNKNOWN_ERR = 1379 +ER_RELAY_LOG_INIT = 1380 +ER_NO_BINARY_LOGGING = 1381 +ER_RESERVED_SYNTAX = 1382 +OBSOLETE_ER_WSAS_FAILED = 1383 +OBSOLETE_ER_DIFF_GROUPS_PROC = 1384 +OBSOLETE_ER_NO_GROUP_FOR_PROC = 1385 +OBSOLETE_ER_ORDER_WITH_PROC = 1386 +OBSOLETE_ER_LOGGING_PROHIBIT_CHANGING_OF = 1387 +OBSOLETE_ER_NO_FILE_MAPPING = 1388 +OBSOLETE_ER_WRONG_MAGIC = 1389 +ER_PS_MANY_PARAM = 1390 +ER_KEY_PART_0 = 1391 +ER_VIEW_CHECKSUM = 1392 +ER_VIEW_MULTIUPDATE = 1393 +ER_VIEW_NO_INSERT_FIELD_LIST = 1394 +ER_VIEW_DELETE_MERGE_VIEW = 1395 +ER_CANNOT_USER = 1396 +ER_XAER_NOTA = 1397 +ER_XAER_INVAL = 1398 +ER_XAER_RMFAIL = 1399 +ER_XAER_OUTSIDE = 1400 +ER_XAER_RMERR = 1401 +ER_XA_RBROLLBACK = 1402 +ER_NONEXISTING_PROC_GRANT = 1403 +ER_PROC_AUTO_GRANT_FAIL = 1404 +ER_PROC_AUTO_REVOKE_FAIL = 1405 +ER_DATA_TOO_LONG = 1406 +ER_SP_BAD_SQLSTATE = 1407 +ER_STARTUP = 1408 +ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR = 1409 +ER_CANT_CREATE_USER_WITH_GRANT = 1410 +ER_WRONG_VALUE_FOR_TYPE = 1411 +ER_TABLE_DEF_CHANGED = 1412 +ER_SP_DUP_HANDLER = 1413 +ER_SP_NOT_VAR_ARG = 1414 +ER_SP_NO_RETSET = 1415 +ER_CANT_CREATE_GEOMETRY_OBJECT = 1416 +OBSOLETE_ER_FAILED_ROUTINE_BREAK_BINLOG = 1417 +ER_BINLOG_UNSAFE_ROUTINE = 1418 +ER_BINLOG_CREATE_ROUTINE_NEED_SUPER = 1419 +OBSOLETE_ER_EXEC_STMT_WITH_OPEN_CURSOR = 1420 +ER_STMT_HAS_NO_OPEN_CURSOR = 1421 +ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG = 1422 +ER_NO_DEFAULT_FOR_VIEW_FIELD = 1423 +ER_SP_NO_RECURSION = 1424 +ER_TOO_BIG_SCALE = 1425 +ER_TOO_BIG_PRECISION = 1426 +ER_M_BIGGER_THAN_D = 1427 +ER_WRONG_LOCK_OF_SYSTEM_TABLE = 1428 +ER_CONNECT_TO_FOREIGN_DATA_SOURCE = 1429 +ER_QUERY_ON_FOREIGN_DATA_SOURCE = 1430 +ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST = 1431 +ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE = 1432 +ER_FOREIGN_DATA_STRING_INVALID = 1433 +OBSOLETE_ER_CANT_CREATE_FEDERATED_TABLE = 1434 +ER_TRG_IN_WRONG_SCHEMA = 1435 +ER_STACK_OVERRUN_NEED_MORE = 1436 +ER_TOO_LONG_BODY = 1437 +ER_WARN_CANT_DROP_DEFAULT_KEYCACHE = 1438 +ER_TOO_BIG_DISPLAYWIDTH = 1439 +ER_XAER_DUPID = 1440 +ER_DATETIME_FUNCTION_OVERFLOW = 1441 +ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG = 1442 +ER_VIEW_PREVENT_UPDATE = 1443 +ER_PS_NO_RECURSION = 1444 +ER_SP_CANT_SET_AUTOCOMMIT = 1445 +OBSOLETE_ER_MALFORMED_DEFINER = 1446 +ER_VIEW_FRM_NO_USER = 1447 +ER_VIEW_OTHER_USER = 1448 +ER_NO_SUCH_USER = 1449 +ER_FORBID_SCHEMA_CHANGE = 1450 +ER_ROW_IS_REFERENCED_2 = 1451 +ER_NO_REFERENCED_ROW_2 = 1452 +ER_SP_BAD_VAR_SHADOW = 1453 +ER_TRG_NO_DEFINER = 1454 +ER_OLD_FILE_FORMAT = 1455 +ER_SP_RECURSION_LIMIT = 1456 +OBSOLETE_ER_SP_PROC_TABLE_CORRUPT = 1457 +ER_SP_WRONG_NAME = 1458 +ER_TABLE_NEEDS_UPGRADE = 1459 +ER_SP_NO_AGGREGATE = 1460 +ER_MAX_PREPARED_STMT_COUNT_REACHED = 1461 +ER_VIEW_RECURSIVE = 1462 +ER_NON_GROUPING_FIELD_USED = 1463 +ER_TABLE_CANT_HANDLE_SPKEYS = 1464 +ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA = 1465 +ER_REMOVED_SPACES = 1466 +ER_AUTOINC_READ_FAILED = 1467 +ER_USERNAME = 1468 +ER_HOSTNAME = 1469 +ER_WRONG_STRING_LENGTH = 1470 +ER_NON_INSERTABLE_TABLE = 1471 +ER_ADMIN_WRONG_MRG_TABLE = 1472 +ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT = 1473 +ER_NAME_BECOMES_EMPTY = 1474 +ER_AMBIGUOUS_FIELD_TERM = 1475 +ER_FOREIGN_SERVER_EXISTS = 1476 +ER_FOREIGN_SERVER_DOESNT_EXIST = 1477 +ER_ILLEGAL_HA_CREATE_OPTION = 1478 +ER_PARTITION_REQUIRES_VALUES_ERROR = 1479 +ER_PARTITION_WRONG_VALUES_ERROR = 1480 +ER_PARTITION_MAXVALUE_ERROR = 1481 +OBSOLETE_ER_PARTITION_SUBPARTITION_ERROR = 1482 +OBSOLETE_ER_PARTITION_SUBPART_MIX_ERROR = 1483 +ER_PARTITION_WRONG_NO_PART_ERROR = 1484 +ER_PARTITION_WRONG_NO_SUBPART_ERROR = 1485 +ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR = 1486 +OBSOLETE_ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR = 1487 +ER_FIELD_NOT_FOUND_PART_ERROR = 1488 +OBSOLETE_ER_LIST_OF_FIELDS_ONLY_IN_HASH_ERROR = 1489 +ER_INCONSISTENT_PARTITION_INFO_ERROR = 1490 +ER_PARTITION_FUNC_NOT_ALLOWED_ERROR = 1491 +ER_PARTITIONS_MUST_BE_DEFINED_ERROR = 1492 +ER_RANGE_NOT_INCREASING_ERROR = 1493 +ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR = 1494 +ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR = 1495 +ER_PARTITION_ENTRY_ERROR = 1496 +ER_MIX_HANDLER_ERROR = 1497 +ER_PARTITION_NOT_DEFINED_ERROR = 1498 +ER_TOO_MANY_PARTITIONS_ERROR = 1499 +ER_SUBPARTITION_ERROR = 1500 +ER_CANT_CREATE_HANDLER_FILE = 1501 +ER_BLOB_FIELD_IN_PART_FUNC_ERROR = 1502 +ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF = 1503 +ER_NO_PARTS_ERROR = 1504 +ER_PARTITION_MGMT_ON_NONPARTITIONED = 1505 +ER_FOREIGN_KEY_ON_PARTITIONED = 1506 +ER_DROP_PARTITION_NON_EXISTENT = 1507 +ER_DROP_LAST_PARTITION = 1508 +ER_COALESCE_ONLY_ON_HASH_PARTITION = 1509 +ER_REORG_HASH_ONLY_ON_SAME_NO = 1510 +ER_REORG_NO_PARAM_ERROR = 1511 +ER_ONLY_ON_RANGE_LIST_PARTITION = 1512 +ER_ADD_PARTITION_SUBPART_ERROR = 1513 +ER_ADD_PARTITION_NO_NEW_PARTITION = 1514 +ER_COALESCE_PARTITION_NO_PARTITION = 1515 +ER_REORG_PARTITION_NOT_EXIST = 1516 +ER_SAME_NAME_PARTITION = 1517 +ER_NO_BINLOG_ERROR = 1518 +ER_CONSECUTIVE_REORG_PARTITIONS = 1519 +ER_REORG_OUTSIDE_RANGE = 1520 +ER_PARTITION_FUNCTION_FAILURE = 1521 +OBSOLETE_ER_PART_STATE_ERROR = 1522 +ER_LIMITED_PART_RANGE = 1523 +ER_PLUGIN_IS_NOT_LOADED = 1524 +ER_WRONG_VALUE = 1525 +ER_NO_PARTITION_FOR_GIVEN_VALUE = 1526 +ER_FILEGROUP_OPTION_ONLY_ONCE = 1527 +ER_CREATE_FILEGROUP_FAILED = 1528 +ER_DROP_FILEGROUP_FAILED = 1529 +ER_TABLESPACE_AUTO_EXTEND_ERROR = 1530 +ER_WRONG_SIZE_NUMBER = 1531 +ER_SIZE_OVERFLOW_ERROR = 1532 +ER_ALTER_FILEGROUP_FAILED = 1533 +ER_BINLOG_ROW_LOGGING_FAILED = 1534 +OBSOLETE_ER_BINLOG_ROW_WRONG_TABLE_DEF = 1535 +OBSOLETE_ER_BINLOG_ROW_RBR_TO_SBR = 1536 +ER_EVENT_ALREADY_EXISTS = 1537 +OBSOLETE_ER_EVENT_STORE_FAILED = 1538 +ER_EVENT_DOES_NOT_EXIST = 1539 +OBSOLETE_ER_EVENT_CANT_ALTER = 1540 +OBSOLETE_ER_EVENT_DROP_FAILED = 1541 +ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG = 1542 +ER_EVENT_ENDS_BEFORE_STARTS = 1543 +ER_EVENT_EXEC_TIME_IN_THE_PAST = 1544 +OBSOLETE_ER_EVENT_OPEN_TABLE_FAILED = 1545 +OBSOLETE_ER_EVENT_NEITHER_M_EXPR_NOR_M_AT = 1546 +OBSOLETE_ER_COL_COUNT_DOESNT_MATCH_CORRUPTED = 1547 +OBSOLETE_ER_CANNOT_LOAD_FROM_TABLE = 1548 +OBSOLETE_ER_EVENT_CANNOT_DELETE = 1549 +OBSOLETE_ER_EVENT_COMPILE_ERROR = 1550 +ER_EVENT_SAME_NAME = 1551 +OBSOLETE_ER_EVENT_DATA_TOO_LONG = 1552 +ER_DROP_INDEX_FK = 1553 +ER_WARN_DEPRECATED_SYNTAX_WITH_VER = 1554 +OBSOLETE_ER_CANT_WRITE_LOCK_LOG_TABLE = 1555 +ER_CANT_LOCK_LOG_TABLE = 1556 +ER_FOREIGN_DUPLICATE_KEY_OLD_UNUSED = 1557 +ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE = 1558 +OBSOLETE_ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR = 1559 +ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT = 1560 +OBSOLETE_ER_NDB_CANT_SWITCH_BINLOG_FORMAT = 1561 +ER_PARTITION_NO_TEMPORARY = 1562 +ER_PARTITION_CONST_DOMAIN_ERROR = 1563 +ER_PARTITION_FUNCTION_IS_NOT_ALLOWED = 1564 +OBSOLETE_ER_DDL_LOG_ERROR_UNUSED = 1565 +ER_NULL_IN_VALUES_LESS_THAN = 1566 +ER_WRONG_PARTITION_NAME = 1567 +ER_CANT_CHANGE_TX_CHARACTERISTICS = 1568 +ER_DUP_ENTRY_AUTOINCREMENT_CASE = 1569 +OBSOLETE_ER_EVENT_MODIFY_QUEUE_ERROR = 1570 +ER_EVENT_SET_VAR_ERROR = 1571 +ER_PARTITION_MERGE_ERROR = 1572 +OBSOLETE_ER_CANT_ACTIVATE_LOG = 1573 +OBSOLETE_ER_RBR_NOT_AVAILABLE = 1574 +ER_BASE64_DECODE_ERROR = 1575 +ER_EVENT_RECURSION_FORBIDDEN = 1576 +OBSOLETE_ER_EVENTS_DB_ERROR = 1577 +ER_ONLY_INTEGERS_ALLOWED = 1578 +ER_UNSUPORTED_LOG_ENGINE = 1579 +ER_BAD_LOG_STATEMENT = 1580 +ER_CANT_RENAME_LOG_TABLE = 1581 +ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT = 1582 +ER_WRONG_PARAMETERS_TO_NATIVE_FCT = 1583 +ER_WRONG_PARAMETERS_TO_STORED_FCT = 1584 +ER_NATIVE_FCT_NAME_COLLISION = 1585 +ER_DUP_ENTRY_WITH_KEY_NAME = 1586 +ER_BINLOG_PURGE_EMFILE = 1587 +ER_EVENT_CANNOT_CREATE_IN_THE_PAST = 1588 +ER_EVENT_CANNOT_ALTER_IN_THE_PAST = 1589 +OBSOLETE_ER_SLAVE_INCIDENT = 1590 +ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT = 1591 +ER_BINLOG_UNSAFE_STATEMENT = 1592 +ER_BINLOG_FATAL_ERROR = 1593 +OBSOLETE_ER_SLAVE_RELAY_LOG_READ_FAILURE = 1594 +OBSOLETE_ER_SLAVE_RELAY_LOG_WRITE_FAILURE = 1595 +OBSOLETE_ER_SLAVE_CREATE_EVENT_FAILURE = 1596 +OBSOLETE_ER_SLAVE_MASTER_COM_FAILURE = 1597 +ER_BINLOG_LOGGING_IMPOSSIBLE = 1598 +ER_VIEW_NO_CREATION_CTX = 1599 +ER_VIEW_INVALID_CREATION_CTX = 1600 +OBSOLETE_ER_SR_INVALID_CREATION_CTX = 1601 +ER_TRG_CORRUPTED_FILE = 1602 +ER_TRG_NO_CREATION_CTX = 1603 +ER_TRG_INVALID_CREATION_CTX = 1604 +ER_EVENT_INVALID_CREATION_CTX = 1605 +ER_TRG_CANT_OPEN_TABLE = 1606 +OBSOLETE_ER_CANT_CREATE_SROUTINE = 1607 +OBSOLETE_ER_NEVER_USED = 1608 +ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT = 1609 +ER_SLAVE_CORRUPT_EVENT = 1610 +OBSOLETE_ER_LOAD_DATA_INVALID_COLUMN_UNUSED = 1611 +ER_LOG_PURGE_NO_FILE = 1612 +ER_XA_RBTIMEOUT = 1613 +ER_XA_RBDEADLOCK = 1614 +ER_NEED_REPREPARE = 1615 +OBSOLETE_ER_DELAYED_NOT_SUPPORTED = 1616 +WARN_NO_MASTER_INFO = 1617 +WARN_OPTION_IGNORED = 1618 +ER_PLUGIN_DELETE_BUILTIN = 1619 +WARN_PLUGIN_BUSY = 1620 +ER_VARIABLE_IS_READONLY = 1621 +ER_WARN_ENGINE_TRANSACTION_ROLLBACK = 1622 +OBSOLETE_ER_SLAVE_HEARTBEAT_FAILURE = 1623 +ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE = 1624 +ER_NDB_REPLICATION_SCHEMA_ERROR = 1625 +ER_CONFLICT_FN_PARSE_ERROR = 1626 +ER_EXCEPTIONS_WRITE_ERROR = 1627 +ER_TOO_LONG_TABLE_COMMENT = 1628 +ER_TOO_LONG_FIELD_COMMENT = 1629 +ER_FUNC_INEXISTENT_NAME_COLLISION = 1630 +ER_DATABASE_NAME = 1631 +ER_TABLE_NAME = 1632 +ER_PARTITION_NAME = 1633 +ER_SUBPARTITION_NAME = 1634 +ER_TEMPORARY_NAME = 1635 +ER_RENAMED_NAME = 1636 +ER_TOO_MANY_CONCURRENT_TRXS = 1637 +WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED = 1638 +ER_DEBUG_SYNC_TIMEOUT = 1639 +ER_DEBUG_SYNC_HIT_LIMIT = 1640 +ER_DUP_SIGNAL_SET = 1641 +ER_SIGNAL_WARN = 1642 +ER_SIGNAL_NOT_FOUND = 1643 +ER_SIGNAL_EXCEPTION = 1644 +ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER = 1645 +ER_SIGNAL_BAD_CONDITION_TYPE = 1646 +WARN_COND_ITEM_TRUNCATED = 1647 +ER_COND_ITEM_TOO_LONG = 1648 +ER_UNKNOWN_LOCALE = 1649 +ER_SLAVE_IGNORE_SERVER_IDS = 1650 +OBSOLETE_ER_QUERY_CACHE_DISABLED = 1651 +ER_SAME_NAME_PARTITION_FIELD = 1652 +ER_PARTITION_COLUMN_LIST_ERROR = 1653 +ER_WRONG_TYPE_COLUMN_VALUE_ERROR = 1654 +ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR = 1655 +ER_MAXVALUE_IN_VALUES_IN = 1656 +ER_TOO_MANY_VALUES_ERROR = 1657 +ER_ROW_SINGLE_PARTITION_FIELD_ERROR = 1658 +ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD = 1659 +ER_PARTITION_FIELDS_TOO_LONG = 1660 +ER_BINLOG_ROW_ENGINE_AND_STMT_ENGINE = 1661 +ER_BINLOG_ROW_MODE_AND_STMT_ENGINE = 1662 +ER_BINLOG_UNSAFE_AND_STMT_ENGINE = 1663 +ER_BINLOG_ROW_INJECTION_AND_STMT_ENGINE = 1664 +ER_BINLOG_STMT_MODE_AND_ROW_ENGINE = 1665 +ER_BINLOG_ROW_INJECTION_AND_STMT_MODE = 1666 +ER_BINLOG_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE = 1667 +ER_BINLOG_UNSAFE_LIMIT = 1668 +OBSOLETE_ER_UNUSED4 = 1669 +ER_BINLOG_UNSAFE_SYSTEM_TABLE = 1670 +ER_BINLOG_UNSAFE_AUTOINC_COLUMNS = 1671 +ER_BINLOG_UNSAFE_UDF = 1672 +ER_BINLOG_UNSAFE_SYSTEM_VARIABLE = 1673 +ER_BINLOG_UNSAFE_SYSTEM_FUNCTION = 1674 +ER_BINLOG_UNSAFE_NONTRANS_AFTER_TRANS = 1675 +ER_MESSAGE_AND_STATEMENT = 1676 +OBSOLETE_ER_SLAVE_CONVERSION_FAILED = 1677 +ER_SLAVE_CANT_CREATE_CONVERSION = 1678 +ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT = 1679 +ER_PATH_LENGTH = 1680 +ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT = 1681 +ER_WRONG_NATIVE_TABLE_STRUCTURE = 1682 +ER_WRONG_PERFSCHEMA_USAGE = 1683 +ER_WARN_I_S_SKIPPED_TABLE = 1684 +ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT = 1685 +ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_DIRECT = 1686 +ER_SPATIAL_MUST_HAVE_GEOM_COL = 1687 +ER_TOO_LONG_INDEX_COMMENT = 1688 +ER_LOCK_ABORTED = 1689 +ER_DATA_OUT_OF_RANGE = 1690 +OBSOLETE_ER_WRONG_SPVAR_TYPE_IN_LIMIT = 1691 +ER_BINLOG_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE = 1692 +ER_BINLOG_UNSAFE_MIXED_STATEMENT = 1693 +ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SQL_LOG_BIN = 1694 +ER_STORED_FUNCTION_PREVENTS_SWITCH_SQL_LOG_BIN = 1695 +ER_FAILED_READ_FROM_PAR_FILE = 1696 +ER_VALUES_IS_NOT_INT_TYPE_ERROR = 1697 +ER_ACCESS_DENIED_NO_PASSWORD_ERROR = 1698 +ER_SET_PASSWORD_AUTH_PLUGIN = 1699 +OBSOLETE_ER_GRANT_PLUGIN_USER_EXISTS = 1700 +ER_TRUNCATE_ILLEGAL_FK = 1701 +ER_PLUGIN_IS_PERMANENT = 1702 +ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN = 1703 +ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX = 1704 +ER_STMT_CACHE_FULL = 1705 +ER_MULTI_UPDATE_KEY_CONFLICT = 1706 +ER_TABLE_NEEDS_REBUILD = 1707 +WARN_OPTION_BELOW_LIMIT = 1708 +ER_INDEX_COLUMN_TOO_LONG = 1709 +ER_ERROR_IN_TRIGGER_BODY = 1710 +ER_ERROR_IN_UNKNOWN_TRIGGER_BODY = 1711 +ER_INDEX_CORRUPT = 1712 +ER_UNDO_RECORD_TOO_BIG = 1713 +ER_BINLOG_UNSAFE_INSERT_IGNORE_SELECT = 1714 +ER_BINLOG_UNSAFE_INSERT_SELECT_UPDATE = 1715 +ER_BINLOG_UNSAFE_REPLACE_SELECT = 1716 +ER_BINLOG_UNSAFE_CREATE_IGNORE_SELECT = 1717 +ER_BINLOG_UNSAFE_CREATE_REPLACE_SELECT = 1718 +ER_BINLOG_UNSAFE_UPDATE_IGNORE = 1719 +ER_PLUGIN_NO_UNINSTALL = 1720 +ER_PLUGIN_NO_INSTALL = 1721 +ER_BINLOG_UNSAFE_WRITE_AUTOINC_SELECT = 1722 +ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC = 1723 +ER_BINLOG_UNSAFE_INSERT_TWO_KEYS = 1724 +ER_TABLE_IN_FK_CHECK = 1725 +ER_UNSUPPORTED_ENGINE = 1726 +ER_BINLOG_UNSAFE_AUTOINC_NOT_FIRST = 1727 +ER_CANNOT_LOAD_FROM_TABLE_V2 = 1728 +ER_MASTER_DELAY_VALUE_OUT_OF_RANGE = 1729 +ER_ONLY_FD_AND_RBR_EVENTS_ALLOWED_IN_BINLOG_STATEMENT = 1730 +ER_PARTITION_EXCHANGE_DIFFERENT_OPTION = 1731 +ER_PARTITION_EXCHANGE_PART_TABLE = 1732 +ER_PARTITION_EXCHANGE_TEMP_TABLE = 1733 +ER_PARTITION_INSTEAD_OF_SUBPARTITION = 1734 +ER_UNKNOWN_PARTITION = 1735 +ER_TABLES_DIFFERENT_METADATA = 1736 +ER_ROW_DOES_NOT_MATCH_PARTITION = 1737 +ER_BINLOG_CACHE_SIZE_GREATER_THAN_MAX = 1738 +ER_WARN_INDEX_NOT_APPLICABLE = 1739 +ER_PARTITION_EXCHANGE_FOREIGN_KEY = 1740 +OBSOLETE_ER_NO_SUCH_KEY_VALUE = 1741 +ER_RPL_INFO_DATA_TOO_LONG = 1742 +OBSOLETE_ER_NETWORK_READ_EVENT_CHECKSUM_FAILURE = 1743 +OBSOLETE_ER_BINLOG_READ_EVENT_CHECKSUM_FAILURE = 1744 +ER_BINLOG_STMT_CACHE_SIZE_GREATER_THAN_MAX = 1745 +ER_CANT_UPDATE_TABLE_IN_CREATE_TABLE_SELECT = 1746 +ER_PARTITION_CLAUSE_ON_NONPARTITIONED = 1747 +ER_ROW_DOES_NOT_MATCH_GIVEN_PARTITION_SET = 1748 +OBSOLETE_ER_NO_SUCH_PARTITION__UNUSED = 1749 +ER_CHANGE_RPL_INFO_REPOSITORY_FAILURE = 1750 +ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_CREATED_TEMP_TABLE = 1751 +ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_DROPPED_TEMP_TABLE = 1752 +ER_MTS_FEATURE_IS_NOT_SUPPORTED = 1753 +ER_MTS_UPDATED_DBS_GREATER_MAX = 1754 +ER_MTS_CANT_PARALLEL = 1755 +ER_MTS_INCONSISTENT_DATA = 1756 +ER_FULLTEXT_NOT_SUPPORTED_WITH_PARTITIONING = 1757 +ER_DA_INVALID_CONDITION_NUMBER = 1758 +ER_INSECURE_PLAIN_TEXT = 1759 +ER_INSECURE_CHANGE_MASTER = 1760 +ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO = 1761 +ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO = 1762 +ER_SQLTHREAD_WITH_SECURE_SLAVE = 1763 +ER_TABLE_HAS_NO_FT = 1764 +ER_VARIABLE_NOT_SETTABLE_IN_SF_OR_TRIGGER = 1765 +ER_VARIABLE_NOT_SETTABLE_IN_TRANSACTION = 1766 +OBSOLETE_ER_GTID_NEXT_IS_NOT_IN_GTID_NEXT_LIST = 1767 +OBSOLETE_ER_CANT_CHANGE_GTID_NEXT_IN_TRANSACTION = 1768 +ER_SET_STATEMENT_CANNOT_INVOKE_FUNCTION = 1769 +ER_GTID_NEXT_CANT_BE_AUTOMATIC_IF_GTID_NEXT_LIST_IS_NON_NULL = 1770 +OBSOLETE_ER_SKIPPING_LOGGED_TRANSACTION = 1771 +ER_MALFORMED_GTID_SET_SPECIFICATION = 1772 +ER_MALFORMED_GTID_SET_ENCODING = 1773 +ER_MALFORMED_GTID_SPECIFICATION = 1774 +ER_GNO_EXHAUSTED = 1775 +ER_BAD_SLAVE_AUTO_POSITION = 1776 +ER_AUTO_POSITION_REQUIRES_GTID_MODE_NOT_OFF = 1777 +ER_CANT_DO_IMPLICIT_COMMIT_IN_TRX_WHEN_GTID_NEXT_IS_SET = 1778 +ER_GTID_MODE_ON_REQUIRES_ENFORCE_GTID_CONSISTENCY_ON = 1779 +OBSOLETE_ER_GTID_MODE_REQUIRES_BINLOG = 1780 +ER_CANT_SET_GTID_NEXT_TO_GTID_WHEN_GTID_MODE_IS_OFF = 1781 +ER_CANT_SET_GTID_NEXT_TO_ANONYMOUS_WHEN_GTID_MODE_IS_ON = 1782 +ER_CANT_SET_GTID_NEXT_LIST_TO_NON_NULL_WHEN_GTID_MODE_IS_OFF = 1783 +OBSOLETE_ER_FOUND_GTID_EVENT_WHEN_GTID_MODE_IS_OFF__UNUSED = 1784 +ER_GTID_UNSAFE_NON_TRANSACTIONAL_TABLE = 1785 +ER_GTID_UNSAFE_CREATE_SELECT = 1786 +OBSOLETE_ER_GTID_UNSAFE_CREATE_DROP_TEMP_TABLE_IN_TRANSACTION = 1787 +ER_GTID_MODE_CAN_ONLY_CHANGE_ONE_STEP_AT_A_TIME = 1788 +ER_MASTER_HAS_PURGED_REQUIRED_GTIDS = 1789 +ER_CANT_SET_GTID_NEXT_WHEN_OWNING_GTID = 1790 +ER_UNKNOWN_EXPLAIN_FORMAT = 1791 +ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION = 1792 +ER_TOO_LONG_TABLE_PARTITION_COMMENT = 1793 +ER_SLAVE_CONFIGURATION = 1794 +ER_INNODB_FT_LIMIT = 1795 +ER_INNODB_NO_FT_TEMP_TABLE = 1796 +ER_INNODB_FT_WRONG_DOCID_COLUMN = 1797 +ER_INNODB_FT_WRONG_DOCID_INDEX = 1798 +ER_INNODB_ONLINE_LOG_TOO_BIG = 1799 +ER_UNKNOWN_ALTER_ALGORITHM = 1800 +ER_UNKNOWN_ALTER_LOCK = 1801 +ER_MTS_CHANGE_MASTER_CANT_RUN_WITH_GAPS = 1802 +ER_MTS_RECOVERY_FAILURE = 1803 +ER_MTS_RESET_WORKERS = 1804 +ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2 = 1805 +ER_SLAVE_SILENT_RETRY_TRANSACTION = 1806 +ER_DISCARD_FK_CHECKS_RUNNING = 1807 +ER_TABLE_SCHEMA_MISMATCH = 1808 +ER_TABLE_IN_SYSTEM_TABLESPACE = 1809 +ER_IO_READ_ERROR = 1810 +ER_IO_WRITE_ERROR = 1811 +ER_TABLESPACE_MISSING = 1812 +ER_TABLESPACE_EXISTS = 1813 +ER_TABLESPACE_DISCARDED = 1814 +ER_INTERNAL_ERROR = 1815 +ER_INNODB_IMPORT_ERROR = 1816 +ER_INNODB_INDEX_CORRUPT = 1817 +ER_INVALID_YEAR_COLUMN_LENGTH = 1818 +ER_NOT_VALID_PASSWORD = 1819 +ER_MUST_CHANGE_PASSWORD = 1820 +ER_FK_NO_INDEX_CHILD = 1821 +ER_FK_NO_INDEX_PARENT = 1822 +ER_FK_FAIL_ADD_SYSTEM = 1823 +ER_FK_CANNOT_OPEN_PARENT = 1824 +ER_FK_INCORRECT_OPTION = 1825 +ER_FK_DUP_NAME = 1826 +ER_PASSWORD_FORMAT = 1827 +ER_FK_COLUMN_CANNOT_DROP = 1828 +ER_FK_COLUMN_CANNOT_DROP_CHILD = 1829 +ER_FK_COLUMN_NOT_NULL = 1830 +ER_DUP_INDEX = 1831 +ER_FK_COLUMN_CANNOT_CHANGE = 1832 +ER_FK_COLUMN_CANNOT_CHANGE_CHILD = 1833 +OBSOLETE_ER_UNUSED5 = 1834 +ER_MALFORMED_PACKET = 1835 +ER_READ_ONLY_MODE = 1836 +ER_GTID_NEXT_TYPE_UNDEFINED_GTID = 1837 +ER_VARIABLE_NOT_SETTABLE_IN_SP = 1838 +OBSOLETE_ER_CANT_SET_GTID_PURGED_WHEN_GTID_MODE_IS_OFF = 1839 +ER_CANT_SET_GTID_PURGED_WHEN_GTID_EXECUTED_IS_NOT_EMPTY = 1840 +ER_CANT_SET_GTID_PURGED_WHEN_OWNED_GTIDS_IS_NOT_EMPTY = 1841 +ER_GTID_PURGED_WAS_CHANGED = 1842 +ER_GTID_EXECUTED_WAS_CHANGED = 1843 +ER_BINLOG_STMT_MODE_AND_NO_REPL_TABLES = 1844 +ER_ALTER_OPERATION_NOT_SUPPORTED = 1845 +ER_ALTER_OPERATION_NOT_SUPPORTED_REASON = 1846 +ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COPY = 1847 +ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_PARTITION = 1848 +ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_RENAME = 1849 +ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COLUMN_TYPE = 1850 +ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_CHECK = 1851 +OBSOLETE_ER_UNUSED6 = 1852 +ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOPK = 1853 +ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_AUTOINC = 1854 +ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_HIDDEN_FTS = 1855 +ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS = 1856 +ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS = 1857 +OBSOLETE_ER_SQL_REPLICA_SKIP_COUNTER_NOT_SETTABLE_IN_GTID_MODE = 1858 +ER_DUP_UNKNOWN_IN_INDEX = 1859 +ER_IDENT_CAUSES_TOO_LONG_PATH = 1860 +ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL = 1861 +ER_MUST_CHANGE_PASSWORD_LOGIN = 1862 +ER_ROW_IN_WRONG_PARTITION = 1863 +ER_MTS_EVENT_BIGGER_PENDING_JOBS_SIZE_MAX = 1864 +OBSOLETE_ER_INNODB_NO_FT_USES_PARSER = 1865 +ER_BINLOG_LOGICAL_CORRUPTION = 1866 +ER_WARN_PURGE_LOG_IN_USE = 1867 +ER_WARN_PURGE_LOG_IS_ACTIVE = 1868 +ER_AUTO_INCREMENT_CONFLICT = 1869 +WARN_ON_BLOCKHOLE_IN_RBR = 1870 +ER_SLAVE_MI_INIT_REPOSITORY = 1871 +ER_SLAVE_RLI_INIT_REPOSITORY = 1872 +ER_ACCESS_DENIED_CHANGE_USER_ERROR = 1873 +ER_INNODB_READ_ONLY = 1874 +ER_STOP_SLAVE_SQL_THREAD_TIMEOUT = 1875 +ER_STOP_SLAVE_IO_THREAD_TIMEOUT = 1876 +ER_TABLE_CORRUPT = 1877 +ER_TEMP_FILE_WRITE_FAILURE = 1878 +ER_INNODB_FT_AUX_NOT_HEX_ID = 1879 +ER_OLD_TEMPORALS_UPGRADED = 1880 +ER_INNODB_FORCED_RECOVERY = 1881 +ER_AES_INVALID_IV = 1882 +ER_PLUGIN_CANNOT_BE_UNINSTALLED = 1883 +ER_GTID_UNSAFE_BINLOG_SPLITTABLE_STATEMENT_AND_ASSIGNED_GTID = 1884 +ER_SLAVE_HAS_MORE_GTIDS_THAN_MASTER = 1885 +ER_MISSING_KEY = 1886 +WARN_NAMED_PIPE_ACCESS_EVERYONE = 1887 +ER_FILE_CORRUPT = 3000 +ER_ERROR_ON_MASTER = 3001 +OBSOLETE_ER_INCONSISTENT_ERROR = 3002 +ER_STORAGE_ENGINE_NOT_LOADED = 3003 +ER_GET_STACKED_DA_WITHOUT_ACTIVE_HANDLER = 3004 +ER_WARN_LEGACY_SYNTAX_CONVERTED = 3005 +ER_BINLOG_UNSAFE_FULLTEXT_PLUGIN = 3006 +ER_CANNOT_DISCARD_TEMPORARY_TABLE = 3007 +ER_FK_DEPTH_EXCEEDED = 3008 +ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE_V2 = 3009 +ER_WARN_TRIGGER_DOESNT_HAVE_CREATED = 3010 +ER_REFERENCED_TRG_DOES_NOT_EXIST = 3011 +ER_EXPLAIN_NOT_SUPPORTED = 3012 +ER_INVALID_FIELD_SIZE = 3013 +ER_MISSING_HA_CREATE_OPTION = 3014 +ER_ENGINE_OUT_OF_MEMORY = 3015 +ER_PASSWORD_EXPIRE_ANONYMOUS_USER = 3016 +ER_SLAVE_SQL_THREAD_MUST_STOP = 3017 +ER_NO_FT_MATERIALIZED_SUBQUERY = 3018 +ER_INNODB_UNDO_LOG_FULL = 3019 +ER_INVALID_ARGUMENT_FOR_LOGARITHM = 3020 +ER_SLAVE_CHANNEL_IO_THREAD_MUST_STOP = 3021 +ER_WARN_OPEN_TEMP_TABLES_MUST_BE_ZERO = 3022 +ER_WARN_ONLY_MASTER_LOG_FILE_NO_POS = 3023 +ER_QUERY_TIMEOUT = 3024 +ER_NON_RO_SELECT_DISABLE_TIMER = 3025 +ER_DUP_LIST_ENTRY = 3026 +OBSOLETE_ER_SQL_MODE_NO_EFFECT = 3027 +ER_AGGREGATE_ORDER_FOR_UNION = 3028 +ER_AGGREGATE_ORDER_NON_AGG_QUERY = 3029 +ER_SLAVE_WORKER_STOPPED_PREVIOUS_THD_ERROR = 3030 +ER_DONT_SUPPORT_REPLICA_PRESERVE_COMMIT_ORDER = 3031 +ER_SERVER_OFFLINE_MODE = 3032 +ER_GIS_DIFFERENT_SRIDS = 3033 +ER_GIS_UNSUPPORTED_ARGUMENT = 3034 +ER_GIS_UNKNOWN_ERROR = 3035 +ER_GIS_UNKNOWN_EXCEPTION = 3036 +ER_GIS_INVALID_DATA = 3037 +ER_BOOST_GEOMETRY_EMPTY_INPUT_EXCEPTION = 3038 +ER_BOOST_GEOMETRY_CENTROID_EXCEPTION = 3039 +ER_BOOST_GEOMETRY_OVERLAY_INVALID_INPUT_EXCEPTION = 3040 +ER_BOOST_GEOMETRY_TURN_INFO_EXCEPTION = 3041 +ER_BOOST_GEOMETRY_SELF_INTERSECTION_POINT_EXCEPTION = 3042 +ER_BOOST_GEOMETRY_UNKNOWN_EXCEPTION = 3043 +ER_STD_BAD_ALLOC_ERROR = 3044 +ER_STD_DOMAIN_ERROR = 3045 +ER_STD_LENGTH_ERROR = 3046 +ER_STD_INVALID_ARGUMENT = 3047 +ER_STD_OUT_OF_RANGE_ERROR = 3048 +ER_STD_OVERFLOW_ERROR = 3049 +ER_STD_RANGE_ERROR = 3050 +ER_STD_UNDERFLOW_ERROR = 3051 +ER_STD_LOGIC_ERROR = 3052 +ER_STD_RUNTIME_ERROR = 3053 +ER_STD_UNKNOWN_EXCEPTION = 3054 +ER_GIS_DATA_WRONG_ENDIANESS = 3055 +ER_CHANGE_MASTER_PASSWORD_LENGTH = 3056 +ER_USER_LOCK_WRONG_NAME = 3057 +ER_USER_LOCK_DEADLOCK = 3058 +ER_REPLACE_INACCESSIBLE_ROWS = 3059 +ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_GIS = 3060 +ER_ILLEGAL_USER_VAR = 3061 +ER_GTID_MODE_OFF = 3062 +OBSOLETE_ER_UNSUPPORTED_BY_REPLICATION_THREAD = 3063 +ER_INCORRECT_TYPE = 3064 +ER_FIELD_IN_ORDER_NOT_SELECT = 3065 +ER_AGGREGATE_IN_ORDER_NOT_SELECT = 3066 +ER_INVALID_RPL_WILD_TABLE_FILTER_PATTERN = 3067 +ER_NET_OK_PACKET_TOO_LARGE = 3068 +ER_INVALID_JSON_DATA = 3069 +ER_INVALID_GEOJSON_MISSING_MEMBER = 3070 +ER_INVALID_GEOJSON_WRONG_TYPE = 3071 +ER_INVALID_GEOJSON_UNSPECIFIED = 3072 +ER_DIMENSION_UNSUPPORTED = 3073 +ER_SLAVE_CHANNEL_DOES_NOT_EXIST = 3074 +OBSOLETE_ER_SLAVE_MULTIPLE_CHANNELS_HOST_PORT = 3075 +ER_SLAVE_CHANNEL_NAME_INVALID_OR_TOO_LONG = 3076 +ER_SLAVE_NEW_CHANNEL_WRONG_REPOSITORY = 3077 +OBSOLETE_ER_SLAVE_CHANNEL_DELETE = 3078 +ER_SLAVE_MULTIPLE_CHANNELS_CMD = 3079 +ER_SLAVE_MAX_CHANNELS_EXCEEDED = 3080 +ER_SLAVE_CHANNEL_MUST_STOP = 3081 +ER_SLAVE_CHANNEL_NOT_RUNNING = 3082 +ER_SLAVE_CHANNEL_WAS_RUNNING = 3083 +ER_SLAVE_CHANNEL_WAS_NOT_RUNNING = 3084 +ER_SLAVE_CHANNEL_SQL_THREAD_MUST_STOP = 3085 +ER_SLAVE_CHANNEL_SQL_SKIP_COUNTER = 3086 +ER_WRONG_FIELD_WITH_GROUP_V2 = 3087 +ER_MIX_OF_GROUP_FUNC_AND_FIELDS_V2 = 3088 +ER_WARN_DEPRECATED_SYSVAR_UPDATE = 3089 +ER_WARN_DEPRECATED_SQLMODE = 3090 +ER_CANNOT_LOG_PARTIAL_DROP_DATABASE_WITH_GTID = 3091 +ER_GROUP_REPLICATION_CONFIGURATION = 3092 +ER_GROUP_REPLICATION_RUNNING = 3093 +ER_GROUP_REPLICATION_APPLIER_INIT_ERROR = 3094 +ER_GROUP_REPLICATION_STOP_APPLIER_THREAD_TIMEOUT = 3095 +ER_GROUP_REPLICATION_COMMUNICATION_LAYER_SESSION_ERROR = 3096 +ER_GROUP_REPLICATION_COMMUNICATION_LAYER_JOIN_ERROR = 3097 +ER_BEFORE_DML_VALIDATION_ERROR = 3098 +ER_PREVENTS_VARIABLE_WITHOUT_RBR = 3099 +ER_RUN_HOOK_ERROR = 3100 +ER_TRANSACTION_ROLLBACK_DURING_COMMIT = 3101 +ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED = 3102 +ER_UNSUPPORTED_ALTER_INPLACE_ON_VIRTUAL_COLUMN = 3103 +ER_WRONG_FK_OPTION_FOR_GENERATED_COLUMN = 3104 +ER_NON_DEFAULT_VALUE_FOR_GENERATED_COLUMN = 3105 +ER_UNSUPPORTED_ACTION_ON_GENERATED_COLUMN = 3106 +ER_GENERATED_COLUMN_NON_PRIOR = 3107 +ER_DEPENDENT_BY_GENERATED_COLUMN = 3108 +ER_GENERATED_COLUMN_REF_AUTO_INC = 3109 +ER_FEATURE_NOT_AVAILABLE = 3110 +ER_CANT_SET_GTID_MODE = 3111 +ER_CANT_USE_AUTO_POSITION_WITH_GTID_MODE_OFF = 3112 +OBSOLETE_ER_CANT_REPLICATE_ANONYMOUS_WITH_AUTO_POSITION = 3113 +OBSOLETE_ER_CANT_REPLICATE_ANONYMOUS_WITH_GTID_MODE_ON = 3114 +OBSOLETE_ER_CANT_REPLICATE_GTID_WITH_GTID_MODE_OFF = 3115 +ER_CANT_ENFORCE_GTID_CONSISTENCY_WITH_ONGOING_GTID_VIOLATING_TX = 3116 +ER_ENFORCE_GTID_CONSISTENCY_WARN_WITH_ONGOING_GTID_VIOLATING_TX = 3117 +ER_ACCOUNT_HAS_BEEN_LOCKED = 3118 +ER_WRONG_TABLESPACE_NAME = 3119 +ER_TABLESPACE_IS_NOT_EMPTY = 3120 +ER_WRONG_FILE_NAME = 3121 +ER_BOOST_GEOMETRY_INCONSISTENT_TURNS_EXCEPTION = 3122 +ER_WARN_OPTIMIZER_HINT_SYNTAX_ERROR = 3123 +ER_WARN_BAD_MAX_EXECUTION_TIME = 3124 +ER_WARN_UNSUPPORTED_MAX_EXECUTION_TIME = 3125 +ER_WARN_CONFLICTING_HINT = 3126 +ER_WARN_UNKNOWN_QB_NAME = 3127 +ER_UNRESOLVED_HINT_NAME = 3128 +ER_WARN_ON_MODIFYING_GTID_EXECUTED_TABLE = 3129 +ER_PLUGGABLE_PROTOCOL_COMMAND_NOT_SUPPORTED = 3130 +ER_LOCKING_SERVICE_WRONG_NAME = 3131 +ER_LOCKING_SERVICE_DEADLOCK = 3132 +ER_LOCKING_SERVICE_TIMEOUT = 3133 +ER_GIS_MAX_POINTS_IN_GEOMETRY_OVERFLOWED = 3134 +ER_SQL_MODE_MERGED = 3135 +ER_VTOKEN_PLUGIN_TOKEN_MISMATCH = 3136 +ER_VTOKEN_PLUGIN_TOKEN_NOT_FOUND = 3137 +ER_CANT_SET_VARIABLE_WHEN_OWNING_GTID = 3138 +ER_SLAVE_CHANNEL_OPERATION_NOT_ALLOWED = 3139 +ER_INVALID_JSON_TEXT = 3140 +ER_INVALID_JSON_TEXT_IN_PARAM = 3141 +ER_INVALID_JSON_BINARY_DATA = 3142 +ER_INVALID_JSON_PATH = 3143 +ER_INVALID_JSON_CHARSET = 3144 +ER_INVALID_JSON_CHARSET_IN_FUNCTION = 3145 +ER_INVALID_TYPE_FOR_JSON = 3146 +ER_INVALID_CAST_TO_JSON = 3147 +ER_INVALID_JSON_PATH_CHARSET = 3148 +ER_INVALID_JSON_PATH_WILDCARD = 3149 +ER_JSON_VALUE_TOO_BIG = 3150 +ER_JSON_KEY_TOO_BIG = 3151 +ER_JSON_USED_AS_KEY = 3152 +ER_JSON_VACUOUS_PATH = 3153 +ER_JSON_BAD_ONE_OR_ALL_ARG = 3154 +ER_NUMERIC_JSON_VALUE_OUT_OF_RANGE = 3155 +ER_INVALID_JSON_VALUE_FOR_CAST = 3156 +ER_JSON_DOCUMENT_TOO_DEEP = 3157 +ER_JSON_DOCUMENT_NULL_KEY = 3158 +ER_SECURE_TRANSPORT_REQUIRED = 3159 +ER_NO_SECURE_TRANSPORTS_CONFIGURED = 3160 +ER_DISABLED_STORAGE_ENGINE = 3161 +ER_USER_DOES_NOT_EXIST = 3162 +ER_USER_ALREADY_EXISTS = 3163 +ER_AUDIT_API_ABORT = 3164 +ER_INVALID_JSON_PATH_ARRAY_CELL = 3165 +ER_BUFPOOL_RESIZE_INPROGRESS = 3166 +ER_FEATURE_DISABLED_SEE_DOC = 3167 +ER_SERVER_ISNT_AVAILABLE = 3168 +ER_SESSION_WAS_KILLED = 3169 +ER_CAPACITY_EXCEEDED = 3170 +ER_CAPACITY_EXCEEDED_IN_RANGE_OPTIMIZER = 3171 +OBSOLETE_ER_TABLE_NEEDS_UPG_PART = 3172 +ER_CANT_WAIT_FOR_EXECUTED_GTID_SET_WHILE_OWNING_A_GTID = 3173 +ER_CANNOT_ADD_FOREIGN_BASE_COL_VIRTUAL = 3174 +ER_CANNOT_CREATE_VIRTUAL_INDEX_CONSTRAINT = 3175 +ER_ERROR_ON_MODIFYING_GTID_EXECUTED_TABLE = 3176 +ER_LOCK_REFUSED_BY_ENGINE = 3177 +ER_UNSUPPORTED_ALTER_ONLINE_ON_VIRTUAL_COLUMN = 3178 +ER_MASTER_KEY_ROTATION_NOT_SUPPORTED_BY_SE = 3179 +OBSOLETE_ER_MASTER_KEY_ROTATION_ERROR_BY_SE = 3180 +ER_MASTER_KEY_ROTATION_BINLOG_FAILED = 3181 +ER_MASTER_KEY_ROTATION_SE_UNAVAILABLE = 3182 +ER_TABLESPACE_CANNOT_ENCRYPT = 3183 +ER_INVALID_ENCRYPTION_OPTION = 3184 +ER_CANNOT_FIND_KEY_IN_KEYRING = 3185 +ER_CAPACITY_EXCEEDED_IN_PARSER = 3186 +ER_UNSUPPORTED_ALTER_ENCRYPTION_INPLACE = 3187 +ER_KEYRING_UDF_KEYRING_SERVICE_ERROR = 3188 +ER_USER_COLUMN_OLD_LENGTH = 3189 +ER_CANT_RESET_MASTER = 3190 +ER_GROUP_REPLICATION_MAX_GROUP_SIZE = 3191 +ER_CANNOT_ADD_FOREIGN_BASE_COL_STORED = 3192 +ER_TABLE_REFERENCED = 3193 +OBSOLETE_ER_PARTITION_ENGINE_DEPRECATED_FOR_TABLE = 3194 +OBSOLETE_ER_WARN_USING_GEOMFROMWKB_TO_SET_SRID_ZERO = 3195 +OBSOLETE_ER_WARN_USING_GEOMFROMWKB_TO_SET_SRID = 3196 +ER_XA_RETRY = 3197 +ER_KEYRING_AWS_UDF_AWS_KMS_ERROR = 3198 +ER_BINLOG_UNSAFE_XA = 3199 +ER_UDF_ERROR = 3200 +ER_KEYRING_MIGRATION_FAILURE = 3201 +ER_KEYRING_ACCESS_DENIED_ERROR = 3202 +ER_KEYRING_MIGRATION_STATUS = 3203 +OBSOLETE_ER_PLUGIN_FAILED_TO_OPEN_TABLES = 3204 +OBSOLETE_ER_PLUGIN_FAILED_TO_OPEN_TABLE = 3205 +OBSOLETE_ER_AUDIT_LOG_NO_KEYRING_PLUGIN_INSTALLED = 3206 +OBSOLETE_ER_AUDIT_LOG_ENCRYPTION_PASSWORD_HAS_NOT_BEEN_SET = 3207 +OBSOLETE_ER_AUDIT_LOG_COULD_NOT_CREATE_AES_KEY = 3208 +OBSOLETE_ER_AUDIT_LOG_ENCRYPTION_PASSWORD_CANNOT_BE_FETCHED = 3209 +OBSOLETE_ER_AUDIT_LOG_JSON_FILTERING_NOT_ENABLED = 3210 +OBSOLETE_ER_AUDIT_LOG_UDF_INSUFFICIENT_PRIVILEGE = 3211 +OBSOLETE_ER_AUDIT_LOG_SUPER_PRIVILEGE_REQUIRED = 3212 +OBSOLETE_ER_COULD_NOT_REINITIALIZE_AUDIT_LOG_FILTERS = 3213 +OBSOLETE_ER_AUDIT_LOG_UDF_INVALID_ARGUMENT_TYPE = 3214 +OBSOLETE_ER_AUDIT_LOG_UDF_INVALID_ARGUMENT_COUNT = 3215 +OBSOLETE_ER_AUDIT_LOG_HAS_NOT_BEEN_INSTALLED = 3216 +OBSOLETE_ER_AUDIT_LOG_UDF_READ_INVALID_MAX_ARRAY_LENGTH_ARG_TYPE = 3217 +ER_AUDIT_LOG_UDF_READ_INVALID_MAX_ARRAY_LENGTH_ARG_VALUE = 3218 +OBSOLETE_ER_AUDIT_LOG_JSON_FILTER_PARSING_ERROR = 3219 +OBSOLETE_ER_AUDIT_LOG_JSON_FILTER_NAME_CANNOT_BE_EMPTY = 3220 +OBSOLETE_ER_AUDIT_LOG_JSON_USER_NAME_CANNOT_BE_EMPTY = 3221 +OBSOLETE_ER_AUDIT_LOG_JSON_FILTER_DOES_NOT_EXISTS = 3222 +OBSOLETE_ER_AUDIT_LOG_USER_FIRST_CHARACTER_MUST_BE_ALPHANUMERIC = 3223 +OBSOLETE_ER_AUDIT_LOG_USER_NAME_INVALID_CHARACTER = 3224 +OBSOLETE_ER_AUDIT_LOG_HOST_NAME_INVALID_CHARACTER = 3225 +OBSOLETE_ER_XA_REPLICATION_FILTERS = 3226 +OBSOLETE_ER_CANT_OPEN_ERROR_LOG = 3227 +OBSOLETE_ER_GROUPING_ON_TIMESTAMP_IN_DST = 3228 +OBSOLETE_ER_CANT_START_SERVER_NAMED_PIPE = 3229 +ER_WRITE_SET_EXCEEDS_LIMIT = 3230 +ER_UNSUPPORT_COMPRESSED_TEMPORARY_TABLE = 3500 +ER_ACL_OPERATION_FAILED = 3501 +ER_UNSUPPORTED_INDEX_ALGORITHM = 3502 +ER_NO_SUCH_DB = 3503 +ER_TOO_BIG_ENUM = 3504 +ER_TOO_LONG_SET_ENUM_VALUE = 3505 +ER_INVALID_DD_OBJECT = 3506 +ER_UPDATING_DD_TABLE = 3507 +ER_INVALID_DD_OBJECT_ID = 3508 +ER_INVALID_DD_OBJECT_NAME = 3509 +ER_TABLESPACE_MISSING_WITH_NAME = 3510 +ER_TOO_LONG_ROUTINE_COMMENT = 3511 +ER_SP_LOAD_FAILED = 3512 +ER_INVALID_BITWISE_OPERANDS_SIZE = 3513 +ER_INVALID_BITWISE_AGGREGATE_OPERANDS_SIZE = 3514 +ER_WARN_UNSUPPORTED_HINT = 3515 +ER_UNEXPECTED_GEOMETRY_TYPE = 3516 +ER_SRS_PARSE_ERROR = 3517 +ER_SRS_PROJ_PARAMETER_MISSING = 3518 +ER_WARN_SRS_NOT_FOUND = 3519 +ER_SRS_NOT_CARTESIAN = 3520 +ER_SRS_NOT_CARTESIAN_UNDEFINED = 3521 +ER_PK_INDEX_CANT_BE_INVISIBLE = 3522 +ER_UNKNOWN_AUTHID = 3523 +ER_FAILED_ROLE_GRANT = 3524 +ER_OPEN_ROLE_TABLES = 3525 +ER_FAILED_DEFAULT_ROLES = 3526 +ER_COMPONENTS_NO_SCHEME = 3527 +ER_COMPONENTS_NO_SCHEME_SERVICE = 3528 +ER_COMPONENTS_CANT_LOAD = 3529 +ER_ROLE_NOT_GRANTED = 3530 +ER_FAILED_REVOKE_ROLE = 3531 +ER_RENAME_ROLE = 3532 +ER_COMPONENTS_CANT_ACQUIRE_SERVICE_IMPLEMENTATION = 3533 +ER_COMPONENTS_CANT_SATISFY_DEPENDENCY = 3534 +ER_COMPONENTS_LOAD_CANT_REGISTER_SERVICE_IMPLEMENTATION = 3535 +ER_COMPONENTS_LOAD_CANT_INITIALIZE = 3536 +ER_COMPONENTS_UNLOAD_NOT_LOADED = 3537 +ER_COMPONENTS_UNLOAD_CANT_DEINITIALIZE = 3538 +ER_COMPONENTS_CANT_RELEASE_SERVICE = 3539 +ER_COMPONENTS_UNLOAD_CANT_UNREGISTER_SERVICE = 3540 +ER_COMPONENTS_CANT_UNLOAD = 3541 +ER_WARN_UNLOAD_THE_NOT_PERSISTED = 3542 +ER_COMPONENT_TABLE_INCORRECT = 3543 +ER_COMPONENT_MANIPULATE_ROW_FAILED = 3544 +ER_COMPONENTS_UNLOAD_DUPLICATE_IN_GROUP = 3545 +ER_CANT_SET_GTID_PURGED_DUE_SETS_CONSTRAINTS = 3546 +ER_CANNOT_LOCK_USER_MANAGEMENT_CACHES = 3547 +ER_SRS_NOT_FOUND = 3548 +ER_VARIABLE_NOT_PERSISTED = 3549 +ER_IS_QUERY_INVALID_CLAUSE = 3550 +ER_UNABLE_TO_STORE_STATISTICS = 3551 +ER_NO_SYSTEM_SCHEMA_ACCESS = 3552 +ER_NO_SYSTEM_TABLESPACE_ACCESS = 3553 +ER_NO_SYSTEM_TABLE_ACCESS = 3554 +ER_NO_SYSTEM_TABLE_ACCESS_FOR_DICTIONARY_TABLE = 3555 +ER_NO_SYSTEM_TABLE_ACCESS_FOR_SYSTEM_TABLE = 3556 +ER_NO_SYSTEM_TABLE_ACCESS_FOR_TABLE = 3557 +ER_INVALID_OPTION_KEY = 3558 +ER_INVALID_OPTION_VALUE = 3559 +ER_INVALID_OPTION_KEY_VALUE_PAIR = 3560 +ER_INVALID_OPTION_START_CHARACTER = 3561 +ER_INVALID_OPTION_END_CHARACTER = 3562 +ER_INVALID_OPTION_CHARACTERS = 3563 +ER_DUPLICATE_OPTION_KEY = 3564 +ER_WARN_SRS_NOT_FOUND_AXIS_ORDER = 3565 +ER_NO_ACCESS_TO_NATIVE_FCT = 3566 +ER_RESET_MASTER_TO_VALUE_OUT_OF_RANGE = 3567 +ER_UNRESOLVED_TABLE_LOCK = 3568 +ER_DUPLICATE_TABLE_LOCK = 3569 +ER_BINLOG_UNSAFE_SKIP_LOCKED = 3570 +ER_BINLOG_UNSAFE_NOWAIT = 3571 +ER_LOCK_NOWAIT = 3572 +ER_CTE_RECURSIVE_REQUIRES_UNION = 3573 +ER_CTE_RECURSIVE_REQUIRES_NONRECURSIVE_FIRST = 3574 +ER_CTE_RECURSIVE_FORBIDS_AGGREGATION = 3575 +ER_CTE_RECURSIVE_FORBIDDEN_JOIN_ORDER = 3576 +ER_CTE_RECURSIVE_REQUIRES_SINGLE_REFERENCE = 3577 +ER_SWITCH_TMP_ENGINE = 3578 +ER_WINDOW_NO_SUCH_WINDOW = 3579 +ER_WINDOW_CIRCULARITY_IN_WINDOW_GRAPH = 3580 +ER_WINDOW_NO_CHILD_PARTITIONING = 3581 +ER_WINDOW_NO_INHERIT_FRAME = 3582 +ER_WINDOW_NO_REDEFINE_ORDER_BY = 3583 +ER_WINDOW_FRAME_START_ILLEGAL = 3584 +ER_WINDOW_FRAME_END_ILLEGAL = 3585 +ER_WINDOW_FRAME_ILLEGAL = 3586 +ER_WINDOW_RANGE_FRAME_ORDER_TYPE = 3587 +ER_WINDOW_RANGE_FRAME_TEMPORAL_TYPE = 3588 +ER_WINDOW_RANGE_FRAME_NUMERIC_TYPE = 3589 +ER_WINDOW_RANGE_BOUND_NOT_CONSTANT = 3590 +ER_WINDOW_DUPLICATE_NAME = 3591 +ER_WINDOW_ILLEGAL_ORDER_BY = 3592 +ER_WINDOW_INVALID_WINDOW_FUNC_USE = 3593 +ER_WINDOW_INVALID_WINDOW_FUNC_ALIAS_USE = 3594 +ER_WINDOW_NESTED_WINDOW_FUNC_USE_IN_WINDOW_SPEC = 3595 +ER_WINDOW_ROWS_INTERVAL_USE = 3596 +ER_WINDOW_NO_GROUP_ORDER_UNUSED = 3597 +ER_WINDOW_EXPLAIN_JSON = 3598 +ER_WINDOW_FUNCTION_IGNORES_FRAME = 3599 +ER_WL9236_NOW_UNUSED = 3600 +ER_INVALID_NO_OF_ARGS = 3601 +ER_FIELD_IN_GROUPING_NOT_GROUP_BY = 3602 +ER_TOO_LONG_TABLESPACE_COMMENT = 3603 +ER_ENGINE_CANT_DROP_TABLE = 3604 +ER_ENGINE_CANT_DROP_MISSING_TABLE = 3605 +ER_TABLESPACE_DUP_FILENAME = 3606 +ER_DB_DROP_RMDIR2 = 3607 +ER_IMP_NO_FILES_MATCHED = 3608 +ER_IMP_SCHEMA_DOES_NOT_EXIST = 3609 +ER_IMP_TABLE_ALREADY_EXISTS = 3610 +ER_IMP_INCOMPATIBLE_MYSQLD_VERSION = 3611 +ER_IMP_INCOMPATIBLE_DD_VERSION = 3612 +ER_IMP_INCOMPATIBLE_SDI_VERSION = 3613 +ER_WARN_INVALID_HINT = 3614 +ER_VAR_DOES_NOT_EXIST = 3615 +ER_LONGITUDE_OUT_OF_RANGE = 3616 +ER_LATITUDE_OUT_OF_RANGE = 3617 +ER_NOT_IMPLEMENTED_FOR_GEOGRAPHIC_SRS = 3618 +ER_ILLEGAL_PRIVILEGE_LEVEL = 3619 +ER_NO_SYSTEM_VIEW_ACCESS = 3620 +ER_COMPONENT_FILTER_FLABBERGASTED = 3621 +ER_PART_EXPR_TOO_LONG = 3622 +ER_UDF_DROP_DYNAMICALLY_REGISTERED = 3623 +ER_UNABLE_TO_STORE_COLUMN_STATISTICS = 3624 +ER_UNABLE_TO_UPDATE_COLUMN_STATISTICS = 3625 +ER_UNABLE_TO_DROP_COLUMN_STATISTICS = 3626 +ER_UNABLE_TO_BUILD_HISTOGRAM = 3627 +ER_MANDATORY_ROLE = 3628 +ER_MISSING_TABLESPACE_FILE = 3629 +ER_PERSIST_ONLY_ACCESS_DENIED_ERROR = 3630 +ER_CMD_NEED_SUPER = 3631 +ER_PATH_IN_DATADIR = 3632 +ER_CLONE_DDL_IN_PROGRESS = 3633 +ER_CLONE_TOO_MANY_CONCURRENT_CLONES = 3634 +ER_APPLIER_LOG_EVENT_VALIDATION_ERROR = 3635 +ER_CTE_MAX_RECURSION_DEPTH = 3636 +ER_NOT_HINT_UPDATABLE_VARIABLE = 3637 +ER_CREDENTIALS_CONTRADICT_TO_HISTORY = 3638 +ER_WARNING_PASSWORD_HISTORY_CLAUSES_VOID = 3639 +ER_CLIENT_DOES_NOT_SUPPORT = 3640 +ER_I_S_SKIPPED_TABLESPACE = 3641 +ER_TABLESPACE_ENGINE_MISMATCH = 3642 +ER_WRONG_SRID_FOR_COLUMN = 3643 +ER_CANNOT_ALTER_SRID_DUE_TO_INDEX = 3644 +ER_WARN_BINLOG_PARTIAL_UPDATES_DISABLED = 3645 +ER_WARN_BINLOG_V1_ROW_EVENTS_DISABLED = 3646 +ER_WARN_BINLOG_PARTIAL_UPDATES_SUGGESTS_PARTIAL_IMAGES = 3647 +ER_COULD_NOT_APPLY_JSON_DIFF = 3648 +ER_CORRUPTED_JSON_DIFF = 3649 +ER_RESOURCE_GROUP_EXISTS = 3650 +ER_RESOURCE_GROUP_NOT_EXISTS = 3651 +ER_INVALID_VCPU_ID = 3652 +ER_INVALID_VCPU_RANGE = 3653 +ER_INVALID_THREAD_PRIORITY = 3654 +ER_DISALLOWED_OPERATION = 3655 +ER_RESOURCE_GROUP_BUSY = 3656 +ER_RESOURCE_GROUP_DISABLED = 3657 +ER_FEATURE_UNSUPPORTED = 3658 +ER_ATTRIBUTE_IGNORED = 3659 +ER_INVALID_THREAD_ID = 3660 +ER_RESOURCE_GROUP_BIND_FAILED = 3661 +ER_INVALID_USE_OF_FORCE_OPTION = 3662 +ER_GROUP_REPLICATION_COMMAND_FAILURE = 3663 +ER_SDI_OPERATION_FAILED = 3664 +ER_MISSING_JSON_TABLE_VALUE = 3665 +ER_WRONG_JSON_TABLE_VALUE = 3666 +ER_TF_MUST_HAVE_ALIAS = 3667 +ER_TF_FORBIDDEN_JOIN_TYPE = 3668 +ER_JT_VALUE_OUT_OF_RANGE = 3669 +ER_JT_MAX_NESTED_PATH = 3670 +ER_PASSWORD_EXPIRATION_NOT_SUPPORTED_BY_AUTH_METHOD = 3671 +ER_INVALID_GEOJSON_CRS_NOT_TOP_LEVEL = 3672 +ER_BAD_NULL_ERROR_NOT_IGNORED = 3673 +WARN_USELESS_SPATIAL_INDEX = 3674 +ER_DISK_FULL_NOWAIT = 3675 +ER_PARSE_ERROR_IN_DIGEST_FN = 3676 +ER_UNDISCLOSED_PARSE_ERROR_IN_DIGEST_FN = 3677 +ER_SCHEMA_DIR_EXISTS = 3678 +ER_SCHEMA_DIR_MISSING = 3679 +ER_SCHEMA_DIR_CREATE_FAILED = 3680 +ER_SCHEMA_DIR_UNKNOWN = 3681 +ER_ONLY_IMPLEMENTED_FOR_SRID_0_AND_4326 = 3682 +ER_BINLOG_EXPIRE_LOG_DAYS_AND_SECS_USED_TOGETHER = 3683 +ER_REGEXP_BUFFER_OVERFLOW = 3684 +ER_REGEXP_ILLEGAL_ARGUMENT = 3685 +ER_REGEXP_INDEX_OUTOFBOUNDS_ERROR = 3686 +ER_REGEXP_INTERNAL_ERROR = 3687 +ER_REGEXP_RULE_SYNTAX = 3688 +ER_REGEXP_BAD_ESCAPE_SEQUENCE = 3689 +ER_REGEXP_UNIMPLEMENTED = 3690 +ER_REGEXP_MISMATCHED_PAREN = 3691 +ER_REGEXP_BAD_INTERVAL = 3692 +ER_REGEXP_MAX_LT_MIN = 3693 +ER_REGEXP_INVALID_BACK_REF = 3694 +ER_REGEXP_LOOK_BEHIND_LIMIT = 3695 +ER_REGEXP_MISSING_CLOSE_BRACKET = 3696 +ER_REGEXP_INVALID_RANGE = 3697 +ER_REGEXP_STACK_OVERFLOW = 3698 +ER_REGEXP_TIME_OUT = 3699 +ER_REGEXP_PATTERN_TOO_BIG = 3700 +ER_CANT_SET_ERROR_LOG_SERVICE = 3701 +ER_EMPTY_PIPELINE_FOR_ERROR_LOG_SERVICE = 3702 +ER_COMPONENT_FILTER_DIAGNOSTICS = 3703 +ER_NOT_IMPLEMENTED_FOR_CARTESIAN_SRS = 3704 +ER_NOT_IMPLEMENTED_FOR_PROJECTED_SRS = 3705 +ER_NONPOSITIVE_RADIUS = 3706 +ER_RESTART_SERVER_FAILED = 3707 +ER_SRS_MISSING_MANDATORY_ATTRIBUTE = 3708 +ER_SRS_MULTIPLE_ATTRIBUTE_DEFINITIONS = 3709 +ER_SRS_NAME_CANT_BE_EMPTY_OR_WHITESPACE = 3710 +ER_SRS_ORGANIZATION_CANT_BE_EMPTY_OR_WHITESPACE = 3711 +ER_SRS_ID_ALREADY_EXISTS = 3712 +ER_WARN_SRS_ID_ALREADY_EXISTS = 3713 +ER_CANT_MODIFY_SRID_0 = 3714 +ER_WARN_RESERVED_SRID_RANGE = 3715 +ER_CANT_MODIFY_SRS_USED_BY_COLUMN = 3716 +ER_SRS_INVALID_CHARACTER_IN_ATTRIBUTE = 3717 +ER_SRS_ATTRIBUTE_STRING_TOO_LONG = 3718 +ER_DEPRECATED_UTF8_ALIAS = 3719 +ER_DEPRECATED_NATIONAL = 3720 +ER_INVALID_DEFAULT_UTF8MB4_COLLATION = 3721 +ER_UNABLE_TO_COLLECT_LOG_STATUS = 3722 +ER_RESERVED_TABLESPACE_NAME = 3723 +ER_UNABLE_TO_SET_OPTION = 3724 +ER_SLAVE_POSSIBLY_DIVERGED_AFTER_DDL = 3725 +ER_SRS_NOT_GEOGRAPHIC = 3726 +ER_POLYGON_TOO_LARGE = 3727 +ER_SPATIAL_UNIQUE_INDEX = 3728 +ER_INDEX_TYPE_NOT_SUPPORTED_FOR_SPATIAL_INDEX = 3729 +ER_FK_CANNOT_DROP_PARENT = 3730 +ER_GEOMETRY_PARAM_LONGITUDE_OUT_OF_RANGE = 3731 +ER_GEOMETRY_PARAM_LATITUDE_OUT_OF_RANGE = 3732 +ER_FK_CANNOT_USE_VIRTUAL_COLUMN = 3733 +ER_FK_NO_COLUMN_PARENT = 3734 +ER_CANT_SET_ERROR_SUPPRESSION_LIST = 3735 +ER_SRS_GEOGCS_INVALID_AXES = 3736 +ER_SRS_INVALID_SEMI_MAJOR_AXIS = 3737 +ER_SRS_INVALID_INVERSE_FLATTENING = 3738 +ER_SRS_INVALID_ANGULAR_UNIT = 3739 +ER_SRS_INVALID_PRIME_MERIDIAN = 3740 +ER_TRANSFORM_SOURCE_SRS_NOT_SUPPORTED = 3741 +ER_TRANSFORM_TARGET_SRS_NOT_SUPPORTED = 3742 +ER_TRANSFORM_SOURCE_SRS_MISSING_TOWGS84 = 3743 +ER_TRANSFORM_TARGET_SRS_MISSING_TOWGS84 = 3744 +ER_TEMP_TABLE_PREVENTS_SWITCH_SESSION_BINLOG_FORMAT = 3745 +ER_TEMP_TABLE_PREVENTS_SWITCH_GLOBAL_BINLOG_FORMAT = 3746 +ER_RUNNING_APPLIER_PREVENTS_SWITCH_GLOBAL_BINLOG_FORMAT = 3747 +ER_CLIENT_GTID_UNSAFE_CREATE_DROP_TEMP_TABLE_IN_TRX_IN_SBR = 3748 +OBSOLETE_ER_XA_CANT_CREATE_MDL_BACKUP = 3749 +ER_TABLE_WITHOUT_PK = 3750 +ER_WARN_DATA_TRUNCATED_FUNCTIONAL_INDEX = 3751 +ER_WARN_DATA_OUT_OF_RANGE_FUNCTIONAL_INDEX = 3752 +ER_FUNCTIONAL_INDEX_ON_JSON_OR_GEOMETRY_FUNCTION = 3753 +ER_FUNCTIONAL_INDEX_REF_AUTO_INCREMENT = 3754 +ER_CANNOT_DROP_COLUMN_FUNCTIONAL_INDEX = 3755 +ER_FUNCTIONAL_INDEX_PRIMARY_KEY = 3756 +ER_FUNCTIONAL_INDEX_ON_LOB = 3757 +ER_FUNCTIONAL_INDEX_FUNCTION_IS_NOT_ALLOWED = 3758 +ER_FULLTEXT_FUNCTIONAL_INDEX = 3759 +ER_SPATIAL_FUNCTIONAL_INDEX = 3760 +ER_WRONG_KEY_COLUMN_FUNCTIONAL_INDEX = 3761 +ER_FUNCTIONAL_INDEX_ON_FIELD = 3762 +ER_GENERATED_COLUMN_NAMED_FUNCTION_IS_NOT_ALLOWED = 3763 +ER_GENERATED_COLUMN_ROW_VALUE = 3764 +ER_GENERATED_COLUMN_VARIABLES = 3765 +ER_DEPENDENT_BY_DEFAULT_GENERATED_VALUE = 3766 +ER_DEFAULT_VAL_GENERATED_NON_PRIOR = 3767 +ER_DEFAULT_VAL_GENERATED_REF_AUTO_INC = 3768 +ER_DEFAULT_VAL_GENERATED_FUNCTION_IS_NOT_ALLOWED = 3769 +ER_DEFAULT_VAL_GENERATED_NAMED_FUNCTION_IS_NOT_ALLOWED = 3770 +ER_DEFAULT_VAL_GENERATED_ROW_VALUE = 3771 +ER_DEFAULT_VAL_GENERATED_VARIABLES = 3772 +ER_DEFAULT_AS_VAL_GENERATED = 3773 +ER_UNSUPPORTED_ACTION_ON_DEFAULT_VAL_GENERATED = 3774 +ER_GTID_UNSAFE_ALTER_ADD_COL_WITH_DEFAULT_EXPRESSION = 3775 +ER_FK_CANNOT_CHANGE_ENGINE = 3776 +ER_WARN_DEPRECATED_USER_SET_EXPR = 3777 +ER_WARN_DEPRECATED_UTF8MB3_COLLATION = 3778 +ER_WARN_DEPRECATED_NESTED_COMMENT_SYNTAX = 3779 +ER_FK_INCOMPATIBLE_COLUMNS = 3780 +ER_GR_HOLD_WAIT_TIMEOUT = 3781 +ER_GR_HOLD_KILLED = 3782 +ER_GR_HOLD_MEMBER_STATUS_ERROR = 3783 +ER_RPL_ENCRYPTION_FAILED_TO_FETCH_KEY = 3784 +ER_RPL_ENCRYPTION_KEY_NOT_FOUND = 3785 +ER_RPL_ENCRYPTION_KEYRING_INVALID_KEY = 3786 +ER_RPL_ENCRYPTION_HEADER_ERROR = 3787 +ER_RPL_ENCRYPTION_FAILED_TO_ROTATE_LOGS = 3788 +ER_RPL_ENCRYPTION_KEY_EXISTS_UNEXPECTED = 3789 +ER_RPL_ENCRYPTION_FAILED_TO_GENERATE_KEY = 3790 +ER_RPL_ENCRYPTION_FAILED_TO_STORE_KEY = 3791 +ER_RPL_ENCRYPTION_FAILED_TO_REMOVE_KEY = 3792 +ER_RPL_ENCRYPTION_UNABLE_TO_CHANGE_OPTION = 3793 +ER_RPL_ENCRYPTION_MASTER_KEY_RECOVERY_FAILED = 3794 +ER_SLOW_LOG_MODE_IGNORED_WHEN_NOT_LOGGING_TO_FILE = 3795 +ER_GRP_TRX_CONSISTENCY_NOT_ALLOWED = 3796 +ER_GRP_TRX_CONSISTENCY_BEFORE = 3797 +ER_GRP_TRX_CONSISTENCY_AFTER_ON_TRX_BEGIN = 3798 +ER_GRP_TRX_CONSISTENCY_BEGIN_NOT_ALLOWED = 3799 +ER_FUNCTIONAL_INDEX_ROW_VALUE_IS_NOT_ALLOWED = 3800 +ER_RPL_ENCRYPTION_FAILED_TO_ENCRYPT = 3801 +ER_PAGE_TRACKING_NOT_STARTED = 3802 +ER_PAGE_TRACKING_RANGE_NOT_TRACKED = 3803 +ER_PAGE_TRACKING_CANNOT_PURGE = 3804 +ER_RPL_ENCRYPTION_CANNOT_ROTATE_BINLOG_MASTER_KEY = 3805 +ER_BINLOG_MASTER_KEY_RECOVERY_OUT_OF_COMBINATION = 3806 +ER_BINLOG_MASTER_KEY_ROTATION_FAIL_TO_OPERATE_KEY = 3807 +ER_BINLOG_MASTER_KEY_ROTATION_FAIL_TO_ROTATE_LOGS = 3808 +ER_BINLOG_MASTER_KEY_ROTATION_FAIL_TO_REENCRYPT_LOG = 3809 +ER_BINLOG_MASTER_KEY_ROTATION_FAIL_TO_CLEANUP_UNUSED_KEYS = 3810 +ER_BINLOG_MASTER_KEY_ROTATION_FAIL_TO_CLEANUP_AUX_KEY = 3811 +ER_NON_BOOLEAN_EXPR_FOR_CHECK_CONSTRAINT = 3812 +ER_COLUMN_CHECK_CONSTRAINT_REFERENCES_OTHER_COLUMN = 3813 +ER_CHECK_CONSTRAINT_NAMED_FUNCTION_IS_NOT_ALLOWED = 3814 +ER_CHECK_CONSTRAINT_FUNCTION_IS_NOT_ALLOWED = 3815 +ER_CHECK_CONSTRAINT_VARIABLES = 3816 +ER_CHECK_CONSTRAINT_ROW_VALUE = 3817 +ER_CHECK_CONSTRAINT_REFERS_AUTO_INCREMENT_COLUMN = 3818 +ER_CHECK_CONSTRAINT_VIOLATED = 3819 +ER_CHECK_CONSTRAINT_REFERS_UNKNOWN_COLUMN = 3820 +ER_CHECK_CONSTRAINT_NOT_FOUND = 3821 +ER_CHECK_CONSTRAINT_DUP_NAME = 3822 +ER_CHECK_CONSTRAINT_CLAUSE_USING_FK_REFER_ACTION_COLUMN = 3823 +WARN_UNENCRYPTED_TABLE_IN_ENCRYPTED_DB = 3824 +ER_INVALID_ENCRYPTION_REQUEST = 3825 +ER_CANNOT_SET_TABLE_ENCRYPTION = 3826 +ER_CANNOT_SET_DATABASE_ENCRYPTION = 3827 +ER_CANNOT_SET_TABLESPACE_ENCRYPTION = 3828 +ER_TABLESPACE_CANNOT_BE_ENCRYPTED = 3829 +ER_TABLESPACE_CANNOT_BE_DECRYPTED = 3830 +ER_TABLESPACE_TYPE_UNKNOWN = 3831 +ER_TARGET_TABLESPACE_UNENCRYPTED = 3832 +ER_CANNOT_USE_ENCRYPTION_CLAUSE = 3833 +ER_INVALID_MULTIPLE_CLAUSES = 3834 +ER_UNSUPPORTED_USE_OF_GRANT_AS = 3835 +ER_UKNOWN_AUTH_ID_OR_ACCESS_DENIED_FOR_GRANT_AS = 3836 +ER_DEPENDENT_BY_FUNCTIONAL_INDEX = 3837 +ER_PLUGIN_NOT_EARLY = 3838 +ER_INNODB_REDO_LOG_ARCHIVE_START_SUBDIR_PATH = 3839 +ER_INNODB_REDO_LOG_ARCHIVE_START_TIMEOUT = 3840 +ER_INNODB_REDO_LOG_ARCHIVE_DIRS_INVALID = 3841 +ER_INNODB_REDO_LOG_ARCHIVE_LABEL_NOT_FOUND = 3842 +ER_INNODB_REDO_LOG_ARCHIVE_DIR_EMPTY = 3843 +ER_INNODB_REDO_LOG_ARCHIVE_NO_SUCH_DIR = 3844 +ER_INNODB_REDO_LOG_ARCHIVE_DIR_CLASH = 3845 +ER_INNODB_REDO_LOG_ARCHIVE_DIR_PERMISSIONS = 3846 +ER_INNODB_REDO_LOG_ARCHIVE_FILE_CREATE = 3847 +ER_INNODB_REDO_LOG_ARCHIVE_ACTIVE = 3848 +ER_INNODB_REDO_LOG_ARCHIVE_INACTIVE = 3849 +ER_INNODB_REDO_LOG_ARCHIVE_FAILED = 3850 +ER_INNODB_REDO_LOG_ARCHIVE_SESSION = 3851 +ER_STD_REGEX_ERROR = 3852 +ER_INVALID_JSON_TYPE = 3853 +ER_CANNOT_CONVERT_STRING = 3854 +ER_DEPENDENT_BY_PARTITION_FUNC = 3855 +ER_WARN_DEPRECATED_FLOAT_AUTO_INCREMENT = 3856 +ER_RPL_CANT_STOP_SLAVE_WHILE_LOCKED_BACKUP = 3857 +ER_WARN_DEPRECATED_FLOAT_DIGITS = 3858 +ER_WARN_DEPRECATED_FLOAT_UNSIGNED = 3859 +ER_WARN_DEPRECATED_INTEGER_DISPLAY_WIDTH = 3860 +ER_WARN_DEPRECATED_ZEROFILL = 3861 +ER_CLONE_DONOR = 3862 +ER_CLONE_PROTOCOL = 3863 +ER_CLONE_DONOR_VERSION = 3864 +ER_CLONE_OS = 3865 +ER_CLONE_PLATFORM = 3866 +ER_CLONE_CHARSET = 3867 +ER_CLONE_CONFIG = 3868 +ER_CLONE_SYS_CONFIG = 3869 +ER_CLONE_PLUGIN_MATCH = 3870 +ER_CLONE_LOOPBACK = 3871 +ER_CLONE_ENCRYPTION = 3872 +ER_CLONE_DISK_SPACE = 3873 +ER_CLONE_IN_PROGRESS = 3874 +ER_CLONE_DISALLOWED = 3875 +ER_CANNOT_GRANT_ROLES_TO_ANONYMOUS_USER = 3876 +ER_SECONDARY_ENGINE_PLUGIN = 3877 +ER_SECOND_PASSWORD_CANNOT_BE_EMPTY = 3878 +ER_DB_ACCESS_DENIED = 3879 +ER_DA_AUTH_ID_WITH_SYSTEM_USER_PRIV_IN_MANDATORY_ROLES = 3880 +ER_DA_RPL_GTID_TABLE_CANNOT_OPEN = 3881 +ER_GEOMETRY_IN_UNKNOWN_LENGTH_UNIT = 3882 +ER_DA_PLUGIN_INSTALL_ERROR = 3883 +ER_NO_SESSION_TEMP = 3884 +ER_DA_UNKNOWN_ERROR_NUMBER = 3885 +ER_COLUMN_CHANGE_SIZE = 3886 +ER_REGEXP_INVALID_CAPTURE_GROUP_NAME = 3887 +ER_DA_SSL_LIBRARY_ERROR = 3888 +ER_SECONDARY_ENGINE = 3889 +ER_SECONDARY_ENGINE_DDL = 3890 +ER_INCORRECT_CURRENT_PASSWORD = 3891 +ER_MISSING_CURRENT_PASSWORD = 3892 +ER_CURRENT_PASSWORD_NOT_REQUIRED = 3893 +ER_PASSWORD_CANNOT_BE_RETAINED_ON_PLUGIN_CHANGE = 3894 +ER_CURRENT_PASSWORD_CANNOT_BE_RETAINED = 3895 +ER_PARTIAL_REVOKES_EXIST = 3896 +ER_CANNOT_GRANT_SYSTEM_PRIV_TO_MANDATORY_ROLE = 3897 +ER_XA_REPLICATION_FILTERS = 3898 +ER_UNSUPPORTED_SQL_MODE = 3899 +ER_REGEXP_INVALID_FLAG = 3900 +ER_PARTIAL_REVOKE_AND_DB_GRANT_BOTH_EXISTS = 3901 +ER_UNIT_NOT_FOUND = 3902 +ER_INVALID_JSON_VALUE_FOR_FUNC_INDEX = 3903 +ER_JSON_VALUE_OUT_OF_RANGE_FOR_FUNC_INDEX = 3904 +ER_EXCEEDED_MV_KEYS_NUM = 3905 +ER_EXCEEDED_MV_KEYS_SPACE = 3906 +ER_FUNCTIONAL_INDEX_DATA_IS_TOO_LONG = 3907 +ER_WRONG_MVI_VALUE = 3908 +ER_WARN_FUNC_INDEX_NOT_APPLICABLE = 3909 +ER_GRP_RPL_UDF_ERROR = 3910 +ER_UPDATE_GTID_PURGED_WITH_GR = 3911 +ER_GROUPING_ON_TIMESTAMP_IN_DST = 3912 +ER_TABLE_NAME_CAUSES_TOO_LONG_PATH = 3913 +ER_AUDIT_LOG_INSUFFICIENT_PRIVILEGE = 3914 +OBSOLETE_ER_AUDIT_LOG_PASSWORD_HAS_BEEN_COPIED = 3915 +ER_DA_GRP_RPL_STARTED_AUTO_REJOIN = 3916 +ER_SYSVAR_CHANGE_DURING_QUERY = 3917 +ER_GLOBSTAT_CHANGE_DURING_QUERY = 3918 +ER_GRP_RPL_MESSAGE_SERVICE_INIT_FAILURE = 3919 +ER_CHANGE_MASTER_WRONG_COMPRESSION_ALGORITHM_CLIENT = 3920 +ER_CHANGE_MASTER_WRONG_COMPRESSION_LEVEL_CLIENT = 3921 +ER_WRONG_COMPRESSION_ALGORITHM_CLIENT = 3922 +ER_WRONG_COMPRESSION_LEVEL_CLIENT = 3923 +ER_CHANGE_MASTER_WRONG_COMPRESSION_ALGORITHM_LIST_CLIENT = 3924 +ER_CLIENT_PRIVILEGE_CHECKS_USER_CANNOT_BE_ANONYMOUS = 3925 +ER_CLIENT_PRIVILEGE_CHECKS_USER_DOES_NOT_EXIST = 3926 +ER_CLIENT_PRIVILEGE_CHECKS_USER_CORRUPT = 3927 +ER_CLIENT_PRIVILEGE_CHECKS_USER_NEEDS_RPL_APPLIER_PRIV = 3928 +ER_WARN_DA_PRIVILEGE_NOT_REGISTERED = 3929 +ER_CLIENT_KEYRING_UDF_KEY_INVALID = 3930 +ER_CLIENT_KEYRING_UDF_KEY_TYPE_INVALID = 3931 +ER_CLIENT_KEYRING_UDF_KEY_TOO_LONG = 3932 +ER_CLIENT_KEYRING_UDF_KEY_TYPE_TOO_LONG = 3933 +ER_JSON_SCHEMA_VALIDATION_ERROR_WITH_DETAILED_REPORT = 3934 +ER_DA_UDF_INVALID_CHARSET_SPECIFIED = 3935 +ER_DA_UDF_INVALID_CHARSET = 3936 +ER_DA_UDF_INVALID_COLLATION = 3937 +ER_DA_UDF_INVALID_EXTENSION_ARGUMENT_TYPE = 3938 +ER_MULTIPLE_CONSTRAINTS_WITH_SAME_NAME = 3939 +ER_CONSTRAINT_NOT_FOUND = 3940 +ER_ALTER_CONSTRAINT_ENFORCEMENT_NOT_SUPPORTED = 3941 +ER_TABLE_VALUE_CONSTRUCTOR_MUST_HAVE_COLUMNS = 3942 +ER_TABLE_VALUE_CONSTRUCTOR_CANNOT_HAVE_DEFAULT = 3943 +ER_CLIENT_QUERY_FAILURE_INVALID_NON_ROW_FORMAT = 3944 +ER_REQUIRE_ROW_FORMAT_INVALID_VALUE = 3945 +ER_FAILED_TO_DETERMINE_IF_ROLE_IS_MANDATORY = 3946 +ER_FAILED_TO_FETCH_MANDATORY_ROLE_LIST = 3947 +ER_CLIENT_LOCAL_FILES_DISABLED = 3948 +ER_IMP_INCOMPATIBLE_CFG_VERSION = 3949 +ER_DA_OOM = 3950 +ER_DA_UDF_INVALID_ARGUMENT_TO_SET_CHARSET = 3951 +ER_DA_UDF_INVALID_RETURN_TYPE_TO_SET_CHARSET = 3952 +ER_MULTIPLE_INTO_CLAUSES = 3953 +ER_MISPLACED_INTO = 3954 +ER_USER_ACCESS_DENIED_FOR_USER_ACCOUNT_BLOCKED_BY_PASSWORD_LOCK = 3955 +ER_WARN_DEPRECATED_YEAR_UNSIGNED = 3956 +ER_CLONE_NETWORK_PACKET = 3957 +ER_SDI_OPERATION_FAILED_MISSING_RECORD = 3958 +ER_DEPENDENT_BY_CHECK_CONSTRAINT = 3959 +ER_GRP_OPERATION_NOT_ALLOWED_GR_MUST_STOP = 3960 +ER_WARN_DEPRECATED_JSON_TABLE_ON_ERROR_ON_EMPTY = 3961 +ER_WARN_DEPRECATED_INNER_INTO = 3962 +ER_WARN_DEPRECATED_VALUES_FUNCTION_ALWAYS_NULL = 3963 +ER_WARN_DEPRECATED_SQL_CALC_FOUND_ROWS = 3964 +ER_WARN_DEPRECATED_FOUND_ROWS = 3965 +ER_MISSING_JSON_VALUE = 3966 +ER_MULTIPLE_JSON_VALUES = 3967 +ER_HOSTNAME_TOO_LONG = 3968 +ER_WARN_CLIENT_DEPRECATED_PARTITION_PREFIX_KEY = 3969 +ER_GROUP_REPLICATION_USER_EMPTY_MSG = 3970 +ER_GROUP_REPLICATION_USER_MANDATORY_MSG = 3971 +ER_GROUP_REPLICATION_PASSWORD_LENGTH = 3972 +ER_SUBQUERY_TRANSFORM_REJECTED = 3973 +ER_DA_GRP_RPL_RECOVERY_ENDPOINT_FORMAT = 3974 +ER_DA_GRP_RPL_RECOVERY_ENDPOINT_INVALID = 3975 +ER_WRONG_VALUE_FOR_VAR_PLUS_ACTIONABLE_PART = 3976 +ER_STATEMENT_NOT_ALLOWED_AFTER_START_TRANSACTION = 3977 +ER_FOREIGN_KEY_WITH_ATOMIC_CREATE_SELECT = 3978 +ER_NOT_ALLOWED_WITH_START_TRANSACTION = 3979 +ER_INVALID_JSON_ATTRIBUTE = 3980 +ER_ENGINE_ATTRIBUTE_NOT_SUPPORTED = 3981 +ER_INVALID_USER_ATTRIBUTE_JSON = 3982 +ER_INNODB_REDO_DISABLED = 3983 +ER_INNODB_REDO_ARCHIVING_ENABLED = 3984 +ER_MDL_OUT_OF_RESOURCES = 3985 +ER_IMPLICIT_COMPARISON_FOR_JSON = 3986 +ER_FUNCTION_DOES_NOT_SUPPORT_CHARACTER_SET = 3987 +ER_IMPOSSIBLE_STRING_CONVERSION = 3988 +ER_SCHEMA_READ_ONLY = 3989 +ER_RPL_ASYNC_RECONNECT_GTID_MODE_OFF = 3990 +ER_RPL_ASYNC_RECONNECT_AUTO_POSITION_OFF = 3991 +ER_DISABLE_GTID_MODE_REQUIRES_ASYNC_RECONNECT_OFF = 3992 +ER_DISABLE_AUTO_POSITION_REQUIRES_ASYNC_RECONNECT_OFF = 3993 +ER_INVALID_PARAMETER_USE = 3994 +ER_CHARACTER_SET_MISMATCH = 3995 +ER_WARN_VAR_VALUE_CHANGE_NOT_SUPPORTED = 3996 +ER_INVALID_TIME_ZONE_INTERVAL = 3997 +ER_INVALID_CAST = 3998 +ER_HYPERGRAPH_NOT_SUPPORTED_YET = 3999 +ER_WARN_HYPERGRAPH_EXPERIMENTAL = 4000 +ER_DA_NO_ERROR_LOG_PARSER_CONFIGURED = 4001 +ER_DA_ERROR_LOG_TABLE_DISABLED = 4002 +ER_DA_ERROR_LOG_MULTIPLE_FILTERS = 4003 +ER_DA_CANT_OPEN_ERROR_LOG = 4004 +ER_USER_REFERENCED_AS_DEFINER = 4005 +ER_CANNOT_USER_REFERENCED_AS_DEFINER = 4006 +ER_REGEX_NUMBER_TOO_BIG = 4007 +ER_SPVAR_NONINTEGER_TYPE = 4008 +WARN_UNSUPPORTED_ACL_TABLES_READ = 4009 +ER_BINLOG_UNSAFE_ACL_TABLE_READ_IN_DML_DDL = 4010 +ER_STOP_REPLICA_MONITOR_IO_THREAD_TIMEOUT = 4011 +ER_STARTING_REPLICA_MONITOR_IO_THREAD = 4012 +ER_CANT_USE_ANONYMOUS_TO_GTID_WITH_GTID_MODE_NOT_ON = 4013 +ER_CANT_COMBINE_ANONYMOUS_TO_GTID_AND_AUTOPOSITION = 4014 +ER_ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS_REQUIRES_GTID_MODE_ON = 4015 +ER_SQL_REPLICA_SKIP_COUNTER_USED_WITH_GTID_MODE_ON = 4016 +ER_USING_ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS_AS_LOCAL_OR_UUID = 4017 +ER_CANT_SET_ANONYMOUS_TO_GTID_AND_WAIT_UNTIL_SQL_THD_AFTER_GTIDS = 4018 +ER_CANT_SET_SQL_AFTER_OR_BEFORE_GTIDS_WITH_ANONYMOUS_TO_GTID = 4019 +ER_ANONYMOUS_TO_GTID_UUID_SAME_AS_GROUP_NAME = 4020 +ER_CANT_USE_SAME_UUID_AS_GROUP_NAME = 4021 +ER_GRP_RPL_RECOVERY_CHANNEL_STILL_RUNNING = 4022 +ER_INNODB_INVALID_AUTOEXTEND_SIZE_VALUE = 4023 +ER_INNODB_INCOMPATIBLE_WITH_TABLESPACE = 4024 +ER_INNODB_AUTOEXTEND_SIZE_OUT_OF_RANGE = 4025 +ER_CANNOT_USE_AUTOEXTEND_SIZE_CLAUSE = 4026 +ER_ROLE_GRANTED_TO_ITSELF = 4027 +ER_TABLE_MUST_HAVE_A_VISIBLE_COLUMN = 4028 +ER_INNODB_COMPRESSION_FAILURE = 4029 +ER_WARN_ASYNC_CONN_FAILOVER_NETWORK_NAMESPACE = 4030 +ER_CLIENT_INTERACTION_TIMEOUT = 4031 +ER_INVALID_CAST_TO_GEOMETRY = 4032 +ER_INVALID_CAST_POLYGON_RING_DIRECTION = 4033 +ER_GIS_DIFFERENT_SRIDS_AGGREGATION = 4034 +ER_RELOAD_KEYRING_FAILURE = 4035 +ER_SDI_GET_KEYS_INVALID_TABLESPACE = 4036 +ER_CHANGE_RPL_SRC_WRONG_COMPRESSION_ALGORITHM_SIZE = 4037 +ER_WARN_DEPRECATED_TLS_VERSION_FOR_CHANNEL_CLI = 4038 +ER_CANT_USE_SAME_UUID_AS_VIEW_CHANGE_UUID = 4039 +ER_ANONYMOUS_TO_GTID_UUID_SAME_AS_VIEW_CHANGE_UUID = 4040 +ER_GRP_RPL_VIEW_CHANGE_UUID_FAIL_GET_VARIABLE = 4041 +ER_WARN_ADUIT_LOG_MAX_SIZE_AND_PRUNE_SECONDS = 4042 +ER_WARN_ADUIT_LOG_MAX_SIZE_CLOSE_TO_ROTATE_ON_SIZE = 4043 +ER_KERBEROS_CREATE_USER = 4044 +ER_INSTALL_PLUGIN_CONFLICT_CLIENT = 4045 +ER_DA_ERROR_LOG_COMPONENT_FLUSH_FAILED = 4046 +ER_WARN_SQL_AFTER_MTS_GAPS_GAP_NOT_CALCULATED = 4047 +ER_INVALID_ASSIGNMENT_TARGET = 4048 +ER_OPERATION_NOT_ALLOWED_ON_GR_SECONDARY = 4049 +ER_GRP_RPL_FAILOVER_CHANNEL_STATUS_PROPAGATION = 4050 +ER_WARN_AUDIT_LOG_FORMAT_UNIX_TIMESTAMP_ONLY_WHEN_JSON = 4051 +ER_INVALID_MFA_PLUGIN_SPECIFIED = 4052 +ER_IDENTIFIED_BY_UNSUPPORTED = 4053 +ER_INVALID_PLUGIN_FOR_REGISTRATION = 4054 +ER_PLUGIN_REQUIRES_REGISTRATION = 4055 +ER_MFA_METHOD_EXISTS = 4056 +ER_MFA_METHOD_NOT_EXISTS = 4057 +ER_AUTHENTICATION_POLICY_MISMATCH = 4058 +ER_PLUGIN_REGISTRATION_DONE = 4059 +ER_INVALID_USER_FOR_REGISTRATION = 4060 +ER_USER_REGISTRATION_FAILED = 4061 +ER_MFA_METHODS_INVALID_ORDER = 4062 +ER_MFA_METHODS_IDENTICAL = 4063 +ER_INVALID_MFA_OPERATIONS_FOR_PASSWORDLESS_USER = 4064 +ER_CHANGE_REPLICATION_SOURCE_NO_OPTIONS_FOR_GTID_ONLY = 4065 +ER_CHANGE_REP_SOURCE_CANT_DISABLE_REQ_ROW_FORMAT_WITH_GTID_ONLY = 4066 +ER_CHANGE_REP_SOURCE_CANT_DISABLE_AUTO_POSITION_WITH_GTID_ONLY = 4067 +ER_CHANGE_REP_SOURCE_CANT_DISABLE_GTID_ONLY_WITHOUT_POSITIONS = 4068 +ER_CHANGE_REP_SOURCE_CANT_DISABLE_AUTO_POS_WITHOUT_POSITIONS = 4069 +ER_CHANGE_REP_SOURCE_GR_CHANNEL_WITH_GTID_MODE_NOT_ON = 4070 +ER_CANT_USE_GTID_ONLY_WITH_GTID_MODE_NOT_ON = 4071 +ER_WARN_C_DISABLE_GTID_ONLY_WITH_SOURCE_AUTO_POS_INVALID_POS = 4072 +ER_DA_SSL_FIPS_MODE_ERROR = 4073 +CR_UNKNOWN_ERROR = 2000 +CR_SOCKET_CREATE_ERROR = 2001 +CR_CONNECTION_ERROR = 2002 +CR_CONN_HOST_ERROR = 2003 +CR_IPSOCK_ERROR = 2004 +CR_UNKNOWN_HOST = 2005 +CR_SERVER_GONE_ERROR = 2006 +CR_VERSION_ERROR = 2007 +CR_OUT_OF_MEMORY = 2008 +CR_WRONG_HOST_INFO = 2009 +CR_LOCALHOST_CONNECTION = 2010 +CR_TCP_CONNECTION = 2011 +CR_SERVER_HANDSHAKE_ERR = 2012 +CR_SERVER_LOST = 2013 +CR_COMMANDS_OUT_OF_SYNC = 2014 +CR_NAMEDPIPE_CONNECTION = 2015 +CR_NAMEDPIPEWAIT_ERROR = 2016 +CR_NAMEDPIPEOPEN_ERROR = 2017 +CR_NAMEDPIPESETSTATE_ERROR = 2018 +CR_CANT_READ_CHARSET = 2019 +CR_NET_PACKET_TOO_LARGE = 2020 +CR_EMBEDDED_CONNECTION = 2021 +CR_PROBE_SLAVE_STATUS = 2022 +CR_PROBE_SLAVE_HOSTS = 2023 +CR_PROBE_SLAVE_CONNECT = 2024 +CR_PROBE_MASTER_CONNECT = 2025 +CR_SSL_CONNECTION_ERROR = 2026 +CR_MALFORMED_PACKET = 2027 +CR_WRONG_LICENSE = 2028 +CR_NULL_POINTER = 2029 +CR_NO_PREPARE_STMT = 2030 +CR_PARAMS_NOT_BOUND = 2031 +CR_DATA_TRUNCATED = 2032 +CR_NO_PARAMETERS_EXISTS = 2033 +CR_INVALID_PARAMETER_NO = 2034 +CR_INVALID_BUFFER_USE = 2035 +CR_UNSUPPORTED_PARAM_TYPE = 2036 +CR_SHARED_MEMORY_CONNECTION = 2037 +CR_SHARED_MEMORY_CONNECT_REQUEST_ERROR = 2038 +CR_SHARED_MEMORY_CONNECT_ANSWER_ERROR = 2039 +CR_SHARED_MEMORY_CONNECT_FILE_MAP_ERROR = 2040 +CR_SHARED_MEMORY_CONNECT_MAP_ERROR = 2041 +CR_SHARED_MEMORY_FILE_MAP_ERROR = 2042 +CR_SHARED_MEMORY_MAP_ERROR = 2043 +CR_SHARED_MEMORY_EVENT_ERROR = 2044 +CR_SHARED_MEMORY_CONNECT_ABANDONED_ERROR = 2045 +CR_SHARED_MEMORY_CONNECT_SET_ERROR = 2046 +CR_CONN_UNKNOW_PROTOCOL = 2047 +CR_INVALID_CONN_HANDLE = 2048 +CR_UNUSED_1 = 2049 +CR_FETCH_CANCELED = 2050 +CR_NO_DATA = 2051 +CR_NO_STMT_METADATA = 2052 +CR_NO_RESULT_SET = 2053 +CR_NOT_IMPLEMENTED = 2054 +CR_SERVER_LOST_EXTENDED = 2055 +CR_STMT_CLOSED = 2056 +CR_NEW_STMT_METADATA = 2057 +CR_ALREADY_CONNECTED = 2058 +CR_AUTH_PLUGIN_CANNOT_LOAD = 2059 +CR_DUPLICATE_CONNECTION_ATTR = 2060 +CR_AUTH_PLUGIN_ERR = 2061 +CR_INSECURE_API_ERR = 2062 +CR_FILE_NAME_TOO_LONG = 2063 +CR_SSL_FIPS_MODE_ERR = 2064 +CR_DEPRECATED_COMPRESSION_NOT_SUPPORTED = 2065 +CR_COMPRESSION_WRONGLY_CONFIGURED = 2066 +CR_KERBEROS_USER_NOT_FOUND = 2067 +CR_LOAD_DATA_LOCAL_INFILE_REJECTED = 2068 +CR_LOAD_DATA_LOCAL_INFILE_REALPATH_FAIL = 2069 +CR_DNS_SRV_LOOKUP_FAILED = 2070 +CR_MANDATORY_TRACKER_NOT_FOUND = 2071 +CR_INVALID_FACTOR_NO = 2072 +# End MySQL Errors + +# Start X Plugin Errors +ER_X_BAD_MESSAGE = 5000 +ER_X_CAPABILITIES_PREPARE_FAILED = 5001 +ER_X_CAPABILITY_NOT_FOUND = 5002 +ER_X_INVALID_PROTOCOL_DATA = 5003 +ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_VALUE_LENGTH = 5004 +ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_KEY_LENGTH = 5005 +ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_EMPTY_KEY = 5006 +ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_LENGTH = 5007 +ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_TYPE = 5008 +ER_X_CAPABILITY_SET_NOT_ALLOWED = 5009 +ER_X_SERVICE_ERROR = 5010 +ER_X_SESSION = 5011 +ER_X_INVALID_ARGUMENT = 5012 +ER_X_MISSING_ARGUMENT = 5013 +ER_X_BAD_INSERT_DATA = 5014 +ER_X_CMD_NUM_ARGUMENTS = 5015 +ER_X_CMD_ARGUMENT_TYPE = 5016 +ER_X_CMD_ARGUMENT_VALUE = 5017 +ER_X_BAD_UPSERT_DATA = 5018 +ER_X_DUPLICATED_CAPABILITIES = 5019 +ER_X_CMD_ARGUMENT_OBJECT_EMPTY = 5020 +ER_X_CMD_INVALID_ARGUMENT = 5021 +ER_X_BAD_UPDATE_DATA = 5050 +ER_X_BAD_TYPE_OF_UPDATE = 5051 +ER_X_BAD_COLUMN_TO_UPDATE = 5052 +ER_X_BAD_MEMBER_TO_UPDATE = 5053 +ER_X_BAD_STATEMENT_ID = 5110 +ER_X_BAD_CURSOR_ID = 5111 +ER_X_BAD_SCHEMA = 5112 +ER_X_BAD_TABLE = 5113 +ER_X_BAD_PROJECTION = 5114 +ER_X_DOC_ID_MISSING = 5115 +ER_X_DUPLICATE_ENTRY = 5116 +ER_X_DOC_REQUIRED_FIELD_MISSING = 5117 +ER_X_PROJ_BAD_KEY_NAME = 5120 +ER_X_BAD_DOC_PATH = 5121 +ER_X_CURSOR_EXISTS = 5122 +ER_X_CURSOR_REACHED_EOF = 5123 +ER_X_PREPARED_STATMENT_CAN_HAVE_ONE_CURSOR = 5131 +ER_X_PREPARED_EXECUTE_ARGUMENT_NOT_SUPPORTED = 5133 +ER_X_PREPARED_EXECUTE_ARGUMENT_CONSISTENCY = 5134 +ER_X_EXPR_BAD_OPERATOR = 5150 +ER_X_EXPR_BAD_NUM_ARGS = 5151 +ER_X_EXPR_MISSING_ARG = 5152 +ER_X_EXPR_BAD_TYPE_VALUE = 5153 +ER_X_EXPR_BAD_VALUE = 5154 +ER_X_INVALID_COLLECTION = 5156 +ER_X_INVALID_ADMIN_COMMAND = 5157 +ER_X_EXPECT_NOT_OPEN = 5158 +ER_X_EXPECT_NO_ERROR_FAILED = 5159 +ER_X_EXPECT_BAD_CONDITION = 5160 +ER_X_EXPECT_BAD_CONDITION_VALUE = 5161 +ER_X_INVALID_NAMESPACE = 5162 +ER_X_BAD_NOTICE = 5163 +ER_X_CANNOT_DISABLE_NOTICE = 5164 +ER_X_BAD_CONFIGURATION = 5165 +ER_X_MYSQLX_ACCOUNT_MISSING_PERMISSIONS = 5167 +ER_X_EXPECT_FIELD_EXISTS_FAILED = 5168 +ER_X_BAD_LOCKING = 5169 +ER_X_FRAME_COMPRESSION_DISABLED = 5170 +ER_X_DECOMPRESSION_FAILED = 5171 +ER_X_BAD_COMPRESSED_FRAME = 5174 +ER_X_CAPABILITY_COMPRESSION_INVALID_ALGORITHM = 5175 +ER_X_CAPABILITY_COMPRESSION_INVALID_SERVER_STYLE = 5176 +ER_X_CAPABILITY_COMPRESSION_INVALID_CLIENT_STYLE = 5177 +ER_X_CAPABILITY_COMPRESSION_INVALID_OPTION = 5178 +ER_X_CAPABILITY_COMPRESSION_MISSING_REQUIRED_FIELDS = 5179 +ER_X_DOCUMENT_DOESNT_MATCH_EXPECTED_SCHEMA = 5180 +ER_X_COLLECTION_OPTION_DOESNT_EXISTS = 5181 +ER_X_INVALID_VALIDATION_SCHEMA = 5182 +# End X Plugin Errors diff --git a/virt/lib/python3.9/site-packages/mysqlx/expr 3.py b/virt/lib/python3.9/site-packages/mysqlx/expr 3.py new file mode 100644 index 00000000..8057b4c4 --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysqlx/expr 3.py @@ -0,0 +1,1366 @@ +# Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, as +# published by the Free Software Foundation. +# +# This program is also distributed with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an +# additional permission to link the program and your derivative works +# with the separately licensed software that they have included with +# MySQL. +# +# Without limiting anything contained in the foregoing, this file, +# which is part of MySQL Connector/Python, is also subject to the +# Universal FOSS Exception, version 1.0, a copy of which can be found at +# http://oss.oracle.com/licenses/universal-foss-exception. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License, version 2.0, for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""Expression Parser.""" + +from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Union + +from .dbdoc import DbDoc +from .helpers import BYTE_TYPES, get_item_or_attr +from .protobuf import Message, mysqlxpb_enum +from .types import ( + BuildExprTypes, + BuildScalarTypes, + MessageType, + ProtobufMessageCextType, + ProtobufMessageType, + StrOrBytes, +) + + +# pylint: disable=missing-function-docstring +class TokenType: + """Token types class.""" + + NOT = 1 + AND = 2 + OR = 3 + XOR = 4 + IS = 5 + LPAREN = 6 + RPAREN = 7 + LSQBRACKET = 8 + RSQBRACKET = 9 + BETWEEN = 10 + TRUE = 11 + NULL = 12 + FALSE = 13 + IN = 14 + LIKE = 15 + INTERVAL = 16 + REGEXP = 17 + ESCAPE = 18 + IDENT = 19 + LSTRING = 20 + LNUM = 21 + DOT = 22 + DOLLAR = 23 + COMMA = 24 + EQ = 25 + NE = 26 + GT = 27 + GE = 28 + LT = 29 + LE = 30 + BITAND = 31 + BITOR = 32 + BITXOR = 33 + LSHIFT = 34 + RSHIFT = 35 + PLUS = 36 + MINUS = 37 + MUL = 38 + DIV = 39 + HEX = 40 + BIN = 41 + NEG = 42 + BANG = 43 + MICROSECOND = 44 + SECOND = 45 + MINUTE = 46 + HOUR = 47 + DAY = 48 + WEEK = 49 + MONTH = 50 + QUARTER = 51 + YEAR = 52 + EROTEME = 53 + DOUBLESTAR = 54 + MOD = 55 + COLON = 56 + OROR = 57 + ANDAND = 58 + LCURLY = 59 + RCURLY = 60 + CAST = 61 + DOTSTAR = 62 + ORDERBY_ASC = 63 + ORDERBY_DESC = 64 + AS = 65 + ARROW = 66 + QUOTE = 67 + BINARY = 68 + DATETIME = 69 + TIME = 70 + CHAR = 71 + DATE = 72 + DECIMAL = 73 + SIGNED = 74 + INTEGER = 75 + UNSIGNED = 76 + JSON = 77 + SECOND_MICROSECOND = 78 + MINUTE_MICROSECOND = 79 + MINUTE_SECOND = 80 + HOUR_MICROSECOND = 81 + HOUR_SECOND = 82 + HOUR_MINUTE = 83 + DAY_MICROSECOND = 84 + DAY_SECOND = 85 + DAY_MINUTE = 86 + DAY_HOUR = 87 + YEAR_MONTH = 88 + OVERLAPS = 89 + + +_INTERVAL_UNITS = set( + [ + TokenType.MICROSECOND, + TokenType.SECOND, + TokenType.MINUTE, + TokenType.HOUR, + TokenType.DAY, + TokenType.WEEK, + TokenType.MONTH, + TokenType.QUARTER, + TokenType.YEAR, + TokenType.SECOND_MICROSECOND, + TokenType.MINUTE_MICROSECOND, + TokenType.MINUTE_SECOND, + TokenType.HOUR_MICROSECOND, + TokenType.HOUR_SECOND, + TokenType.HOUR_MINUTE, + TokenType.DAY_MICROSECOND, + TokenType.DAY_SECOND, + TokenType.DAY_MINUTE, + TokenType.DAY_HOUR, + TokenType.YEAR_MONTH, + ] +) + +# map of reserved word to token type +_RESERVED_WORDS = { + "and": TokenType.AND, + "or": TokenType.OR, + "xor": TokenType.XOR, + "is": TokenType.IS, + "not": TokenType.NOT, + "like": TokenType.LIKE, + "in": TokenType.IN, + "overlaps": TokenType.OVERLAPS, + "regexp": TokenType.REGEXP, + "between": TokenType.BETWEEN, + "interval": TokenType.INTERVAL, + "escape": TokenType.ESCAPE, + "cast": TokenType.CAST, + "div": TokenType.DIV, + "hex": TokenType.HEX, + "bin": TokenType.BIN, + "true": TokenType.TRUE, + "false": TokenType.FALSE, + "null": TokenType.NULL, + "second": TokenType.SECOND, + "minute": TokenType.MINUTE, + "hour": TokenType.HOUR, + "day": TokenType.DAY, + "week": TokenType.WEEK, + "month": TokenType.MONTH, + "quarter": TokenType.QUARTER, + "year": TokenType.YEAR, + "microsecond": TokenType.MICROSECOND, + "asc": TokenType.ORDERBY_ASC, + "desc": TokenType.ORDERBY_DESC, + "as": TokenType.AS, + "binary": TokenType.BINARY, + "datetime": TokenType.DATETIME, + "time": TokenType.TIME, + "char": TokenType.CHAR, + "date": TokenType.DATE, + "decimal": TokenType.DECIMAL, + "signed": TokenType.SIGNED, + "unsigned": TokenType.UNSIGNED, + "integer": TokenType.INTEGER, + "json": TokenType.JSON, + "second_microsecond": TokenType.SECOND_MICROSECOND, + "minute_microsecond": TokenType.MINUTE_MICROSECOND, + "minute_second": TokenType.MINUTE_SECOND, + "hour_microsecond": TokenType.HOUR_MICROSECOND, + "hour_second": TokenType.HOUR_SECOND, + "hour_minute": TokenType.HOUR_MINUTE, + "day_microsecond": TokenType.DAY_MICROSECOND, + "day_second": TokenType.DAY_SECOND, + "day_minute": TokenType.DAY_MINUTE, + "day_hour": TokenType.DAY_HOUR, + "year_month": TokenType.YEAR_MONTH, +} + +_SQL_FUNTION_RESERVED_WORDS_COLLISION = { + "binary": TokenType.BINARY, + "cast": TokenType.CAST, + "char": TokenType.CHAR, + "date": TokenType.DATE, + "decimal": TokenType.DECIMAL, + "signed": TokenType.SIGNED, + "time": TokenType.TIME, + "unsigned": TokenType.UNSIGNED, +} + +_OPERATORS = { + "=": "==", + "==": "==", + "and": "&&", + "div": "div", + "||": "||", + "or": "||", + "not": "not", + "xor": "xor", + "^": "^", + "is": "is", + "between": "between", + "in": "in", + "like": "like", + "!=": "!=", + "<>": "!=", + ">": ">", + ">=": ">=", + "<": "<", + "<=": "<=", + "&": "&", + "&&": "&&", + "|": "|", + "<<": "<<", + ">>": ">>", + "+": "+", + "-": "-", + "*": "*", + "/": "/", + "~": "~", + "%": "%", + "cast": "cast", + "cont_in": "cont_in", + "overlaps": "overlaps", +} + +_UNARY_OPERATORS = { + "+": "sign_plus", + "-": "sign_minus", + "~": "~", + "not": "not", + "!": "!", +} + +_NEGATION = { + "is": "is_not", + "between": "not_between", + "regexp": "not_regexp", + "like": "not_like", + "in": "not_in", + "cont_in": "not_cont_in", + "overlaps": "not_overlaps", +} + + +class Token: + """Token representation class.""" + + def __init__(self, token_type: int, value: str, length: int = 1) -> None: + self.token_type: int = token_type + self.value: str = value + self.length: int = length + + def __repr__(self) -> str: + return self.__str__() + + def __str__(self) -> str: + if self.token_type in ( + TokenType.IDENT, + TokenType.LNUM, + TokenType.LSTRING, + ): + return f"{self.token_type}({self.value})" + return f"{self.token_type}" + + +# static protobuf helper functions + + +def build_expr(value: BuildExprTypes) -> MessageType: + msg = Message("Mysqlx.Expr.Expr") + if isinstance(value, (Message)): + return value + if isinstance(value, (ExprParser)): + return value.expr(reparse=True) + if isinstance(value, (dict, DbDoc)): + msg["type"] = mysqlxpb_enum("Mysqlx.Expr.Expr.Type.OBJECT") + msg["object"] = build_object(value).get_message() + elif isinstance(value, (list, tuple)): + msg["type"] = mysqlxpb_enum("Mysqlx.Expr.Expr.Type.ARRAY") + msg["array"] = build_array(value).get_message() + else: + msg["type"] = mysqlxpb_enum("Mysqlx.Expr.Expr.Type.LITERAL") + msg["literal"] = build_scalar(value).get_message() + return msg + + +def build_scalar(value: BuildScalarTypes) -> MessageType: + if isinstance(value, str): + return build_string_scalar(value) + if isinstance(value, BYTE_TYPES): + return build_bytes_scalar(value) + if isinstance(value, bool): + return build_bool_scalar(value) + if isinstance(value, int): + return build_int_scalar(value) + if isinstance(value, float): + return build_double_scalar(value) + if value is None: + return build_null_scalar() + raise ValueError(f"Unsupported data type: {type(value)}") + + +def build_object(obj: Union[Dict, DbDoc]) -> MessageType: + if isinstance(obj, DbDoc): + return build_object(obj.__dict__) + + msg = Message("Mysqlx.Expr.Object") + for key, value in obj.items(): + pair = Message("Mysqlx.Expr.Object.ObjectField") + pair["key"] = key.encode() if isinstance(key, str) else key + pair["value"] = build_expr(value).get_message() + msg["fld"].extend([pair.get_message()]) + return msg + + +def build_array(array: Sequence[BuildExprTypes]) -> MessageType: + msg = Message("Mysqlx.Expr.Array") + msg["value"].extend([build_expr(value).get_message() for value in array]) + return msg + + +def build_null_scalar() -> MessageType: + msg = Message("Mysqlx.Datatypes.Scalar") + msg["type"] = mysqlxpb_enum("Mysqlx.Datatypes.Scalar.Type.V_NULL") + return msg + + +def build_double_scalar(value: float) -> MessageType: + msg = Message("Mysqlx.Datatypes.Scalar") + msg["type"] = mysqlxpb_enum("Mysqlx.Datatypes.Scalar.Type.V_DOUBLE") + msg["v_double"] = value + return msg + + +def build_int_scalar(value: int) -> MessageType: + msg = Message("Mysqlx.Datatypes.Scalar") + msg["type"] = mysqlxpb_enum("Mysqlx.Datatypes.Scalar.Type.V_SINT") + msg["v_signed_int"] = value + return msg + + +def build_unsigned_int_scalar(value: int) -> MessageType: + msg = Message("Mysqlx.Datatypes.Scalar") + msg["type"] = mysqlxpb_enum("Mysqlx.Datatypes.Scalar.Type.V_UINT") + msg["v_unsigned_int"] = value + return msg + + +def build_string_scalar(value: StrOrBytes) -> MessageType: + if isinstance(value, str): + value = bytes(bytearray(value, "utf-8")) + msg = Message("Mysqlx.Datatypes.Scalar") + msg["type"] = mysqlxpb_enum("Mysqlx.Datatypes.Scalar.Type.V_STRING") + msg["v_string"] = Message("Mysqlx.Datatypes.Scalar.String", value=value) + return msg + + +def build_bool_scalar(value: bool) -> MessageType: + msg = Message("Mysqlx.Datatypes.Scalar") + msg["type"] = mysqlxpb_enum("Mysqlx.Datatypes.Scalar.Type.V_BOOL") + msg["v_bool"] = value + return msg + + +def build_bytes_scalar(value: bytes) -> MessageType: + msg = Message("Mysqlx.Datatypes.Scalar") + msg["type"] = mysqlxpb_enum("Mysqlx.Datatypes.Scalar.Type.V_OCTETS") + msg["v_octets"] = Message("Mysqlx.Datatypes.Scalar.Octets", value=value) + return msg + + +def build_literal_expr(value: MessageType) -> MessageType: + msg = Message("Mysqlx.Expr.Expr") + msg["type"] = mysqlxpb_enum("Mysqlx.Expr.Expr.Type.LITERAL") + msg["literal"] = value + return msg + + +def build_unary_op(name: str, param: MessageType) -> MessageType: + operator = Message("Mysqlx.Expr.Operator") + operator["name"] = _UNARY_OPERATORS[name] + operator["param"] = [param.get_message()] + msg = Message("Mysqlx.Expr.Expr") + msg["type"] = mysqlxpb_enum("Mysqlx.Expr.Expr.Type.OPERATOR") + msg["operator"] = operator.get_message() + return msg + + +def escape_literal(string: str) -> str: + return string.replace('"', '""') + + +class ExprParser: + """Expression parser class.""" + + def __init__(self, string: str, allow_relational: bool = True) -> None: + self.string: str = string + self.tokens: List[Token] = [] + self.path_name_queue: List[str] = [] + self.pos: int = 0 + self._allow_relational_columns: bool = allow_relational + self.placeholder_name_to_position: Dict[str, int] = {} + self.positional_placeholder_count: int = 0 + self.clean_expression() + self.lex() + + def __str__(self) -> str: + return f"<mysqlx.ExprParser '{self.string}'>" + + def clean_expression(self) -> None: + """Removes the keywords that does not form part of the expression. + + Removes the keywords "SELECT" and "WHERE" that does not form part of + the expression itself. + """ + if not isinstance(self.string, str): + self.string = repr(self.string) + self.string = self.string.strip(" ") + if len(self.string) > 1 and self.string[-1] == ";": + self.string = self.string[:-1] + if "SELECT" in self.string[:6].upper(): + self.string = self.string[6:] + if "WHERE" in self.string[:5].upper(): + self.string = self.string[5:] + + # convencience checker for lexer + def next_char_is(self, key: int, char: str) -> bool: + return key + 1 < len(self.string) and self.string[key + 1] == char + + def lex_number(self, pos: int) -> Token: + # numeric literal + start = pos + found_dot = False + while pos < len(self.string) and ( + self.string[pos].isdigit() or self.string[pos] == "." + ): + if self.string[pos] == ".": + if found_dot is True: + raise ValueError("Invalid number. Found multiple '.'") + found_dot = True + # technically we allow more than one "." and let float()'s parsing + # complain later + pos += 1 + val = self.string[start:pos] + return Token(TokenType.LNUM, val, len(val)) + + def lex_alpha(self, i: int, allow_space: bool = False) -> Token: + start = i + while i < len(self.string) and ( + self.string[i].isalnum() + or self.string[i] == "_" + or (self.string[i].isspace() and allow_space) + ): + i += 1 + + val = self.string[start:i] + try: + if ( + i < len(self.string) + and self.string[i] == "(" + and val.lower() not in _SQL_FUNTION_RESERVED_WORDS_COLLISION + ): + token = Token(TokenType.IDENT, val, len(val)) + else: + token = Token(_RESERVED_WORDS[val.lower()], val.lower(), len(val)) + except KeyError: + token = Token(TokenType.IDENT, val, len(val)) + return token + + def lex_quoted_token(self, key: int) -> Token: + quote_char = self.string[key] + val = "" + key += 1 + start = key + while key < len(self.string): + char = self.string[key] + if ( + char == quote_char + and key + 1 < len(self.string) + and self.string[key + 1] != quote_char + ): + # break if we have a quote char that's not double + break + if char in (quote_char, "\\"): + # this quote char has to be doubled + if key + 1 >= len(self.string): + break + key += 1 + val += self.string[key] + else: + val += char + key += 1 + if key >= len(self.string) or self.string[key] != quote_char: + raise ValueError(f"Unterminated quoted string starting at {start}") + if quote_char == "`": + return Token(TokenType.IDENT, val, len(val) + 2) + return Token(TokenType.LSTRING, val, len(val) + 2) + + def lex(self) -> None: + i = 0 + arrow_last = False + inside_arrow = False + while i < len(self.string): + char = self.string[i] + if char.isspace(): + i += 1 + continue + if char.isdigit(): + token = self.lex_number(i) + elif char.isalpha() or char == "_": + token = self.lex_alpha(i, inside_arrow) + elif char == "?": + token = Token(TokenType.EROTEME, char) + elif char == ":": + token = Token(TokenType.COLON, char) + elif char == "{": + token = Token(TokenType.LCURLY, char) + elif char == "}": + token = Token(TokenType.RCURLY, char) + elif char == "+": + token = Token(TokenType.PLUS, char) + elif char == "-": + if self.next_char_is(i, ">") and not arrow_last: + token = Token(TokenType.ARROW, "->", 2) + arrow_last = True + else: + token = Token(TokenType.MINUS, char) + elif char == "*": + if self.next_char_is(i, "*"): + token = Token(TokenType.DOUBLESTAR, "**", 2) + else: + token = Token(TokenType.MUL, char) + elif char == "/": + token = Token(TokenType.DIV, char) + elif char == "$": + token = Token(TokenType.DOLLAR, char) + elif char == "%": + token = Token(TokenType.MOD, char) + elif char == "=": + if self.next_char_is(i, "="): + token = Token(TokenType.EQ, "==", 2) + else: + token = Token(TokenType.EQ, "==", 1) + elif char == "&": + if self.next_char_is(i, "&"): + token = Token(TokenType.ANDAND, "&&", 2) + else: + token = Token(TokenType.BITAND, char) + elif char == "^": + token = Token(TokenType.BITXOR, char) + elif char == "|": + if self.next_char_is(i, "|"): + token = Token(TokenType.OROR, "||", 2) + else: + token = Token(TokenType.BITOR, char) + elif char == "(": + token = Token(TokenType.LPAREN, char) + elif char == ")": + token = Token(TokenType.RPAREN, char) + elif char == "[": + token = Token(TokenType.LSQBRACKET, char) + elif char == "]": + token = Token(TokenType.RSQBRACKET, char) + elif char == "~": + token = Token(TokenType.NEG, char) + elif char == ",": + token = Token(TokenType.COMMA, char) + elif char == "!": + if self.next_char_is(i, "="): + token = Token(TokenType.NE, "!=", 2) + else: + token = Token(TokenType.BANG, char) + elif char == "<": + if self.next_char_is(i, ">"): + token = Token(TokenType.NE, "<>", 2) + elif self.next_char_is(i, "<"): + token = Token(TokenType.LSHIFT, "<<", 2) + elif self.next_char_is(i, "="): + token = Token(TokenType.LE, "<=", 2) + else: + token = Token(TokenType.LT, char) + elif char == ">": + if self.next_char_is(i, ">"): + token = Token(TokenType.RSHIFT, ">>", 2) + elif self.next_char_is(i, "="): + token = Token(TokenType.GE, ">=", 2) + else: + token = Token(TokenType.GT, char) + elif char == ".": + if self.next_char_is(i, "*"): + token = Token(TokenType.DOTSTAR, ".*", 2) + elif i + 1 < len(self.string) and self.string[i + 1].isdigit(): + token = self.lex_number(i) + else: + token = Token(TokenType.DOT, char) + elif char in ("'", '"') and arrow_last: + token = Token(TokenType.QUOTE, char) + if not inside_arrow: + inside_arrow = True + else: + arrow_last = False + inside_arrow = False + elif char in ('"', "'", "`"): + token = self.lex_quoted_token(i) + else: + raise ValueError(f"Unknown character at {i}") + self.tokens.append(token) + i += token.length + + def assert_cur_token(self, token_type: int) -> None: + if self.pos >= len(self.tokens): + raise ValueError( + f"Expected token type {token_type} at pos {self.pos} but no " + "tokens left" + ) + if self.tokens[self.pos].token_type != token_type: + raise ValueError( + f"Expected token type {token_type} at pos {self.pos} but found " + f"type {self.tokens[self.pos]}, on tokens {self.tokens}" + ) + + def cur_token_type_is(self, token_type: int) -> bool: + return self.pos_token_type_is(self.pos, token_type) + + def cur_token_type_in(self, *types: int) -> bool: + return self.pos < len(self.tokens) and self.tokens[self.pos].token_type in types + + def next_token_type_is(self, token_type: int) -> bool: + return self.pos_token_type_is(self.pos + 1, token_type) + + def next_token_type_in(self, *types: int) -> bool: + return ( + self.pos < len(self.tokens) + and self.tokens[self.pos + 1].token_type in types + ) + + def pos_token_type_is(self, pos: int, token_type: int) -> bool: + return pos < len(self.tokens) and self.tokens[pos].token_type == token_type + + def consume_token(self, token_type: int) -> str: + self.assert_cur_token(token_type) + value = self.tokens[self.pos].value + self.pos += 1 + return value + + def paren_expr_list( + self, + ) -> List[Union[ProtobufMessageType, ProtobufMessageCextType]]: + """Parse a paren-bounded expression list for function arguments or IN + list and return a list of Expr objects. + """ + exprs = [] + path_name_added = False + self.consume_token(TokenType.LPAREN) + if not self.cur_token_type_is(TokenType.RPAREN): + msg_expr = self._expr().get_message() + if hasattr(msg_expr, "identifier") and msg_expr.identifier.name: + self.path_name_queue.insert(0, msg_expr.identifier.name) + path_name_added = True + elif ( + not hasattr(msg_expr, "identifier") + and "identifier" in msg_expr + and "name" in msg_expr["identifier"] + ): + self.path_name_queue.insert(0, msg_expr["identifier"]["name"]) + path_name_added = True + exprs.append(msg_expr) + while self.cur_token_type_is(TokenType.COMMA): + self.pos += 1 + exprs.append(self._expr().get_message()) + self.consume_token(TokenType.RPAREN) + if path_name_added: + self.path_name_queue.pop() + return exprs + + def identifier(self) -> MessageType: + self.assert_cur_token(TokenType.IDENT) + ident = Message("Mysqlx.Expr.Identifier") + if self.next_token_type_is(TokenType.DOT): + ident["schema_name"] = self.consume_token(TokenType.IDENT) + self.consume_token(TokenType.DOT) + ident["name"] = self.tokens[self.pos].value + self.pos += 1 + return ident + + def function_call(self) -> MessageType: + function_call = Message("Mysqlx.Expr.FunctionCall") + function_call["name"] = self.identifier() + function_call["param"] = self.paren_expr_list() + msg_expr = Message("Mysqlx.Expr.Expr") + msg_expr["type"] = mysqlxpb_enum("Mysqlx.Expr.Expr.Type.FUNC_CALL") + msg_expr["function_call"] = function_call.get_message() + return msg_expr + + def docpath_member(self) -> MessageType: + self.consume_token(TokenType.DOT) + token = self.tokens[self.pos] + + if token.token_type == TokenType.IDENT: + if token.value.startswith("`") and token.value.endswith("`"): + raise ValueError( + f"{token.value} is not a valid JSON/ECMAScript identifier" + ) + self.consume_token(TokenType.IDENT) + member_name = token.value + elif token.token_type == TokenType.LSTRING: + self.consume_token(TokenType.LSTRING) + member_name = token.value + else: + raise ValueError( + "Expected token type IDENT or LSTRING in JSON path at token " + f"pos {self.pos}" + ) + doc_path_item = Message("Mysqlx.Expr.DocumentPathItem") + doc_path_item["type"] = mysqlxpb_enum( + "Mysqlx.Expr.DocumentPathItem.Type.MEMBER" + ) + doc_path_item["value"] = member_name + return doc_path_item + + def docpath_array_loc(self) -> MessageType: + self.consume_token(TokenType.LSQBRACKET) + if self.cur_token_type_is(TokenType.MUL): + self.consume_token(TokenType.MUL) + self.consume_token(TokenType.RSQBRACKET) + doc_path_item = Message("Mysqlx.Expr.DocumentPathItem") + doc_path_item["type"] = mysqlxpb_enum( + "Mysqlx.Expr.DocumentPathItem.Type.ARRAY_INDEX_ASTERISK" + ) + return doc_path_item + if self.cur_token_type_is(TokenType.LNUM): + value = int(self.consume_token(TokenType.LNUM)) + if value < 0: + raise IndexError(f"Array index cannot be negative at {self.pos}") + self.consume_token(TokenType.RSQBRACKET) + doc_path_item = Message("Mysqlx.Expr.DocumentPathItem") + doc_path_item["type"] = mysqlxpb_enum( + "Mysqlx.Expr.DocumentPathItem.Type.ARRAY_INDEX" + ) + doc_path_item["index"] = value + return doc_path_item + raise ValueError( + "Exception token type MUL or LNUM in JSON path array index at " + f"token pos {self.pos}" + ) + + def document_field(self) -> MessageType: + if not self.tokens: + raise ValueError("Empty string cannot be used as document field") + if self.cur_token_type_is(TokenType.DOLLAR): + self.consume_token(TokenType.DOLLAR) + col_id = Message("Mysqlx.Expr.ColumnIdentifier") + if self.cur_token_type_is(TokenType.IDENT): + doc_path_item = Message("Mysqlx.Expr.DocumentPathItem") + doc_path_item["type"] = mysqlxpb_enum( + "Mysqlx.Expr.DocumentPathItem.Type.MEMBER" + ) + doc_path_item["value"] = self.consume_token(TokenType.IDENT) + col_id["document_path"].extend([doc_path_item.get_message()]) + col_id["document_path"].extend(self.document_path()) + if self.path_name_queue: + col_id["name"] = self.path_name_queue[0] + expr = Message("Mysqlx.Expr.Expr") + expr["type"] = mysqlxpb_enum("Mysqlx.Expr.Expr.Type.IDENT") + expr["identifier"] = col_id + return expr + + def document_path( + self, + ) -> List[Union[ProtobufMessageType, ProtobufMessageCextType]]: + """Parse a JSON-style document path, like WL#7909, but prefix by @. + instead of $. We parse this as a string because the protocol doesn't + support it. (yet) + """ + doc_path = [] + while True: + if self.cur_token_type_is(TokenType.DOT): + doc_path.append(self.docpath_member().get_message()) + elif self.cur_token_type_is(TokenType.DOTSTAR): + self.consume_token(TokenType.DOTSTAR) + doc_path_item = Message("Mysqlx.Expr.DocumentPathItem") + doc_path_item["type"] = mysqlxpb_enum( + "Mysqlx.Expr.DocumentPathItem.Type.MEMBER_ASTERISK" + ) + doc_path.append(doc_path_item.get_message()) + elif self.cur_token_type_is(TokenType.LSQBRACKET): + doc_path.append(self.docpath_array_loc().get_message()) + elif self.cur_token_type_is(TokenType.DOUBLESTAR): + self.consume_token(TokenType.DOUBLESTAR) + doc_path_item = Message("Mysqlx.Expr.DocumentPathItem") + doc_path_item["type"] = mysqlxpb_enum( + "Mysqlx.Expr.DocumentPathItem.Type.DOUBLE_ASTERISK" + ) + doc_path.append(doc_path_item.get_message()) + else: + break + items = len(doc_path) + if items > 0 and get_item_or_attr(doc_path[items - 1], "type") == mysqlxpb_enum( + "Mysqlx.Expr.DocumentPathItem.Type.DOUBLE_ASTERISK" + ): + raise ValueError(f"JSON path may not end in '**' at {self.pos}") + return doc_path + + def column_identifier(self) -> MessageType: + parts = [] + parts.append(self.consume_token(TokenType.IDENT)) + while self.cur_token_type_is(TokenType.DOT): + self.consume_token(TokenType.DOT) + parts.append(self.consume_token(TokenType.IDENT)) + if len(parts) > 3: + raise ValueError(f"Too many parts to identifier at {self.pos}") + parts.reverse() + col_id = Message("Mysqlx.Expr.ColumnIdentifier") + # clever way to apply them to the struct + for i in range(0, len(parts)): + if i == 0: + col_id["name"] = parts[0] + elif i == 1: + col_id["table_name"] = parts[1] + elif i == 2: + col_id["schema_name"] = parts[2] + + is_doc = False + if self.cur_token_type_is(TokenType.DOLLAR): + is_doc = True + self.consume_token(TokenType.DOLLAR) + col_id["document_path"] = self.document_path() + elif self.cur_token_type_is(TokenType.ARROW): + is_doc = True + self.consume_token(TokenType.ARROW) + is_quoted = False + if self.cur_token_type_is(TokenType.QUOTE): + is_quoted = True + self.consume_token(TokenType.QUOTE) + self.consume_token(TokenType.DOLLAR) + col_id["document_path"] = self.document_path() + if is_quoted: + self.consume_token(TokenType.QUOTE) + + if is_doc and len(col_id["document_path"]) == 0: + doc_path_item = Message("Mysqlx.Expr.DocumentPathItem") + doc_path_item["type"] = mysqlxpb_enum( + "Mysqlx.Expr.DocumentPathItem.Type.MEMBER" + ) + doc_path_item["value"] = "" + col_id["document_path"].extend([doc_path_item.get_message()]) + + msg_expr = Message("Mysqlx.Expr.Expr") + msg_expr["type"] = mysqlxpb_enum("Mysqlx.Expr.Expr.Type.IDENT") + msg_expr["identifier"] = col_id + return msg_expr + + def next_token(self) -> Token: + if self.pos >= len(self.tokens): + raise ValueError("Unexpected end of token stream") + token = self.tokens[self.pos] + self.pos += 1 + return token + + def expect_token(self, token_type: int) -> None: + token = self.next_token() + if token.token_type != token_type: + raise ValueError(f"Expected token type {token_type}") + + def peek_token(self) -> Token: + return self.tokens[self.pos] + + def consume_any_token(self) -> str: + value = self.tokens[self.pos].value + self.pos += 1 + return value + + def parse_json_array(self) -> MessageType: + """ + jsonArray ::= "[" [ expr ("," expr)* ] "]" + """ + msg = Message("Mysqlx.Expr.Array") + while self.pos < len(self.tokens) and not self.cur_token_type_is( + TokenType.RSQBRACKET + ): + msg["value"].extend([self._expr().get_message()]) + if not self.cur_token_type_is(TokenType.COMMA): + break + self.consume_token(TokenType.COMMA) + self.consume_token(TokenType.RSQBRACKET) + + expr = Message("Mysqlx.Expr.Expr") + expr["type"] = mysqlxpb_enum("Mysqlx.Expr.Expr.Type.ARRAY") + expr["array"] = msg.get_message() + return expr + + def parse_json_doc(self) -> MessageType: + """ + jsonDoc ::= "{" [jsonKeyValue ("," jsonKeyValue)*] "}" + jsonKeyValue ::= STRING_DQ ":" expr + """ + msg = Message("Mysqlx.Expr.Object") + while self.pos < len(self.tokens) and not self.cur_token_type_is( + TokenType.RCURLY + ): + item = Message("Mysqlx.Expr.Object.ObjectField") + item["key"] = self.consume_token(TokenType.LSTRING) + self.consume_token(TokenType.COLON) + item["value"] = self._expr().get_message() + msg["fld"].extend([item.get_message()]) + if not self.cur_token_type_is(TokenType.COMMA): + break + self.consume_token(TokenType.COMMA) + self.consume_token(TokenType.RCURLY) + + expr = Message("Mysqlx.Expr.Expr") + expr["type"] = mysqlxpb_enum("Mysqlx.Expr.Expr.Type.OBJECT") + expr["object"] = msg.get_message() + return expr + + def parse_place_holder(self, token: Token) -> MessageType: + place_holder_name = "" + if self.cur_token_type_is(TokenType.LNUM): + place_holder_name = self.consume_token(TokenType.LNUM) + elif self.cur_token_type_is(TokenType.IDENT): + place_holder_name = self.consume_token(TokenType.IDENT) + elif token.token_type == TokenType.EROTEME: + place_holder_name = str(self.positional_placeholder_count) + else: + raise ValueError(f"Invalid placeholder name at token pos {self.pos}") + + msg_expr = Message("Mysqlx.Expr.Expr") + msg_expr["type"] = mysqlxpb_enum("Mysqlx.Expr.Expr.Type.PLACEHOLDER") + if place_holder_name in self.placeholder_name_to_position: + msg_expr["position"] = self.placeholder_name_to_position[place_holder_name] + else: + msg_expr["position"] = self.positional_placeholder_count + self.placeholder_name_to_position[ + place_holder_name + ] = self.positional_placeholder_count + self.positional_placeholder_count += 1 + return msg_expr + + def cast(self) -> MessageType: + """cast ::= CAST LPAREN expr AS cast_data_type RPAREN""" + operator = Message("Mysqlx.Expr.Operator", name="cast") + self.consume_token(TokenType.LPAREN) + operator["param"].extend([self._expr().get_message()]) + self.consume_token(TokenType.AS) + + type_scalar = build_bytes_scalar(str.encode(self.cast_data_type())) + operator["param"].extend([build_literal_expr(type_scalar).get_message()]) + self.consume_token(TokenType.RPAREN) + msg = Message("Mysqlx.Expr.Expr", operator=operator.get_message()) + msg["type"] = mysqlxpb_enum("Mysqlx.Expr.Expr.Type.OPERATOR") + return msg + + def cast_data_type(self) -> str: + """cast_data_type ::= ( BINARY dimension? ) | + ( CHAR dimension? ) | + ( DATE ) | + ( DATETIME dimension? ) | + ( TIME dimension? ) | + ( DECIMAL dimension? ) | + ( SIGNED INTEGER? ) | + ( UNSIGNED INTEGER? ) | + JSON + """ + token = self.next_token() + if token.token_type in ( + TokenType.BINARY, + TokenType.CHAR, + TokenType.DATETIME, + TokenType.TIME, + ): + dimension = self.cast_data_type_dimension() + return f"{token.value}{dimension}" if dimension else token.value + if token.token_type is TokenType.DECIMAL: + dimension = self.cast_data_type_dimension(True) + return f"{token.value}{dimension}" if dimension else token.value + if token.token_type in (TokenType.SIGNED, TokenType.UNSIGNED): + if self.cur_token_type_is(TokenType.INTEGER): + self.consume_token(TokenType.INTEGER) + return token.value + if token.token_type in ( + TokenType.INTEGER, + TokenType.JSON, + TokenType.DATE, + ): + return token.value + + raise ValueError( + f"Unknown token type {token.token_type} at position {self.pos} " + f"({token.value})" + ) + + def cast_data_type_dimension(self, decimal: bool = False) -> Optional[str]: + """dimension ::= LPAREN LNUM (, LNUM)? RPAREN""" + if not self.cur_token_type_is(TokenType.LPAREN): + return None + + dimension = [] + self.consume_token(TokenType.LPAREN) + dimension.append(self.consume_token(TokenType.LNUM)) + if decimal and self.cur_token_type_is(TokenType.COMMA): + self.consume_token(TokenType.COMMA) + dimension.append(self.consume_token(TokenType.LNUM)) + self.consume_token(TokenType.RPAREN) + + return ( + f"({dimension[0]})" + if len(dimension) == 1 + else f"({dimension[0]},{dimension[1]})" + ) + + @staticmethod + def star_operator() -> MessageType: + msg = Message("Mysqlx.Expr.Expr") + msg["type"] = mysqlxpb_enum("Mysqlx.Expr.Expr.Type.OPERATOR") + msg["operator"] = Message("Mysqlx.Expr.Operator", name="*") + return msg + + def atomic_expr(self) -> MessageType: + """Parse an atomic expression and return a protobuf Expr object""" + token = self.next_token() + + if token.token_type in [TokenType.EROTEME, TokenType.COLON]: + return self.parse_place_holder(token) + if token.token_type == TokenType.LCURLY: + return self.parse_json_doc() + if token.token_type == TokenType.LSQBRACKET: + return self.parse_json_array() + if token.token_type == TokenType.CAST: + return self.cast() + if token.token_type == TokenType.LPAREN: + expr = self._expr() + self.expect_token(TokenType.RPAREN) + return expr + if token.token_type in [TokenType.PLUS, TokenType.MINUS]: + peek = self.peek_token() + if peek.token_type == TokenType.LNUM: + self.tokens[self.pos].value = token.value + peek.value + return self.atomic_expr() + return build_unary_op(token.value, self.atomic_expr()) + if token.token_type in (TokenType.NOT, TokenType.NEG, TokenType.BANG): + return build_unary_op(token.value, self.atomic_expr()) + if token.token_type == TokenType.LSTRING: + return build_literal_expr(build_string_scalar(token.value)) + if token.token_type == TokenType.NULL: + return build_literal_expr(build_null_scalar()) + if token.token_type == TokenType.LNUM: + if "." in token.value: + return build_literal_expr(build_double_scalar(float(token.value))) + return build_literal_expr(build_int_scalar(int(token.value))) + if token.token_type in [TokenType.TRUE, TokenType.FALSE]: + return build_literal_expr( + build_bool_scalar(token.token_type == TokenType.TRUE) + ) + if token.token_type == TokenType.DOLLAR: + return self.document_field() + if token.token_type == TokenType.MUL: + return self.star_operator() + if token.token_type == TokenType.IDENT: + self.pos = self.pos - 1 # stay on the identifier + if self.next_token_type_is(TokenType.LPAREN) or ( + self.next_token_type_is(TokenType.DOT) + and self.pos_token_type_is(self.pos + 2, TokenType.IDENT) + and self.pos_token_type_is(self.pos + 3, TokenType.LPAREN) + ): + # Function call + return self.function_call() + return ( + self.document_field() + if not self._allow_relational_columns + else self.column_identifier() + ) + + raise ValueError( + f"Unknown token type = {token.token_type} when expecting atomic " + f"expression at {self.pos}" + ) + + def parse_left_assoc_binary_op_expr( + self, types: Iterable[int], inner_parser: Callable[[], MessageType] + ) -> MessageType: + """Given a `set' of types and an Expr-returning inner parser function, + parse a left associate binary operator expression""" + lhs = inner_parser() + while self.pos < len(self.tokens) and self.tokens[self.pos].token_type in types: + msg = Message("Mysqlx.Expr.Expr") + msg["type"] = mysqlxpb_enum("Mysqlx.Expr.Expr.Type.OPERATOR") + operator = Message("Mysqlx.Expr.Operator") + operator["name"] = _OPERATORS[self.tokens[self.pos].value] + operator["param"] = [lhs.get_message()] + self.pos += 1 + operator["param"].extend([inner_parser().get_message()]) + msg["operator"] = operator + lhs = msg + return lhs + + # operator precedence is implemented here + def add_sub_interval(self) -> MessageType: + lhs = self.atomic_expr() + if self.cur_token_type_in( + TokenType.PLUS, TokenType.MINUS + ) and self.next_token_type_is(TokenType.INTERVAL): + token = self.next_token() + + operator = Message("Mysqlx.Expr.Operator") + operator["param"].extend([lhs.get_message()]) + operator["name"] = ( + "date_add" if token.token_type is TokenType.PLUS else "date_sub" + ) + + self.consume_token(TokenType.INTERVAL) + operator["param"].extend([self.bit_expr().get_message()]) + + if not self.cur_token_type_in(*_INTERVAL_UNITS): + raise ValueError(f"Expected interval type at position {self.pos}") + + token = str.encode(self.consume_any_token().upper()) + operator["param"].extend( + [build_literal_expr(build_bytes_scalar(token)).get_message()] + ) + + lhs = Message("Mysqlx.Expr.Expr", operator=operator) + lhs["type"] = mysqlxpb_enum("Mysqlx.Expr.Expr.Type.OPERATOR") + + return lhs + + def mul_div_expr(self) -> MessageType: + return self.parse_left_assoc_binary_op_expr( + set([TokenType.MUL, TokenType.DIV, TokenType.MOD]), + self.add_sub_interval, + ) + + def add_sub_expr(self) -> MessageType: + return self.parse_left_assoc_binary_op_expr( + set([TokenType.PLUS, TokenType.MINUS]), self.mul_div_expr + ) + + def shift_expr(self) -> MessageType: + return self.parse_left_assoc_binary_op_expr( + set([TokenType.LSHIFT, TokenType.RSHIFT]), self.add_sub_expr + ) + + def bit_expr(self) -> MessageType: + return self.parse_left_assoc_binary_op_expr( + set([TokenType.BITAND, TokenType.BITOR, TokenType.BITXOR]), + self.shift_expr, + ) + + def comp_expr(self) -> MessageType: + return self.parse_left_assoc_binary_op_expr( + set( + [ + TokenType.GE, + TokenType.GT, + TokenType.LE, + TokenType.LT, + TokenType.EQ, + TokenType.NE, + ] + ), + self.bit_expr, + ) + + def ilri_expr(self) -> MessageType: + params = [] + lhs = self.comp_expr() + is_not = False + if self.cur_token_type_is(TokenType.NOT): + is_not = True + self.consume_token(TokenType.NOT) + if self.pos < len(self.tokens): + params.append(lhs.get_message()) + op_name = self.tokens[self.pos].value + if self.cur_token_type_is(TokenType.IS): + self.consume_token(TokenType.IS) + # for IS, NOT comes AFTER + if self.cur_token_type_is(TokenType.NOT): + is_not = True + self.consume_token(TokenType.NOT) + params.append(self.comp_expr().get_message()) + elif self.cur_token_type_is(TokenType.IN): + self.consume_token(TokenType.IN) + if self.cur_token_type_is(TokenType.LPAREN): + params.extend(self.paren_expr_list()) + else: + op_name = "cont_in" + params.append(self.comp_expr().get_message()) + elif self.cur_token_type_is(TokenType.OVERLAPS): + self.consume_token(TokenType.OVERLAPS) + params.append(self.comp_expr().get_message()) + + elif self.cur_token_type_is(TokenType.LIKE): + self.consume_token(TokenType.LIKE) + params.append(self.comp_expr().get_message()) + if self.cur_token_type_is(TokenType.ESCAPE): + self.consume_token(TokenType.ESCAPE) + params.append(self.comp_expr().get_message()) + elif self.cur_token_type_is(TokenType.BETWEEN): + self.consume_token(TokenType.BETWEEN) + params.append(self.comp_expr().get_message()) + self.consume_token(TokenType.AND) + params.append(self.comp_expr().get_message()) + elif self.cur_token_type_is(TokenType.REGEXP): + self.consume_token(TokenType.REGEXP) + params.append(self.comp_expr().get_message()) + else: + if is_not: + raise ValueError(f"Unknown token after NOT as pos {self.pos}") + op_name = None # not an operator we're interested in + if op_name: + operator = Message("Mysqlx.Expr.Operator") + operator["name"] = _NEGATION[op_name] if is_not else op_name + operator["param"] = params + msg_expr = Message("Mysqlx.Expr.Expr") + msg_expr["type"] = mysqlxpb_enum("Mysqlx.Expr.Expr.Type.OPERATOR") + msg_expr["operator"] = operator.get_message() + lhs = msg_expr + return lhs + + def and_expr(self) -> MessageType: + return self.parse_left_assoc_binary_op_expr( + set([TokenType.AND, TokenType.ANDAND]), self.ilri_expr + ) + + def xor_expr(self) -> MessageType: + return self.parse_left_assoc_binary_op_expr(set([TokenType.XOR]), self.and_expr) + + def or_expr(self) -> MessageType: + return self.parse_left_assoc_binary_op_expr( + set([TokenType.OR, TokenType.OROR]), self.xor_expr + ) + + def _expr(self, reparse: bool = False) -> MessageType: + if reparse: + self.tokens = [] + self.pos = 0 + self.placeholder_name_to_position = {} + self.positional_placeholder_count = 0 + self.lex() + return self.or_expr() + + def expr(self, reparse: bool = False) -> MessageType: + expression = self._expr(reparse) + used_tokens = self.pos + if self.pos_token_type_is(len(self.tokens) - 2, TokenType.AS): + used_tokens += 2 + if used_tokens < len(self.tokens): + raise ValueError( + f"Unused token types {self.tokens[self.pos :]} found in " + f"expression at position: {self.pos}" + ) + return expression + + def parse_table_insert_field(self) -> MessageType: + return Message("Mysqlx.Crud.Column", name=self.consume_token(TokenType.IDENT)) + + def parse_table_update_field(self) -> Any: + return self.column_identifier().identifier + + def _table_fields(self) -> List[str]: + fields = [] + temp = self.string.split(",") + temp.reverse() + while temp: + field = temp.pop() + while ( + field.count("(") != field.count(")") + or field.count("[") != field.count("]") + or field.count("{") != field.count("}") + ): + field = f"{temp.pop()},{field}" + fields.append(field.strip()) + return fields + + def parse_table_select_projection( + self, + ) -> List[Union[ProtobufMessageType, ProtobufMessageCextType]]: + project_expr: List = [] + first = True + fields = self._table_fields() + while self.pos < len(self.tokens): + if not first: + self.consume_token(TokenType.COMMA) + first = False + projection = Message("Mysqlx.Crud.Projection", source=self._expr()) + if self.cur_token_type_is(TokenType.AS): + self.consume_token(TokenType.AS) + projection["alias"] = self.consume_token(TokenType.IDENT) + else: + projection["alias"] = fields[len(project_expr)] + project_expr.append(projection.get_message()) + + return project_expr + + def parse_order_spec( + self, + ) -> List[Union[ProtobufMessageType, ProtobufMessageCextType]]: + order_specs = [] + first = True + while self.pos < len(self.tokens): + if not first: + self.consume_token(TokenType.COMMA) + first = False + order = Message("Mysqlx.Crud.Order", expr=self._expr()) + if self.cur_token_type_is(TokenType.ORDERBY_ASC): + order["direction"] = mysqlxpb_enum("Mysqlx.Crud.Order.Direction.ASC") + self.consume_token(TokenType.ORDERBY_ASC) + elif self.cur_token_type_is(TokenType.ORDERBY_DESC): + order["direction"] = mysqlxpb_enum("Mysqlx.Crud.Order.Direction.DESC") + self.consume_token(TokenType.ORDERBY_DESC) + order_specs.append(order.get_message()) + return order_specs + + def parse_expr_list( + self, + ) -> List[Union[ProtobufMessageType, ProtobufMessageCextType]]: + expr_list = [] + first = True + while self.pos < len(self.tokens): + if not first: + self.consume_token(TokenType.COMMA) + first = False + expr_list.append(self._expr().get_message()) + return expr_list diff --git a/virt/lib/python3.9/site-packages/mysqlx/protocol 3.py b/virt/lib/python3.9/site-packages/mysqlx/protocol 3.py new file mode 100644 index 00000000..8cb069a5 --- /dev/null +++ b/virt/lib/python3.9/site-packages/mysqlx/protocol 3.py @@ -0,0 +1,1214 @@ +# Copyright (c) 2016, 2023, Oracle and/or its affiliates. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, as +# published by the Free Software Foundation. +# +# This program is also distributed with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an +# additional permission to link the program and your derivative works +# with the separately licensed software that they have included with +# MySQL. +# +# Without limiting anything contained in the foregoing, this file, +# which is part of MySQL Connector/Python, is also subject to the +# Universal FOSS Exception, version 1.0, a copy of which can be found at +# http://oss.oracle.com/licenses/universal-foss-exception. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License, version 2.0, for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""Implementation of the X protocol for MySQL servers.""" + +import struct +import zlib + +from io import BytesIO +from typing import Any, Dict, List, Optional, Tuple, Union + +try: + import lz4.frame + + HAVE_LZ4 = True +except ImportError: + HAVE_LZ4 = False + +try: + import zstandard as zstd + + HAVE_ZSTD = True +except ImportError: + HAVE_ZSTD = False + +from .errors import ( + InterfaceError, + NotSupportedError, + OperationalError, + ProgrammingError, +) +from .expr import ( + ExprParser, + build_bool_scalar, + build_expr, + build_int_scalar, + build_scalar, + build_unsigned_int_scalar, +) +from .helpers import encode_to_bytes, get_item_or_attr +from .logger import logger +from .protobuf import CRUD_PREPARE_MAPPING, SERVER_MESSAGES, Message, mysqlxpb_enum +from .result import Column +from .statement import ( + AddStatement, + DeleteStatement, + FilterableStatement, + FindStatement, + InsertStatement, + ModifyStatement, + ReadStatement, + RemoveStatement, + SqlStatement, + UpdateStatement, +) +from .types import ( + ColumnType, + MessageType, + ProtobufMessageCextType, + ProtobufMessageType, + ResultBaseType, + SocketType, + StatementType, + StrOrBytes, +) + +_COMPRESSION_THRESHOLD = 1000 + + +class Compressor: + """Implements compression/decompression using `zstd_stream`, `lz4_message` + and `deflate_stream` algorithms. + + Args: + algorithm (str): Compression algorithm. + + .. versionadded:: 8.0.21 + + """ + + def __init__(self, algorithm: str) -> None: + self._algorithm: str = algorithm + self._compressobj: Any = None + self._decompressobj: Any = None + + if algorithm == "zstd_stream": + self._compressobj = zstd.ZstdCompressor() + self._decompressobj = zstd.ZstdDecompressor() + elif algorithm == "deflate_stream": + self._compressobj = zlib.compressobj() + self._decompressobj = zlib.decompressobj() + + def compress(self, data: StrOrBytes) -> bytes: + """Compresses data and returns it. + + Args: + data (str, bytes or buffer object): Data to be compressed. + + Returns: + bytes: Compressed data. + """ + if self._algorithm == "zstd_stream": + return self._compressobj.compress(data) + if self._algorithm == "lz4_message": + with lz4.frame.LZ4FrameCompressor() as compressor: + compressed = compressor.begin() + compressed += compressor.compress(data) + compressed += compressor.flush() + return compressed + + # Using 'deflate_stream' algorithm + compressed = self._compressobj.compress(data) + compressed += self._compressobj.flush(zlib.Z_SYNC_FLUSH) + return compressed + + def decompress(self, data: StrOrBytes) -> bytes: + """Decompresses a frame of data and returns it as a string of bytes. + + Args: + data (str, bytes or buffer object): Data to be compressed. + + Returns: + bytes: Decompresssed data. + """ + if self._algorithm == "zstd_stream": + return self._decompressobj.decompress(data) + if self._algorithm == "lz4_message": + with lz4.frame.LZ4FrameDecompressor() as decompressor: + decompressed = decompressor.decompress(data) + return decompressed + + # Using 'deflate' algorithm + decompressed = self._decompressobj.decompress(data) + decompressed += self._decompressobj.flush(zlib.Z_SYNC_FLUSH) + return decompressed + + +class MessageReader: + """Implements a Message Reader. + + Args: + socket_stream (mysqlx.connection.SocketStream): `SocketStream` object. + + .. versionadded:: 8.0.21 + """ + + def __init__(self, socket_stream: SocketType) -> None: + self._stream: SocketType = socket_stream + self._compressor: Optional[Compressor] = None + self._msg: MessageType = None + self._msg_queue: List[Message] = [] + + def _read_message(self) -> MessageType: + """Reads X Protocol messages from the stream and returns a + :class:`mysqlx.protobuf.Message` object. + + Raises: + :class:`mysqlx.ProgrammingError`: If e connected server does not + have the MySQL X protocol plugin + enabled. + + Returns: + mysqlx.protobuf.Message: MySQL X Protobuf Message. + """ + if self._msg_queue: + return self._msg_queue.pop(0) + + frame_size, frame_type = struct.unpack("<LB", self._stream.read(5)) + + if frame_type == 10: + raise ProgrammingError( + "The connected server does not have the " + "MySQL X protocol plugin enabled or " + "protocol mismatch" + ) + + frame_payload = self._stream.read(frame_size - 1) + if frame_type not in SERVER_MESSAGES: + raise ValueError(f"Unknown message type: {frame_type}") + + # Do not parse empty notices, Message requires a type in payload + if frame_type == 11 and frame_payload == b"": + return self._read_message() + + frame_msg = Message.from_server_message(frame_type, frame_payload) + + if frame_type == 19: # Mysqlx.ServerMessages.Type.COMPRESSION + uncompressed_size = frame_msg["uncompressed_size"] + stream = BytesIO(self._compressor.decompress(frame_msg["payload"])) + bytes_processed = 0 + while bytes_processed < uncompressed_size: + payload_size, msg_type = struct.unpack("<LB", stream.read(5)) + payload = stream.read(payload_size - 1) + self._msg_queue.append(Message.from_server_message(msg_type, payload)) + bytes_processed += payload_size + 4 + return self._msg_queue.pop(0) if self._msg_queue else None + + return frame_msg + + def read_message(self) -> MessageType: + """Read message. + + Returns: + mysqlx.protobuf.Message: MySQL X Protobuf Message. + """ + if self._msg is not None: + msg = self._msg + self._msg = None + return msg + return self._read_message() + + def push_message(self, msg: MessageType) -> None: + """Push message. + + Args: + msg (mysqlx.protobuf.Message): MySQL X Protobuf Message. + + Raises: + :class:`mysqlx.OperationalError`: If message push slot is full. + """ + if self._msg is not None: + raise OperationalError("Message push slot is full") + self._msg = msg + + def set_compression(self, algorithm: str) -> None: + """Creates a :class:`mysqlx.protocol.Compressor` object based on the + compression algorithm. + + Args: + algorithm (str): Compression algorithm. + + .. versionadded:: 8.0.21 + + """ + self._compressor = Compressor(algorithm) if algorithm else None + + +class MessageWriter: + """Implements a Message Writer. + + Args: + socket_stream (mysqlx.connection.SocketStream): `SocketStream` object. + + .. versionadded:: 8.0.21 + + """ + + def __init__(self, socket_stream: SocketType) -> None: + self._stream: SocketType = socket_stream + self._compressor: Optional[Compressor] = None + + def write_message(self, msg_type: int, msg: MessageType) -> None: + """Write message. + + Args: + msg_type (int): The message type. + msg (mysqlx.protobuf.Message): MySQL X Protobuf Message. + """ + msg_size = msg.byte_size(msg) + if self._compressor and msg_size > _COMPRESSION_THRESHOLD: + msg_str = encode_to_bytes(msg.serialize_to_string()) + header = struct.pack("<LB", msg_size + 1, msg_type) + compressed = self._compressor.compress(b"".join([header, msg_str])) + + msg_first_fields = Message("Mysqlx.Connection.Compression") + msg_first_fields["client_messages"] = msg_type + msg_first_fields["uncompressed_size"] = msg_size + 5 + + msg_payload = Message("Mysqlx.Connection.Compression") + msg_payload["payload"] = compressed + + output = b"".join( + [ + encode_to_bytes(msg_first_fields.serialize_partial_to_string())[ + :-2 + ], + encode_to_bytes(msg_payload.serialize_partial_to_string()), + ] + ) + + msg_comp_id = mysqlxpb_enum("Mysqlx.ClientMessages.Type.COMPRESSION") + header = struct.pack("<LB", len(output) + 1, msg_comp_id) + self._stream.sendall(b"".join([header, output])) + else: + msg_str = encode_to_bytes(msg.serialize_to_string()) + header = struct.pack("<LB", msg_size + 1, msg_type) + self._stream.sendall(b"".join([header, msg_str])) + + def set_compression(self, algorithm: str) -> None: + """Creates a :class:`mysqlx.protocol.Compressor` object based on the + compression algorithm. + + Args: + algorithm (str): Compression algorithm. + """ + self._compressor = Compressor(algorithm) if algorithm else None + + +class Protocol: + """Implements the MySQL X Protocol. + + Args: + read (mysqlx.protocol.MessageReader): A Message Reader object. + writer (mysqlx.protocol.MessageWriter): A Message Writer object. + + .. versionchanged:: 8.0.21 + """ + + def __init__(self, reader: MessageReader, writer: MessageWriter) -> None: + self._reader: MessageReader = reader + self._writer: MessageWriter = writer + self._compression_algorithm: Optional[str] = None + self._warnings: List[str] = [] + + @property + def compression_algorithm(self) -> Optional[str]: + """str: The compresion algorithm.""" + return self._compression_algorithm + + @staticmethod + def _apply_filter(msg: MessageType, stmt: FilterableStatement) -> None: + """Apply filter. + + Args: + msg (mysqlx.protobuf.Message): The MySQL X Protobuf Message. + stmt (Statement): A `Statement` based type object. + """ + if stmt.has_where: + msg["criteria"] = stmt.get_where_expr() + if stmt.has_sort: + msg["order"].extend(stmt.get_sort_expr()) + if stmt.has_group_by: + msg["grouping"].extend(stmt.get_grouping()) + if stmt.has_having: + msg["grouping_criteria"] = stmt.get_having() + + def _create_any(self, arg: Any) -> Optional[MessageType]: + """Create any. + + Args: + arg (object): Arbitrary object. + + Returns: + mysqlx.protobuf.Message: MySQL X Protobuf Message. + """ + if isinstance(arg, str): + value = Message("Mysqlx.Datatypes.Scalar.String", value=arg) + scalar = Message("Mysqlx.Datatypes.Scalar", type=8, v_string=value) + return Message("Mysqlx.Datatypes.Any", type=1, scalar=scalar) + if isinstance(arg, bool): + return Message( + "Mysqlx.Datatypes.Any", type=1, scalar=build_bool_scalar(arg) + ) + if isinstance(arg, int): + if arg < 0: + return Message( + "Mysqlx.Datatypes.Any", + type=1, + scalar=build_int_scalar(arg), + ) + return Message( + "Mysqlx.Datatypes.Any", + type=1, + scalar=build_unsigned_int_scalar(arg), + ) + if isinstance(arg, tuple) and len(arg) == 2: + arg_key, arg_value = arg + obj_fld = Message( + "Mysqlx.Datatypes.Object.ObjectField", + key=arg_key, + value=self._create_any(arg_value), + ) + obj = Message("Mysqlx.Datatypes.Object", fld=[obj_fld.get_message()]) + return Message("Mysqlx.Datatypes.Any", type=2, obj=obj) + if isinstance(arg, dict) or ( + isinstance(arg, (list, tuple)) and isinstance(arg[0], dict) + ): + array_values = [] + for items in arg: + obj_flds = [] + for key, value in items.items(): + # Array can only handle Any types, Mysqlx.Datatypes.Any.obj + obj_fld = Message( + "Mysqlx.Datatypes.Object.ObjectField", + key=key, + value=self._create_any(value), + ) + obj_flds.append(obj_fld.get_message()) + msg_obj = Message("Mysqlx.Datatypes.Object", fld=obj_flds) + msg_any = Message("Mysqlx.Datatypes.Any", type=2, obj=msg_obj) + array_values.append(msg_any.get_message()) + + msg = Message("Mysqlx.Datatypes.Array") + msg["value"] = array_values + return Message("Mysqlx.Datatypes.Any", type=3, array=msg) + if isinstance(arg, list): + obj_flds = [] + for key, value in arg: + obj_fld = Message( + "Mysqlx.Datatypes.Object.ObjectField", + key=key, + value=self._create_any(value), + ) + obj_flds.append(obj_fld.get_message()) + msg_obj = Message("Mysqlx.Datatypes.Object", fld=obj_flds) + msg_any = Message("Mysqlx.Datatypes.Any", type=2, obj=msg_obj) + return msg_any + + return None + + def _get_binding_args( + self, stmt: Union[FilterableStatement, SqlStatement], is_scalar: bool = True + ) -> Union[List[None], List[Union[ProtobufMessageType, ProtobufMessageCextType]]]: + """Returns the binding any/scalar. + + Args: + stmt (Statement): A `Statement` based type object. + is_scalar (bool): `True` to return scalar values. + + Raises: + :class:`mysqlx.ProgrammingError`: If unable to find placeholder for + parameter. + + Returns: + list: A list of ``Any`` or ``Scalar`` objects. + """ + + def build_value( + value: Any, + ) -> Union[ProtobufMessageType, ProtobufMessageCextType]: + if is_scalar: + return build_scalar(value).get_message() + return self._create_any(value).get_message() + + bindings = stmt.get_bindings() + binding_map = stmt.get_binding_map() + + # If binding_map is None it's a SqlStatement object + if binding_map is None: + return [build_value(value) for value in bindings] + + count = len(binding_map) + args: List[Any] = count * [None] + if count != len(bindings): + raise ProgrammingError( + "The number of bind parameters and placeholders do not match" + ) + for name, value in bindings.items(): # type: ignore[union-attr] + if name not in binding_map: + raise ProgrammingError( + f"Unable to find placeholder for parameter: {name}" + ) + pos = binding_map[name] + args[pos] = build_value(value) + return args + + def _process_frame(self, msg: MessageType, result: ResultBaseType) -> None: + """Process frame. + + Args: + msg (mysqlx.protobuf.Message): A MySQL X Protobuf Message. + result (Result): A `Result` based type object. + """ + if msg["type"] == 1: + warn_msg = Message.from_message("Mysqlx.Notice.Warning", msg["payload"]) + self._warnings.append(warn_msg.msg) + logger.warning( + "Protocol.process_frame Received Warning Notice code %s: %s", + warn_msg.code, + warn_msg.msg, + ) + result.append_warning(warn_msg.level, warn_msg.code, warn_msg.msg) + elif msg["type"] == 2: + Message.from_message("Mysqlx.Notice.SessionVariableChanged", msg["payload"]) + elif msg["type"] == 3: + sess_state_msg = Message.from_message( + "Mysqlx.Notice.SessionStateChanged", msg["payload"] + ) + if sess_state_msg["param"] == mysqlxpb_enum( + "Mysqlx.Notice.SessionStateChanged.Parameter.GENERATED_DOCUMENT_IDS" + ): + result.set_generated_ids( + [ + get_item_or_attr( + get_item_or_attr(value, "v_octets"), "value" + ).decode() + for value in sess_state_msg["value"] + ] + ) + else: # Following results are unitary and not a list + sess_state_value = sess_state_msg["value"].pop() + if sess_state_msg["param"] == mysqlxpb_enum( + "Mysqlx.Notice.SessionStateChanged.Parameter.ROWS_AFFECTED" + ): + result.set_rows_affected( + get_item_or_attr(sess_state_value, "v_unsigned_int") + ) + elif sess_state_msg["param"] == mysqlxpb_enum( + "Mysqlx.Notice.SessionStateChanged.Parameter.GENERATED_INSERT_ID" + ): + result.set_generated_insert_id( + get_item_or_attr(sess_state_value, "v_unsigned_int") + ) + + def _read_message(self, result: ResultBaseType) -> Optional[MessageType]: + """Read message. + + Args: + result (Result): A `Result` based type object. + """ + while True: + try: + msg = self._reader.read_message() + except RuntimeError as err: + warnings = repr(result.get_warnings()) + if warnings: + raise RuntimeError(f"{err} reason: {warnings}") from err + if msg.type == "Mysqlx.Error": + raise OperationalError(msg["msg"], msg["code"]) + if msg.type == "Mysqlx.Notice.Frame": + try: + self._process_frame(msg, result) + except (AttributeError, KeyError): + continue + elif msg.type == "Mysqlx.Sql.StmtExecuteOk": + return None + elif msg.type == "Mysqlx.Resultset.FetchDone": + result.set_closed(True) + elif msg.type == "Mysqlx.Resultset.FetchDoneMoreResultsets": + result.set_has_more_results(True) + elif msg.type == "Mysqlx.Resultset.Row": + result.set_has_data(True) + break + else: + break + return msg + + def set_compression(self, algorithm: str) -> None: + """Sets the compression algorithm to be used by the compression + object, for uplink and downlink. + + Args: + algorithm (str): Algorithm to be used in compression/decompression. + + .. versionadded:: 8.0.21 + + """ + self._compression_algorithm = algorithm + self._reader.set_compression(algorithm) + self._writer.set_compression(algorithm) + + def get_capabilites(self) -> MessageType: + """Get capabilities. + + Returns: + mysqlx.protobuf.Message: MySQL X Protobuf Message. + """ + msg = Message("Mysqlx.Connection.CapabilitiesGet") + self._writer.write_message( + mysqlxpb_enum("Mysqlx.ClientMessages.Type.CON_CAPABILITIES_GET"), + msg, + ) + msg = self._reader.read_message() + while msg.type == "Mysqlx.Notice.Frame": + msg = self._reader.read_message() + + if msg.type == "Mysqlx.Error": + raise OperationalError(msg["msg"], msg["code"]) + + return msg + + def set_capabilities(self, **kwargs: Any) -> None: + """Set capabilities. + + Args: + **kwargs: Arbitrary keyword arguments. + + Returns: + mysqlx.protobuf.Message: MySQL X Protobuf Message. + """ + if not kwargs: + return None + capabilities = Message("Mysqlx.Connection.Capabilities") + for key, value in kwargs.items(): + capability = Message("Mysqlx.Connection.Capability") + capability["name"] = key + if isinstance(value, dict): + items = value + obj_flds = [] + for item in items: + obj_fld = Message( + "Mysqlx.Datatypes.Object.ObjectField", + key=item, + value=self._create_any(items[item]), + ) + obj_flds.append(obj_fld.get_message()) + msg_obj = Message("Mysqlx.Datatypes.Object", fld=obj_flds) + msg_any = Message("Mysqlx.Datatypes.Any", type=2, obj=msg_obj) + capability["value"] = msg_any.get_message() + else: + capability["value"] = self._create_any(value) + + capabilities["capabilities"].extend([capability.get_message()]) + msg = Message("Mysqlx.Connection.CapabilitiesSet") + msg["capabilities"] = capabilities + self._writer.write_message( + mysqlxpb_enum("Mysqlx.ClientMessages.Type.CON_CAPABILITIES_SET"), + msg, + ) + + try: + return self.read_ok() + except InterfaceError as err: + # Skip capability "session_connect_attrs" error since + # is only available on version >= 8.0.16 + if err.errno != 5002: + raise + return None + + def send_auth_start( + self, + method: str, + auth_data: Optional[str] = None, + initial_response: Optional[str] = None, + ) -> None: + """Send authenticate start. + + Args: + method (str): Message method. + auth_data (Optional[str]): Authentication data. + initial_response (Optional[str]): Initial response. + """ + msg = Message("Mysqlx.Session.AuthenticateStart") + msg["mech_name"] = method + if auth_data is not None: + msg["auth_data"] = auth_data + if initial_response is not None: + msg["initial_response"] = initial_response + self._writer.write_message( + mysqlxpb_enum("Mysqlx.ClientMessages.Type.SESS_AUTHENTICATE_START"), + msg, + ) + + def read_auth_continue(self) -> bytes: + """Read authenticate continue. + + Raises: + :class:`InterfaceError`: If the message type is not + `Mysqlx.Session.AuthenticateContinue` + + Returns: + str: The authentication data. + """ + msg = self._reader.read_message() + while msg.type == "Mysqlx.Notice.Frame": + msg = self._reader.read_message() + if msg.type != "Mysqlx.Session.AuthenticateContinue": + raise InterfaceError( + "Unexpected message encountered during authentication handshake" + ) + return msg["auth_data"] + + def send_auth_continue(self, auth_data: str) -> None: + """Send authenticate continue. + + Args: + auth_data (str): Authentication data. + """ + msg = Message("Mysqlx.Session.AuthenticateContinue", auth_data=auth_data) + self._writer.write_message( + mysqlxpb_enum("Mysqlx.ClientMessages.Type.SESS_AUTHENTICATE_CONTINUE"), + msg, + ) + + def read_auth_ok(self) -> None: + """Read authenticate OK. + + Raises: + :class:`mysqlx.InterfaceError`: If message type is `Mysqlx.Error`. + """ + while True: + msg = self._reader.read_message() + if msg.type == "Mysqlx.Session.AuthenticateOk": + break + if msg.type == "Mysqlx.Error": + raise InterfaceError(msg.msg) + + def send_prepare_prepare( + self, + msg_type: str, + msg: MessageType, + stmt: Union[ + FindStatement, + DeleteStatement, + ModifyStatement, + ReadStatement, + RemoveStatement, + UpdateStatement, + ], + ) -> None: + """ + Send prepare statement. + + Args: + msg_type (str): Message ID string. + msg (mysqlx.protobuf.Message): MySQL X Protobuf Message. + stmt (Statement): A `Statement` based type object. + + Raises: + :class:`mysqlx.NotSupportedError`: If prepared statements are not + supported. + + .. versionadded:: 8.0.16 + """ + if stmt.has_limit and msg.type != "Mysqlx.Crud.Insert": + # Remove 'limit' from message by building a new one + if msg.type == "Mysqlx.Crud.Find": + _, msg = self.build_find(stmt) # type: ignore[arg-type] + elif msg.type == "Mysqlx.Crud.Update": + _, msg = self.build_update(stmt) # type: ignore[arg-type] + elif msg.type == "Mysqlx.Crud.Delete": + _, msg = self.build_delete(stmt) # type: ignore[arg-type] + else: + raise ValueError(f"Invalid message type: {msg_type}") + # Build 'limit_expr' message + position = len(stmt.get_bindings()) + placeholder = mysqlxpb_enum("Mysqlx.Expr.Expr.Type.PLACEHOLDER") + msg_limit_expr = Message("Mysqlx.Crud.LimitExpr") + msg_limit_expr["row_count"] = Message( + "Mysqlx.Expr.Expr", type=placeholder, position=position + ) + if msg.type == "Mysqlx.Crud.Find": + msg_limit_expr["offset"] = Message( + "Mysqlx.Expr.Expr", type=placeholder, position=position + 1 + ) + msg["limit_expr"] = msg_limit_expr + + oneof_type, oneof_op = CRUD_PREPARE_MAPPING[msg_type] + msg_oneof = Message("Mysqlx.Prepare.Prepare.OneOfMessage") + msg_oneof["type"] = mysqlxpb_enum(oneof_type) + msg_oneof[oneof_op] = msg + msg_prepare = Message("Mysqlx.Prepare.Prepare") + msg_prepare["stmt_id"] = stmt.stmt_id + msg_prepare["stmt"] = msg_oneof + + self._writer.write_message( + mysqlxpb_enum("Mysqlx.ClientMessages.Type.PREPARE_PREPARE"), + msg_prepare, + ) + + try: + self.read_ok() + except InterfaceError as err: + raise NotSupportedError from err + + def send_prepare_execute( + self, msg_type: str, msg: MessageType, stmt: FilterableStatement + ) -> None: + """ + Send execute statement. + + Args: + msg_type (str): Message ID string. + msg (mysqlx.protobuf.Message): MySQL X Protobuf Message. + stmt (Statement): A `Statement` based type object. + + .. versionadded:: 8.0.16 + """ + oneof_type, oneof_op = CRUD_PREPARE_MAPPING[msg_type] + msg_oneof = Message("Mysqlx.Prepare.Prepare.OneOfMessage") + msg_oneof["type"] = mysqlxpb_enum(oneof_type) + msg_oneof[oneof_op] = msg + msg_execute = Message("Mysqlx.Prepare.Execute") + msg_execute["stmt_id"] = stmt.stmt_id + + args = self._get_binding_args(stmt, is_scalar=False) + if args: + msg_execute["args"].extend(args) + + if stmt.has_limit: + msg_execute["args"].extend( + [ + self._create_any(stmt.get_limit_row_count()).get_message(), + self._create_any(stmt.get_limit_offset()).get_message(), + ] + ) + + self._writer.write_message( + mysqlxpb_enum("Mysqlx.ClientMessages.Type.PREPARE_EXECUTE"), + msg_execute, + ) + + def send_prepare_deallocate(self, stmt_id: int) -> None: + """ + Send prepare deallocate statement. + + Args: + stmt_id (int): Statement ID. + + .. versionadded:: 8.0.16 + """ + msg_dealloc = Message("Mysqlx.Prepare.Deallocate") + msg_dealloc["stmt_id"] = stmt_id + self._writer.write_message( + mysqlxpb_enum("Mysqlx.ClientMessages.Type.PREPARE_DEALLOCATE"), + msg_dealloc, + ) + self.read_ok() + + def send_msg_without_ps( + self, + msg_type: str, + msg: MessageType, + stmt: Union[FilterableStatement, SqlStatement], + ) -> None: + """ + Send a message without prepared statements support. + + Args: + msg_type (str): Message ID string. + msg (mysqlx.protobuf.Message): MySQL X Protobuf Message. + stmt (Statement): A `Statement` based type object. + + .. versionadded:: 8.0.16 + """ + if stmt.has_limit: + msg_limit = Message("Mysqlx.Crud.Limit") + msg_limit["row_count"] = stmt.get_limit_row_count() # type: ignore[union-attr] + if msg.type == "Mysqlx.Crud.Find": + msg_limit["offset"] = stmt.get_limit_offset() # type: ignore[union-attr] + msg["limit"] = msg_limit + is_scalar = msg_type != "Mysqlx.ClientMessages.Type.SQL_STMT_EXECUTE" + args = self._get_binding_args(stmt, is_scalar=is_scalar) + if args: + msg["args"].extend(args) + self.send_msg(msg_type, msg) + + def send_msg(self, msg_type: str, msg: MessageType) -> None: + """ + Send a message. + + Args: + msg_type (str): Message ID string. + msg (mysqlx.protobuf.Message): MySQL X Protobuf Message. + + .. versionadded:: 8.0.16 + """ + self._writer.write_message(mysqlxpb_enum(msg_type), msg) + + def build_find( + self, stmt: Union[FindStatement, ReadStatement] + ) -> Tuple[str, MessageType]: + """Build find/read message. + + Args: + stmt (Statement): A :class:`mysqlx.ReadStatement` or + :class:`mysqlx.FindStatement` object. + + Returns: + (tuple): Tuple containing: + + * `str`: Message ID string. + * :class:`mysqlx.protobuf.Message`: MySQL X Protobuf Message. + + .. versionadded:: 8.0.16 + """ + data_model = mysqlxpb_enum( + "Mysqlx.Crud.DataModel.DOCUMENT" + if stmt.is_doc_based() + else "Mysqlx.Crud.DataModel.TABLE" + ) + collection = Message( + "Mysqlx.Crud.Collection", + name=stmt.target.name, + schema=stmt.schema.name, + ) + msg = Message("Mysqlx.Crud.Find", data_model=data_model, collection=collection) + if stmt.has_projection: + msg["projection"] = stmt.get_projection_expr() + self._apply_filter(msg, stmt) + + if stmt.is_lock_exclusive(): + msg["locking"] = mysqlxpb_enum("Mysqlx.Crud.Find.RowLock.EXCLUSIVE_LOCK") + elif stmt.is_lock_shared(): + msg["locking"] = mysqlxpb_enum("Mysqlx.Crud.Find.RowLock.SHARED_LOCK") + + if stmt.lock_contention.value > 0: + msg["locking_options"] = stmt.lock_contention.value + + return "Mysqlx.ClientMessages.Type.CRUD_FIND", msg + + def build_update( + self, stmt: Union[ModifyStatement, UpdateStatement] + ) -> Tuple[str, MessageType]: + """Build update message. + + Args: + stmt (Statement): A :class:`mysqlx.ModifyStatement` or + :class:`mysqlx.UpdateStatement` object. + + Returns: + (tuple): Tuple containing: + + * `str`: Message ID string. + * :class:`mysqlx.protobuf.Message`: MySQL X Protobuf Message. + + .. versionadded:: 8.0.16 + """ + data_model = mysqlxpb_enum( + "Mysqlx.Crud.DataModel.DOCUMENT" + if stmt.is_doc_based() + else "Mysqlx.Crud.DataModel.TABLE" + ) + collection = Message( + "Mysqlx.Crud.Collection", + name=stmt.target.name, + schema=stmt.schema.name, + ) + msg = Message( + "Mysqlx.Crud.Update", data_model=data_model, collection=collection + ) + self._apply_filter(msg, stmt) + for _, update_op in stmt.get_update_ops().items(): + operation = Message("Mysqlx.Crud.UpdateOperation") + operation["operation"] = update_op.update_type + operation["source"] = update_op.source + if update_op.value is not None: + operation["value"] = build_expr(update_op.value) + msg["operation"].extend([operation.get_message()]) + + return "Mysqlx.ClientMessages.Type.CRUD_UPDATE", msg + + def build_delete( + self, stmt: Union[DeleteStatement, RemoveStatement] + ) -> Tuple[str, MessageType]: + """Build delete message. + + Args: + stmt (Statement): A :class:`mysqlx.DeleteStatement` or + :class:`mysqlx.RemoveStatement` object. + + Returns: + (tuple): Tuple containing: + + * `str`: Message ID string. + * :class:`mysqlx.protobuf.Message`: MySQL X Protobuf Message. + + .. versionadded:: 8.0.16 + """ + data_model = mysqlxpb_enum( + "Mysqlx.Crud.DataModel.DOCUMENT" + if stmt.is_doc_based() + else "Mysqlx.Crud.DataModel.TABLE" + ) + collection = Message( + "Mysqlx.Crud.Collection", + name=stmt.target.name, + schema=stmt.schema.name, + ) + msg = Message( + "Mysqlx.Crud.Delete", data_model=data_model, collection=collection + ) + self._apply_filter(msg, stmt) + return "Mysqlx.ClientMessages.Type.CRUD_DELETE", msg + + def build_execute_statement( + self, + namespace: str, + stmt: Union[str, StatementType], + fields: Optional[Dict[str, Any]] = None, + ) -> Tuple[str, MessageType]: + """Build execute statement. + + Args: + namespace (str): The namespace. + stmt (Statement): A `Statement` based type object. + fields (Optional[dict]): The message fields. + + Returns: + (tuple): Tuple containing: + + * `str`: Message ID string. + * :class:`mysqlx.protobuf.Message`: MySQL X Protobuf Message. + + .. versionadded:: 8.0.16 + """ + msg = Message( + "Mysqlx.Sql.StmtExecute", + namespace=namespace, + stmt=stmt, + compact_metadata=False, + ) + + if fields: + obj_flds = [] + for key, value in fields.items(): + obj_fld = Message( + "Mysqlx.Datatypes.Object.ObjectField", + key=key, + value=self._create_any(value), + ) + obj_flds.append(obj_fld.get_message()) + msg_obj = Message("Mysqlx.Datatypes.Object", fld=obj_flds) + msg_any = Message("Mysqlx.Datatypes.Any", type=2, obj=msg_obj) + msg["args"] = [msg_any.get_message()] + return "Mysqlx.ClientMessages.Type.SQL_STMT_EXECUTE", msg + + @staticmethod + def build_insert( + stmt: Union[AddStatement, InsertStatement] + ) -> Tuple[str, MessageType]: + """Build insert statement. + + Args: + stmt (Statement): A :class:`mysqlx.AddStatement` or + :class:`mysqlx.InsertStatement` object. + + Returns: + (tuple): Tuple containing: + + * `str`: Message ID string. + * :class:`mysqlx.protobuf.Message`: MySQL X Protobuf Message. + + .. versionadded:: 8.0.16 + """ + data_model = mysqlxpb_enum( + "Mysqlx.Crud.DataModel.DOCUMENT" + if stmt.is_doc_based() + else "Mysqlx.Crud.DataModel.TABLE" + ) + collection = Message( + "Mysqlx.Crud.Collection", + name=stmt.target.name, + schema=stmt.schema.name, + ) + msg = Message( + "Mysqlx.Crud.Insert", data_model=data_model, collection=collection + ) + + if hasattr(stmt, "_fields"): + for field in stmt._fields: + expr = ExprParser( + field, not stmt.is_doc_based() + ).parse_table_insert_field() + msg["projection"].extend([expr.get_message()]) + + for value in stmt.get_values(): + row = Message("Mysqlx.Crud.Insert.TypedRow") + if isinstance(value, list): + for val in value: + row["field"].extend([build_expr(val).get_message()]) + else: + row["field"].extend([build_expr(value).get_message()]) + msg["row"].extend([row.get_message()]) + + if hasattr(stmt, "is_upsert"): + msg["upsert"] = stmt.is_upsert() + + return "Mysqlx.ClientMessages.Type.CRUD_INSERT", msg + + def close_result(self, result: ResultBaseType) -> None: + """Close the result. + + Args: + result (Result): A `Result` based type object. + + Raises: + :class:`mysqlx.OperationalError`: If message read is None. + """ + msg = self._read_message(result) + if msg is not None: + raise OperationalError("Expected to close the result") + + def read_row(self, result: ResultBaseType) -> Optional[MessageType]: + """Read row. + + Args: + result (Result): A `Result` based type object. + """ + msg = self._read_message(result) + if msg is None: + return None + if msg.type == "Mysqlx.Resultset.Row": + return msg + self._reader.push_message(msg) + return None + + def get_column_metadata(self, result: ResultBaseType) -> List[ColumnType]: + """Returns column metadata. + + Args: + result (Result): A `Result` based type object. + + Raises: + :class:`mysqlx.InterfaceError`: If unexpected message. + """ + columns = [] + while True: + msg = self._read_message(result) + if msg is None: + break + if msg.type == "Mysqlx.Resultset.Row": + self._reader.push_message(msg) + break + if msg.type != "Mysqlx.Resultset.ColumnMetaData": + raise InterfaceError("Unexpected msg type") + col = Column( + msg["type"], + msg["catalog"], + msg["schema"], + msg["table"], + msg["original_table"], + msg["name"], + msg["original_name"], + msg.get("length", 21), + msg.get("collation", 0), + msg.get("fractional_digits", 0), + msg.get("flags", 16), + msg.get("content_type"), + ) + columns.append(col) + return columns + + def read_ok(self) -> None: + """Read OK. + + Raises: + :class:`mysqlx.InterfaceError`: If unexpected message. + """ + msg = self._reader.read_message() + if msg.type == "Mysqlx.Error": + raise InterfaceError(f"Mysqlx.Error: {msg['msg']}", errno=msg["code"]) + if msg.type != "Mysqlx.Ok": + raise InterfaceError("Unexpected message encountered") + + def send_connection_close(self) -> None: + """Send connection close.""" + msg = Message("Mysqlx.Connection.Close") + self._writer.write_message( + mysqlxpb_enum("Mysqlx.ClientMessages.Type.CON_CLOSE"), msg + ) + + def send_close(self) -> None: + """Send close.""" + msg = Message("Mysqlx.Session.Close") + self._writer.write_message( + mysqlxpb_enum("Mysqlx.ClientMessages.Type.SESS_CLOSE"), msg + ) + + def send_expect_open(self) -> None: + """Send expectation.""" + cond_key = mysqlxpb_enum("Mysqlx.Expect.Open.Condition.Key.EXPECT_FIELD_EXIST") + msg_oc = Message("Mysqlx.Expect.Open.Condition") + msg_oc["condition_key"] = cond_key + msg_oc["condition_value"] = "6.1" + + msg_eo = Message("Mysqlx.Expect.Open") + msg_eo["cond"] = [msg_oc.get_message()] + + self._writer.write_message( + mysqlxpb_enum("Mysqlx.ClientMessages.Type.EXPECT_OPEN"), msg_eo + ) + + def send_reset(self, keep_open: Optional[bool] = None) -> bool: + """Send reset session message. + + Returns: + boolean: ``True`` if the server will keep the session open, + otherwise ``False``. + """ + msg = Message("Mysqlx.Session.Reset") + if keep_open is None: + try: + # Send expectation: keep connection open + self.send_expect_open() + self.read_ok() + keep_open = True + except InterfaceError: + # Expectation is unkown by this version of the server + keep_open = False + if keep_open: + msg["keep_open"] = True + self._writer.write_message( + mysqlxpb_enum("Mysqlx.ClientMessages.Type.SESS_RESET"), msg + ) + self.read_ok() + if keep_open: + return True + return False diff --git a/virt/lib/python3.9/site-packages/pip/_internal/index/package_finder 3.py b/virt/lib/python3.9/site-packages/pip/_internal/index/package_finder 3.py new file mode 100644 index 00000000..aaad3a5a --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_internal/index/package_finder 3.py @@ -0,0 +1,1029 @@ +"""Routines related to PyPI, indexes""" + +import enum +import functools +import itertools +import logging +import re +from typing import TYPE_CHECKING, FrozenSet, Iterable, List, Optional, Set, Tuple, Union + +from pip._vendor.packaging import specifiers +from pip._vendor.packaging.tags import Tag +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import _BaseVersion +from pip._vendor.packaging.version import parse as parse_version + +from pip._internal.exceptions import ( + BestVersionAlreadyInstalled, + DistributionNotFound, + InvalidWheelFilename, + UnsupportedWheel, +) +from pip._internal.index.collector import LinkCollector, parse_links +from pip._internal.models.candidate import InstallationCandidate +from pip._internal.models.format_control import FormatControl +from pip._internal.models.link import Link +from pip._internal.models.search_scope import SearchScope +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.models.target_python import TargetPython +from pip._internal.models.wheel import Wheel +from pip._internal.req import InstallRequirement +from pip._internal.utils._log import getLogger +from pip._internal.utils.filetypes import WHEEL_EXTENSION +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import build_netloc +from pip._internal.utils.packaging import check_requires_python +from pip._internal.utils.unpacking import SUPPORTED_EXTENSIONS + +if TYPE_CHECKING: + from pip._vendor.typing_extensions import TypeGuard + +__all__ = ["FormatControl", "BestCandidateResult", "PackageFinder"] + + +logger = getLogger(__name__) + +BuildTag = Union[Tuple[()], Tuple[int, str]] +CandidateSortingKey = Tuple[int, int, int, _BaseVersion, Optional[int], BuildTag] + + +def _check_link_requires_python( + link: Link, + version_info: Tuple[int, int, int], + ignore_requires_python: bool = False, +) -> bool: + """ + Return whether the given Python version is compatible with a link's + "Requires-Python" value. + + :param version_info: A 3-tuple of ints representing the Python + major-minor-micro version to check. + :param ignore_requires_python: Whether to ignore the "Requires-Python" + value if the given Python version isn't compatible. + """ + try: + is_compatible = check_requires_python( + link.requires_python, + version_info=version_info, + ) + except specifiers.InvalidSpecifier: + logger.debug( + "Ignoring invalid Requires-Python (%r) for link: %s", + link.requires_python, + link, + ) + else: + if not is_compatible: + version = ".".join(map(str, version_info)) + if not ignore_requires_python: + logger.verbose( + "Link requires a different Python (%s not in: %r): %s", + version, + link.requires_python, + link, + ) + return False + + logger.debug( + "Ignoring failed Requires-Python check (%s not in: %r) for link: %s", + version, + link.requires_python, + link, + ) + + return True + + +class LinkType(enum.Enum): + candidate = enum.auto() + different_project = enum.auto() + yanked = enum.auto() + format_unsupported = enum.auto() + format_invalid = enum.auto() + platform_mismatch = enum.auto() + requires_python_mismatch = enum.auto() + + +class LinkEvaluator: + + """ + Responsible for evaluating links for a particular project. + """ + + _py_version_re = re.compile(r"-py([123]\.?[0-9]?)$") + + # Don't include an allow_yanked default value to make sure each call + # site considers whether yanked releases are allowed. This also causes + # that decision to be made explicit in the calling code, which helps + # people when reading the code. + def __init__( + self, + project_name: str, + canonical_name: str, + formats: FrozenSet[str], + target_python: TargetPython, + allow_yanked: bool, + ignore_requires_python: Optional[bool] = None, + ) -> None: + """ + :param project_name: The user supplied package name. + :param canonical_name: The canonical package name. + :param formats: The formats allowed for this package. Should be a set + with 'binary' or 'source' or both in it. + :param target_python: The target Python interpreter to use when + evaluating link compatibility. This is used, for example, to + check wheel compatibility, as well as when checking the Python + version, e.g. the Python version embedded in a link filename + (or egg fragment) and against an HTML link's optional PEP 503 + "data-requires-python" attribute. + :param allow_yanked: Whether files marked as yanked (in the sense + of PEP 592) are permitted to be candidates for install. + :param ignore_requires_python: Whether to ignore incompatible + PEP 503 "data-requires-python" values in HTML links. Defaults + to False. + """ + if ignore_requires_python is None: + ignore_requires_python = False + + self._allow_yanked = allow_yanked + self._canonical_name = canonical_name + self._ignore_requires_python = ignore_requires_python + self._formats = formats + self._target_python = target_python + + self.project_name = project_name + + def evaluate_link(self, link: Link) -> Tuple[LinkType, str]: + """ + Determine whether a link is a candidate for installation. + + :return: A tuple (result, detail), where *result* is an enum + representing whether the evaluation found a candidate, or the reason + why one is not found. If a candidate is found, *detail* will be the + candidate's version string; if one is not found, it contains the + reason the link fails to qualify. + """ + version = None + if link.is_yanked and not self._allow_yanked: + reason = link.yanked_reason or "<none given>" + return (LinkType.yanked, f"yanked for reason: {reason}") + + if link.egg_fragment: + egg_info = link.egg_fragment + ext = link.ext + else: + egg_info, ext = link.splitext() + if not ext: + return (LinkType.format_unsupported, "not a file") + if ext not in SUPPORTED_EXTENSIONS: + return ( + LinkType.format_unsupported, + f"unsupported archive format: {ext}", + ) + if "binary" not in self._formats and ext == WHEEL_EXTENSION: + reason = f"No binaries permitted for {self.project_name}" + return (LinkType.format_unsupported, reason) + if "macosx10" in link.path and ext == ".zip": + return (LinkType.format_unsupported, "macosx10 one") + if ext == WHEEL_EXTENSION: + try: + wheel = Wheel(link.filename) + except InvalidWheelFilename: + return ( + LinkType.format_invalid, + "invalid wheel filename", + ) + if canonicalize_name(wheel.name) != self._canonical_name: + reason = f"wrong project name (not {self.project_name})" + return (LinkType.different_project, reason) + + supported_tags = self._target_python.get_tags() + if not wheel.supported(supported_tags): + # Include the wheel's tags in the reason string to + # simplify troubleshooting compatibility issues. + file_tags = ", ".join(wheel.get_formatted_file_tags()) + reason = ( + f"none of the wheel's tags ({file_tags}) are compatible " + f"(run pip debug --verbose to show compatible tags)" + ) + return (LinkType.platform_mismatch, reason) + + version = wheel.version + + # This should be up by the self.ok_binary check, but see issue 2700. + if "source" not in self._formats and ext != WHEEL_EXTENSION: + reason = f"No sources permitted for {self.project_name}" + return (LinkType.format_unsupported, reason) + + if not version: + version = _extract_version_from_fragment( + egg_info, + self._canonical_name, + ) + if not version: + reason = f"Missing project version for {self.project_name}" + return (LinkType.format_invalid, reason) + + match = self._py_version_re.search(version) + if match: + version = version[: match.start()] + py_version = match.group(1) + if py_version != self._target_python.py_version: + return ( + LinkType.platform_mismatch, + "Python version is incorrect", + ) + + supports_python = _check_link_requires_python( + link, + version_info=self._target_python.py_version_info, + ignore_requires_python=self._ignore_requires_python, + ) + if not supports_python: + reason = f"{version} Requires-Python {link.requires_python}" + return (LinkType.requires_python_mismatch, reason) + + logger.debug("Found link %s, version: %s", link, version) + + return (LinkType.candidate, version) + + +def filter_unallowed_hashes( + candidates: List[InstallationCandidate], + hashes: Optional[Hashes], + project_name: str, +) -> List[InstallationCandidate]: + """ + Filter out candidates whose hashes aren't allowed, and return a new + list of candidates. + + If at least one candidate has an allowed hash, then all candidates with + either an allowed hash or no hash specified are returned. Otherwise, + the given candidates are returned. + + Including the candidates with no hash specified when there is a match + allows a warning to be logged if there is a more preferred candidate + with no hash specified. Returning all candidates in the case of no + matches lets pip report the hash of the candidate that would otherwise + have been installed (e.g. permitting the user to more easily update + their requirements file with the desired hash). + """ + if not hashes: + logger.debug( + "Given no hashes to check %s links for project %r: " + "discarding no candidates", + len(candidates), + project_name, + ) + # Make sure we're not returning back the given value. + return list(candidates) + + matches_or_no_digest = [] + # Collect the non-matches for logging purposes. + non_matches = [] + match_count = 0 + for candidate in candidates: + link = candidate.link + if not link.has_hash: + pass + elif link.is_hash_allowed(hashes=hashes): + match_count += 1 + else: + non_matches.append(candidate) + continue + + matches_or_no_digest.append(candidate) + + if match_count: + filtered = matches_or_no_digest + else: + # Make sure we're not returning back the given value. + filtered = list(candidates) + + if len(filtered) == len(candidates): + discard_message = "discarding no candidates" + else: + discard_message = "discarding {} non-matches:\n {}".format( + len(non_matches), + "\n ".join(str(candidate.link) for candidate in non_matches), + ) + + logger.debug( + "Checked %s links for project %r against %s hashes " + "(%s matches, %s no digest): %s", + len(candidates), + project_name, + hashes.digest_count, + match_count, + len(matches_or_no_digest) - match_count, + discard_message, + ) + + return filtered + + +class CandidatePreferences: + + """ + Encapsulates some of the preferences for filtering and sorting + InstallationCandidate objects. + """ + + def __init__( + self, + prefer_binary: bool = False, + allow_all_prereleases: bool = False, + ) -> None: + """ + :param allow_all_prereleases: Whether to allow all pre-releases. + """ + self.allow_all_prereleases = allow_all_prereleases + self.prefer_binary = prefer_binary + + +class BestCandidateResult: + """A collection of candidates, returned by `PackageFinder.find_best_candidate`. + + This class is only intended to be instantiated by CandidateEvaluator's + `compute_best_candidate()` method. + """ + + def __init__( + self, + candidates: List[InstallationCandidate], + applicable_candidates: List[InstallationCandidate], + best_candidate: Optional[InstallationCandidate], + ) -> None: + """ + :param candidates: A sequence of all available candidates found. + :param applicable_candidates: The applicable candidates. + :param best_candidate: The most preferred candidate found, or None + if no applicable candidates were found. + """ + assert set(applicable_candidates) <= set(candidates) + + if best_candidate is None: + assert not applicable_candidates + else: + assert best_candidate in applicable_candidates + + self._applicable_candidates = applicable_candidates + self._candidates = candidates + + self.best_candidate = best_candidate + + def iter_all(self) -> Iterable[InstallationCandidate]: + """Iterate through all candidates.""" + return iter(self._candidates) + + def iter_applicable(self) -> Iterable[InstallationCandidate]: + """Iterate through the applicable candidates.""" + return iter(self._applicable_candidates) + + +class CandidateEvaluator: + + """ + Responsible for filtering and sorting candidates for installation based + on what tags are valid. + """ + + @classmethod + def create( + cls, + project_name: str, + target_python: Optional[TargetPython] = None, + prefer_binary: bool = False, + allow_all_prereleases: bool = False, + specifier: Optional[specifiers.BaseSpecifier] = None, + hashes: Optional[Hashes] = None, + ) -> "CandidateEvaluator": + """Create a CandidateEvaluator object. + + :param target_python: The target Python interpreter to use when + checking compatibility. If None (the default), a TargetPython + object will be constructed from the running Python. + :param specifier: An optional object implementing `filter` + (e.g. `packaging.specifiers.SpecifierSet`) to filter applicable + versions. + :param hashes: An optional collection of allowed hashes. + """ + if target_python is None: + target_python = TargetPython() + if specifier is None: + specifier = specifiers.SpecifierSet() + + supported_tags = target_python.get_tags() + + return cls( + project_name=project_name, + supported_tags=supported_tags, + specifier=specifier, + prefer_binary=prefer_binary, + allow_all_prereleases=allow_all_prereleases, + hashes=hashes, + ) + + def __init__( + self, + project_name: str, + supported_tags: List[Tag], + specifier: specifiers.BaseSpecifier, + prefer_binary: bool = False, + allow_all_prereleases: bool = False, + hashes: Optional[Hashes] = None, + ) -> None: + """ + :param supported_tags: The PEP 425 tags supported by the target + Python in order of preference (most preferred first). + """ + self._allow_all_prereleases = allow_all_prereleases + self._hashes = hashes + self._prefer_binary = prefer_binary + self._project_name = project_name + self._specifier = specifier + self._supported_tags = supported_tags + # Since the index of the tag in the _supported_tags list is used + # as a priority, precompute a map from tag to index/priority to be + # used in wheel.find_most_preferred_tag. + self._wheel_tag_preferences = { + tag: idx for idx, tag in enumerate(supported_tags) + } + + def get_applicable_candidates( + self, + candidates: List[InstallationCandidate], + ) -> List[InstallationCandidate]: + """ + Return the applicable candidates from a list of candidates. + """ + # Using None infers from the specifier instead. + allow_prereleases = self._allow_all_prereleases or None + specifier = self._specifier + versions = { + str(v) + for v in specifier.filter( + # We turn the version object into a str here because otherwise + # when we're debundled but setuptools isn't, Python will see + # packaging.version.Version and + # pkg_resources._vendor.packaging.version.Version as different + # types. This way we'll use a str as a common data interchange + # format. If we stop using the pkg_resources provided specifier + # and start using our own, we can drop the cast to str(). + (str(c.version) for c in candidates), + prereleases=allow_prereleases, + ) + } + + # Again, converting version to str to deal with debundling. + applicable_candidates = [c for c in candidates if str(c.version) in versions] + + filtered_applicable_candidates = filter_unallowed_hashes( + candidates=applicable_candidates, + hashes=self._hashes, + project_name=self._project_name, + ) + + return sorted(filtered_applicable_candidates, key=self._sort_key) + + def _sort_key(self, candidate: InstallationCandidate) -> CandidateSortingKey: + """ + Function to pass as the `key` argument to a call to sorted() to sort + InstallationCandidates by preference. + + Returns a tuple such that tuples sorting as greater using Python's + default comparison operator are more preferred. + + The preference is as follows: + + First and foremost, candidates with allowed (matching) hashes are + always preferred over candidates without matching hashes. This is + because e.g. if the only candidate with an allowed hash is yanked, + we still want to use that candidate. + + Second, excepting hash considerations, candidates that have been + yanked (in the sense of PEP 592) are always less preferred than + candidates that haven't been yanked. Then: + + If not finding wheels, they are sorted by version only. + If finding wheels, then the sort order is by version, then: + 1. existing installs + 2. wheels ordered via Wheel.support_index_min(self._supported_tags) + 3. source archives + If prefer_binary was set, then all wheels are sorted above sources. + + Note: it was considered to embed this logic into the Link + comparison operators, but then different sdist links + with the same version, would have to be considered equal + """ + valid_tags = self._supported_tags + support_num = len(valid_tags) + build_tag: BuildTag = () + binary_preference = 0 + link = candidate.link + if link.is_wheel: + # can raise InvalidWheelFilename + wheel = Wheel(link.filename) + try: + pri = -( + wheel.find_most_preferred_tag( + valid_tags, self._wheel_tag_preferences + ) + ) + except ValueError: + raise UnsupportedWheel( + "{} is not a supported wheel for this platform. It " + "can't be sorted.".format(wheel.filename) + ) + if self._prefer_binary: + binary_preference = 1 + if wheel.build_tag is not None: + match = re.match(r"^(\d+)(.*)$", wheel.build_tag) + assert match is not None, "guaranteed by filename validation" + build_tag_groups = match.groups() + build_tag = (int(build_tag_groups[0]), build_tag_groups[1]) + else: # sdist + pri = -(support_num) + has_allowed_hash = int(link.is_hash_allowed(self._hashes)) + yank_value = -1 * int(link.is_yanked) # -1 for yanked. + return ( + has_allowed_hash, + yank_value, + binary_preference, + candidate.version, + pri, + build_tag, + ) + + def sort_best_candidate( + self, + candidates: List[InstallationCandidate], + ) -> Optional[InstallationCandidate]: + """ + Return the best candidate per the instance's sort order, or None if + no candidate is acceptable. + """ + if not candidates: + return None + best_candidate = max(candidates, key=self._sort_key) + return best_candidate + + def compute_best_candidate( + self, + candidates: List[InstallationCandidate], + ) -> BestCandidateResult: + """ + Compute and return a `BestCandidateResult` instance. + """ + applicable_candidates = self.get_applicable_candidates(candidates) + + best_candidate = self.sort_best_candidate(applicable_candidates) + + return BestCandidateResult( + candidates, + applicable_candidates=applicable_candidates, + best_candidate=best_candidate, + ) + + +class PackageFinder: + """This finds packages. + + This is meant to match easy_install's technique for looking for + packages, by reading pages and looking for appropriate links. + """ + + def __init__( + self, + link_collector: LinkCollector, + target_python: TargetPython, + allow_yanked: bool, + format_control: Optional[FormatControl] = None, + candidate_prefs: Optional[CandidatePreferences] = None, + ignore_requires_python: Optional[bool] = None, + ) -> None: + """ + This constructor is primarily meant to be used by the create() class + method and from tests. + + :param format_control: A FormatControl object, used to control + the selection of source packages / binary packages when consulting + the index and links. + :param candidate_prefs: Options to use when creating a + CandidateEvaluator object. + """ + if candidate_prefs is None: + candidate_prefs = CandidatePreferences() + + format_control = format_control or FormatControl(set(), set()) + + self._allow_yanked = allow_yanked + self._candidate_prefs = candidate_prefs + self._ignore_requires_python = ignore_requires_python + self._link_collector = link_collector + self._target_python = target_python + + self.format_control = format_control + + # These are boring links that have already been logged somehow. + self._logged_links: Set[Tuple[Link, LinkType, str]] = set() + + # Don't include an allow_yanked default value to make sure each call + # site considers whether yanked releases are allowed. This also causes + # that decision to be made explicit in the calling code, which helps + # people when reading the code. + @classmethod + def create( + cls, + link_collector: LinkCollector, + selection_prefs: SelectionPreferences, + target_python: Optional[TargetPython] = None, + ) -> "PackageFinder": + """Create a PackageFinder. + + :param selection_prefs: The candidate selection preferences, as a + SelectionPreferences object. + :param target_python: The target Python interpreter to use when + checking compatibility. If None (the default), a TargetPython + object will be constructed from the running Python. + """ + if target_python is None: + target_python = TargetPython() + + candidate_prefs = CandidatePreferences( + prefer_binary=selection_prefs.prefer_binary, + allow_all_prereleases=selection_prefs.allow_all_prereleases, + ) + + return cls( + candidate_prefs=candidate_prefs, + link_collector=link_collector, + target_python=target_python, + allow_yanked=selection_prefs.allow_yanked, + format_control=selection_prefs.format_control, + ignore_requires_python=selection_prefs.ignore_requires_python, + ) + + @property + def target_python(self) -> TargetPython: + return self._target_python + + @property + def search_scope(self) -> SearchScope: + return self._link_collector.search_scope + + @search_scope.setter + def search_scope(self, search_scope: SearchScope) -> None: + self._link_collector.search_scope = search_scope + + @property + def find_links(self) -> List[str]: + return self._link_collector.find_links + + @property + def index_urls(self) -> List[str]: + return self.search_scope.index_urls + + @property + def trusted_hosts(self) -> Iterable[str]: + for host_port in self._link_collector.session.pip_trusted_origins: + yield build_netloc(*host_port) + + @property + def allow_all_prereleases(self) -> bool: + return self._candidate_prefs.allow_all_prereleases + + def set_allow_all_prereleases(self) -> None: + self._candidate_prefs.allow_all_prereleases = True + + @property + def prefer_binary(self) -> bool: + return self._candidate_prefs.prefer_binary + + def set_prefer_binary(self) -> None: + self._candidate_prefs.prefer_binary = True + + def requires_python_skipped_reasons(self) -> List[str]: + reasons = { + detail + for _, result, detail in self._logged_links + if result == LinkType.requires_python_mismatch + } + return sorted(reasons) + + def make_link_evaluator(self, project_name: str) -> LinkEvaluator: + canonical_name = canonicalize_name(project_name) + formats = self.format_control.get_allowed_formats(canonical_name) + + return LinkEvaluator( + project_name=project_name, + canonical_name=canonical_name, + formats=formats, + target_python=self._target_python, + allow_yanked=self._allow_yanked, + ignore_requires_python=self._ignore_requires_python, + ) + + def _sort_links(self, links: Iterable[Link]) -> List[Link]: + """ + Returns elements of links in order, non-egg links first, egg links + second, while eliminating duplicates + """ + eggs, no_eggs = [], [] + seen: Set[Link] = set() + for link in links: + if link not in seen: + seen.add(link) + if link.egg_fragment: + eggs.append(link) + else: + no_eggs.append(link) + return no_eggs + eggs + + def _log_skipped_link(self, link: Link, result: LinkType, detail: str) -> None: + entry = (link, result, detail) + if entry not in self._logged_links: + # Put the link at the end so the reason is more visible and because + # the link string is usually very long. + logger.debug("Skipping link: %s: %s", detail, link) + self._logged_links.add(entry) + + def get_install_candidate( + self, link_evaluator: LinkEvaluator, link: Link + ) -> Optional[InstallationCandidate]: + """ + If the link is a candidate for install, convert it to an + InstallationCandidate and return it. Otherwise, return None. + """ + result, detail = link_evaluator.evaluate_link(link) + if result != LinkType.candidate: + self._log_skipped_link(link, result, detail) + return None + + return InstallationCandidate( + name=link_evaluator.project_name, + link=link, + version=detail, + ) + + def evaluate_links( + self, link_evaluator: LinkEvaluator, links: Iterable[Link] + ) -> List[InstallationCandidate]: + """ + Convert links that are candidates to InstallationCandidate objects. + """ + candidates = [] + for link in self._sort_links(links): + candidate = self.get_install_candidate(link_evaluator, link) + if candidate is not None: + candidates.append(candidate) + + return candidates + + def process_project_url( + self, project_url: Link, link_evaluator: LinkEvaluator + ) -> List[InstallationCandidate]: + logger.debug( + "Fetching project page and analyzing links: %s", + project_url, + ) + index_response = self._link_collector.fetch_response(project_url) + if index_response is None: + return [] + + page_links = list(parse_links(index_response)) + + with indent_log(): + package_links = self.evaluate_links( + link_evaluator, + links=page_links, + ) + + return package_links + + @functools.lru_cache(maxsize=None) + def find_all_candidates(self, project_name: str) -> List[InstallationCandidate]: + """Find all available InstallationCandidate for project_name + + This checks index_urls and find_links. + All versions found are returned as an InstallationCandidate list. + + See LinkEvaluator.evaluate_link() for details on which files + are accepted. + """ + link_evaluator = self.make_link_evaluator(project_name) + + collected_sources = self._link_collector.collect_sources( + project_name=project_name, + candidates_from_page=functools.partial( + self.process_project_url, + link_evaluator=link_evaluator, + ), + ) + + page_candidates_it = itertools.chain.from_iterable( + source.page_candidates() + for sources in collected_sources + for source in sources + if source is not None + ) + page_candidates = list(page_candidates_it) + + file_links_it = itertools.chain.from_iterable( + source.file_links() + for sources in collected_sources + for source in sources + if source is not None + ) + file_candidates = self.evaluate_links( + link_evaluator, + sorted(file_links_it, reverse=True), + ) + + if logger.isEnabledFor(logging.DEBUG) and file_candidates: + paths = [] + for candidate in file_candidates: + assert candidate.link.url # we need to have a URL + try: + paths.append(candidate.link.file_path) + except Exception: + paths.append(candidate.link.url) # it's not a local file + + logger.debug("Local files found: %s", ", ".join(paths)) + + # This is an intentional priority ordering + return file_candidates + page_candidates + + def make_candidate_evaluator( + self, + project_name: str, + specifier: Optional[specifiers.BaseSpecifier] = None, + hashes: Optional[Hashes] = None, + ) -> CandidateEvaluator: + """Create a CandidateEvaluator object to use.""" + candidate_prefs = self._candidate_prefs + return CandidateEvaluator.create( + project_name=project_name, + target_python=self._target_python, + prefer_binary=candidate_prefs.prefer_binary, + allow_all_prereleases=candidate_prefs.allow_all_prereleases, + specifier=specifier, + hashes=hashes, + ) + + @functools.lru_cache(maxsize=None) + def find_best_candidate( + self, + project_name: str, + specifier: Optional[specifiers.BaseSpecifier] = None, + hashes: Optional[Hashes] = None, + ) -> BestCandidateResult: + """Find matches for the given project and specifier. + + :param specifier: An optional object implementing `filter` + (e.g. `packaging.specifiers.SpecifierSet`) to filter applicable + versions. + + :return: A `BestCandidateResult` instance. + """ + candidates = self.find_all_candidates(project_name) + candidate_evaluator = self.make_candidate_evaluator( + project_name=project_name, + specifier=specifier, + hashes=hashes, + ) + return candidate_evaluator.compute_best_candidate(candidates) + + def find_requirement( + self, req: InstallRequirement, upgrade: bool + ) -> Optional[InstallationCandidate]: + """Try to find a Link matching req + + Expects req, an InstallRequirement and upgrade, a boolean + Returns a InstallationCandidate if found, + Raises DistributionNotFound or BestVersionAlreadyInstalled otherwise + """ + hashes = req.hashes(trust_internet=False) + best_candidate_result = self.find_best_candidate( + req.name, + specifier=req.specifier, + hashes=hashes, + ) + best_candidate = best_candidate_result.best_candidate + + installed_version: Optional[_BaseVersion] = None + if req.satisfied_by is not None: + installed_version = req.satisfied_by.version + + def _format_versions(cand_iter: Iterable[InstallationCandidate]) -> str: + # This repeated parse_version and str() conversion is needed to + # handle different vendoring sources from pip and pkg_resources. + # If we stop using the pkg_resources provided specifier and start + # using our own, we can drop the cast to str(). + return ( + ", ".join( + sorted( + {str(c.version) for c in cand_iter}, + key=parse_version, + ) + ) + or "none" + ) + + if installed_version is None and best_candidate is None: + logger.critical( + "Could not find a version that satisfies the requirement %s " + "(from versions: %s)", + req, + _format_versions(best_candidate_result.iter_all()), + ) + + raise DistributionNotFound( + "No matching distribution found for {}".format(req) + ) + + def _should_install_candidate( + candidate: Optional[InstallationCandidate], + ) -> "TypeGuard[InstallationCandidate]": + if installed_version is None: + return True + if best_candidate is None: + return False + return best_candidate.version > installed_version + + if not upgrade and installed_version is not None: + if _should_install_candidate(best_candidate): + logger.debug( + "Existing installed version (%s) satisfies requirement " + "(most up-to-date version is %s)", + installed_version, + best_candidate.version, + ) + else: + logger.debug( + "Existing installed version (%s) is most up-to-date and " + "satisfies requirement", + installed_version, + ) + return None + + if _should_install_candidate(best_candidate): + logger.debug( + "Using version %s (newest of versions: %s)", + best_candidate.version, + _format_versions(best_candidate_result.iter_applicable()), + ) + return best_candidate + + # We have an existing version, and its the best version + logger.debug( + "Installed version (%s) is most up-to-date (past versions: %s)", + installed_version, + _format_versions(best_candidate_result.iter_applicable()), + ) + raise BestVersionAlreadyInstalled + + +def _find_name_version_sep(fragment: str, canonical_name: str) -> int: + """Find the separator's index based on the package's canonical name. + + :param fragment: A <package>+<version> filename "fragment" (stem) or + egg fragment. + :param canonical_name: The package's canonical name. + + This function is needed since the canonicalized name does not necessarily + have the same length as the egg info's name part. An example:: + + >>> fragment = 'foo__bar-1.0' + >>> canonical_name = 'foo-bar' + >>> _find_name_version_sep(fragment, canonical_name) + 8 + """ + # Project name and version must be separated by one single dash. Find all + # occurrences of dashes; if the string in front of it matches the canonical + # name, this is the one separating the name and version parts. + for i, c in enumerate(fragment): + if c != "-": + continue + if canonicalize_name(fragment[:i]) == canonical_name: + return i + raise ValueError(f"{fragment} does not match {canonical_name}") + + +def _extract_version_from_fragment(fragment: str, canonical_name: str) -> Optional[str]: + """Parse the version string from a <package>+<version> filename + "fragment" (stem) or egg fragment. + + :param fragment: The string to parse. E.g. foo-2.1 + :param canonical_name: The canonicalized name of the package this + belongs to. + """ + try: + version_start = _find_name_version_sep(fragment, canonical_name) + 1 + except ValueError: + return None + version = fragment[version_start:] + if not version: + return None + return version diff --git a/virt/lib/python3.9/site-packages/pip/_internal/models/link 3.py b/virt/lib/python3.9/site-packages/pip/_internal/models/link 3.py new file mode 100644 index 00000000..6ba873c9 --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_internal/models/link 3.py @@ -0,0 +1,581 @@ +import functools +import itertools +import logging +import os +import posixpath +import re +import urllib.parse +from dataclasses import dataclass +from typing import ( + TYPE_CHECKING, + Any, + Dict, + List, + Mapping, + NamedTuple, + Optional, + Tuple, + Union, +) + +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.filetypes import WHEEL_EXTENSION +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.misc import ( + pairwise, + redact_auth_from_url, + split_auth_from_netloc, + splitext, +) +from pip._internal.utils.models import KeyBasedCompareMixin +from pip._internal.utils.urls import path_to_url, url_to_path + +if TYPE_CHECKING: + from pip._internal.index.collector import IndexContent + +logger = logging.getLogger(__name__) + + +# Order matters, earlier hashes have a precedence over later hashes for what +# we will pick to use. +_SUPPORTED_HASHES = ("sha512", "sha384", "sha256", "sha224", "sha1", "md5") + + +@dataclass(frozen=True) +class LinkHash: + """Links to content may have embedded hash values. This class parses those. + + `name` must be any member of `_SUPPORTED_HASHES`. + + This class can be converted to and from `ArchiveInfo`. While ArchiveInfo intends to + be JSON-serializable to conform to PEP 610, this class contains the logic for + parsing a hash name and value for correctness, and then checking whether that hash + conforms to a schema with `.is_hash_allowed()`.""" + + name: str + value: str + + _hash_url_fragment_re = re.compile( + # NB: we do not validate that the second group (.*) is a valid hex + # digest. Instead, we simply keep that string in this class, and then check it + # against Hashes when hash-checking is needed. This is easier to debug than + # proactively discarding an invalid hex digest, as we handle incorrect hashes + # and malformed hashes in the same place. + r"[#&]({choices})=([^&]*)".format( + choices="|".join(re.escape(hash_name) for hash_name in _SUPPORTED_HASHES) + ), + ) + + def __post_init__(self) -> None: + assert self.name in _SUPPORTED_HASHES + + @classmethod + @functools.lru_cache(maxsize=None) + def find_hash_url_fragment(cls, url: str) -> Optional["LinkHash"]: + """Search a string for a checksum algorithm name and encoded output value.""" + match = cls._hash_url_fragment_re.search(url) + if match is None: + return None + name, value = match.groups() + return cls(name=name, value=value) + + def as_dict(self) -> Dict[str, str]: + return {self.name: self.value} + + def as_hashes(self) -> Hashes: + """Return a Hashes instance which checks only for the current hash.""" + return Hashes({self.name: [self.value]}) + + def is_hash_allowed(self, hashes: Optional[Hashes]) -> bool: + """ + Return True if the current hash is allowed by `hashes`. + """ + if hashes is None: + return False + return hashes.is_hash_allowed(self.name, hex_digest=self.value) + + +@dataclass(frozen=True) +class MetadataFile: + """Information about a core metadata file associated with a distribution.""" + + hashes: Optional[Dict[str, str]] + + def __post_init__(self) -> None: + if self.hashes is not None: + assert all(name in _SUPPORTED_HASHES for name in self.hashes) + + +def supported_hashes(hashes: Optional[Dict[str, str]]) -> Optional[Dict[str, str]]: + # Remove any unsupported hash types from the mapping. If this leaves no + # supported hashes, return None + if hashes is None: + return None + hashes = {n: v for n, v in hashes.items() if n in _SUPPORTED_HASHES} + if not hashes: + return None + return hashes + + +def _clean_url_path_part(part: str) -> str: + """ + Clean a "part" of a URL path (i.e. after splitting on "@" characters). + """ + # We unquote prior to quoting to make sure nothing is double quoted. + return urllib.parse.quote(urllib.parse.unquote(part)) + + +def _clean_file_url_path(part: str) -> str: + """ + Clean the first part of a URL path that corresponds to a local + filesystem path (i.e. the first part after splitting on "@" characters). + """ + # We unquote prior to quoting to make sure nothing is double quoted. + # Also, on Windows the path part might contain a drive letter which + # should not be quoted. On Linux where drive letters do not + # exist, the colon should be quoted. We rely on urllib.request + # to do the right thing here. + return urllib.request.pathname2url(urllib.request.url2pathname(part)) + + +# percent-encoded: / +_reserved_chars_re = re.compile("(@|%2F)", re.IGNORECASE) + + +def _clean_url_path(path: str, is_local_path: bool) -> str: + """ + Clean the path portion of a URL. + """ + if is_local_path: + clean_func = _clean_file_url_path + else: + clean_func = _clean_url_path_part + + # Split on the reserved characters prior to cleaning so that + # revision strings in VCS URLs are properly preserved. + parts = _reserved_chars_re.split(path) + + cleaned_parts = [] + for to_clean, reserved in pairwise(itertools.chain(parts, [""])): + cleaned_parts.append(clean_func(to_clean)) + # Normalize %xx escapes (e.g. %2f -> %2F) + cleaned_parts.append(reserved.upper()) + + return "".join(cleaned_parts) + + +def _ensure_quoted_url(url: str) -> str: + """ + Make sure a link is fully quoted. + For example, if ' ' occurs in the URL, it will be replaced with "%20", + and without double-quoting other characters. + """ + # Split the URL into parts according to the general structure + # `scheme://netloc/path;parameters?query#fragment`. + result = urllib.parse.urlparse(url) + # If the netloc is empty, then the URL refers to a local filesystem path. + is_local_path = not result.netloc + path = _clean_url_path(result.path, is_local_path=is_local_path) + return urllib.parse.urlunparse(result._replace(path=path)) + + +class Link(KeyBasedCompareMixin): + """Represents a parsed link from a Package Index's simple URL""" + + __slots__ = [ + "_parsed_url", + "_url", + "_hashes", + "comes_from", + "requires_python", + "yanked_reason", + "metadata_file_data", + "cache_link_parsing", + "egg_fragment", + ] + + def __init__( + self, + url: str, + comes_from: Optional[Union[str, "IndexContent"]] = None, + requires_python: Optional[str] = None, + yanked_reason: Optional[str] = None, + metadata_file_data: Optional[MetadataFile] = None, + cache_link_parsing: bool = True, + hashes: Optional[Mapping[str, str]] = None, + ) -> None: + """ + :param url: url of the resource pointed to (href of the link) + :param comes_from: instance of IndexContent where the link was found, + or string. + :param requires_python: String containing the `Requires-Python` + metadata field, specified in PEP 345. This may be specified by + a data-requires-python attribute in the HTML link tag, as + described in PEP 503. + :param yanked_reason: the reason the file has been yanked, if the + file has been yanked, or None if the file hasn't been yanked. + This is the value of the "data-yanked" attribute, if present, in + a simple repository HTML link. If the file has been yanked but + no reason was provided, this should be the empty string. See + PEP 592 for more information and the specification. + :param metadata_file_data: the metadata attached to the file, or None if + no such metadata is provided. This argument, if not None, indicates + that a separate metadata file exists, and also optionally supplies + hashes for that file. + :param cache_link_parsing: A flag that is used elsewhere to determine + whether resources retrieved from this link should be cached. PyPI + URLs should generally have this set to False, for example. + :param hashes: A mapping of hash names to digests to allow us to + determine the validity of a download. + """ + + # The comes_from, requires_python, and metadata_file_data arguments are + # only used by classmethods of this class, and are not used in client + # code directly. + + # url can be a UNC windows share + if url.startswith("\\\\"): + url = path_to_url(url) + + self._parsed_url = urllib.parse.urlsplit(url) + # Store the url as a private attribute to prevent accidentally + # trying to set a new value. + self._url = url + + link_hash = LinkHash.find_hash_url_fragment(url) + hashes_from_link = {} if link_hash is None else link_hash.as_dict() + if hashes is None: + self._hashes = hashes_from_link + else: + self._hashes = {**hashes, **hashes_from_link} + + self.comes_from = comes_from + self.requires_python = requires_python if requires_python else None + self.yanked_reason = yanked_reason + self.metadata_file_data = metadata_file_data + + super().__init__(key=url, defining_class=Link) + + self.cache_link_parsing = cache_link_parsing + self.egg_fragment = self._egg_fragment() + + @classmethod + def from_json( + cls, + file_data: Dict[str, Any], + page_url: str, + ) -> Optional["Link"]: + """ + Convert an pypi json document from a simple repository page into a Link. + """ + file_url = file_data.get("url") + if file_url is None: + return None + + url = _ensure_quoted_url(urllib.parse.urljoin(page_url, file_url)) + pyrequire = file_data.get("requires-python") + yanked_reason = file_data.get("yanked") + hashes = file_data.get("hashes", {}) + + # PEP 714: Indexes must use the name core-metadata, but + # clients should support the old name as a fallback for compatibility. + metadata_info = file_data.get("core-metadata") + if metadata_info is None: + metadata_info = file_data.get("dist-info-metadata") + + # The metadata info value may be a boolean, or a dict of hashes. + if isinstance(metadata_info, dict): + # The file exists, and hashes have been supplied + metadata_file_data = MetadataFile(supported_hashes(metadata_info)) + elif metadata_info: + # The file exists, but there are no hashes + metadata_file_data = MetadataFile(None) + else: + # False or not present: the file does not exist + metadata_file_data = None + + # The Link.yanked_reason expects an empty string instead of a boolean. + if yanked_reason and not isinstance(yanked_reason, str): + yanked_reason = "" + # The Link.yanked_reason expects None instead of False. + elif not yanked_reason: + yanked_reason = None + + return cls( + url, + comes_from=page_url, + requires_python=pyrequire, + yanked_reason=yanked_reason, + hashes=hashes, + metadata_file_data=metadata_file_data, + ) + + @classmethod + def from_element( + cls, + anchor_attribs: Dict[str, Optional[str]], + page_url: str, + base_url: str, + ) -> Optional["Link"]: + """ + Convert an anchor element's attributes in a simple repository page to a Link. + """ + href = anchor_attribs.get("href") + if not href: + return None + + url = _ensure_quoted_url(urllib.parse.urljoin(base_url, href)) + pyrequire = anchor_attribs.get("data-requires-python") + yanked_reason = anchor_attribs.get("data-yanked") + + # PEP 714: Indexes must use the name data-core-metadata, but + # clients should support the old name as a fallback for compatibility. + metadata_info = anchor_attribs.get("data-core-metadata") + if metadata_info is None: + metadata_info = anchor_attribs.get("data-dist-info-metadata") + # The metadata info value may be the string "true", or a string of + # the form "hashname=hashval" + if metadata_info == "true": + # The file exists, but there are no hashes + metadata_file_data = MetadataFile(None) + elif metadata_info is None: + # The file does not exist + metadata_file_data = None + else: + # The file exists, and hashes have been supplied + hashname, sep, hashval = metadata_info.partition("=") + if sep == "=": + metadata_file_data = MetadataFile(supported_hashes({hashname: hashval})) + else: + # Error - data is wrong. Treat as no hashes supplied. + logger.debug( + "Index returned invalid data-dist-info-metadata value: %s", + metadata_info, + ) + metadata_file_data = MetadataFile(None) + + return cls( + url, + comes_from=page_url, + requires_python=pyrequire, + yanked_reason=yanked_reason, + metadata_file_data=metadata_file_data, + ) + + def __str__(self) -> str: + if self.requires_python: + rp = f" (requires-python:{self.requires_python})" + else: + rp = "" + if self.comes_from: + return "{} (from {}){}".format( + redact_auth_from_url(self._url), self.comes_from, rp + ) + else: + return redact_auth_from_url(str(self._url)) + + def __repr__(self) -> str: + return f"<Link {self}>" + + @property + def url(self) -> str: + return self._url + + @property + def filename(self) -> str: + path = self.path.rstrip("/") + name = posixpath.basename(path) + if not name: + # Make sure we don't leak auth information if the netloc + # includes a username and password. + netloc, user_pass = split_auth_from_netloc(self.netloc) + return netloc + + name = urllib.parse.unquote(name) + assert name, f"URL {self._url!r} produced no filename" + return name + + @property + def file_path(self) -> str: + return url_to_path(self.url) + + @property + def scheme(self) -> str: + return self._parsed_url.scheme + + @property + def netloc(self) -> str: + """ + This can contain auth information. + """ + return self._parsed_url.netloc + + @property + def path(self) -> str: + return urllib.parse.unquote(self._parsed_url.path) + + def splitext(self) -> Tuple[str, str]: + return splitext(posixpath.basename(self.path.rstrip("/"))) + + @property + def ext(self) -> str: + return self.splitext()[1] + + @property + def url_without_fragment(self) -> str: + scheme, netloc, path, query, fragment = self._parsed_url + return urllib.parse.urlunsplit((scheme, netloc, path, query, "")) + + _egg_fragment_re = re.compile(r"[#&]egg=([^&]*)") + + # Per PEP 508. + _project_name_re = re.compile( + r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.IGNORECASE + ) + + def _egg_fragment(self) -> Optional[str]: + match = self._egg_fragment_re.search(self._url) + if not match: + return None + + # An egg fragment looks like a PEP 508 project name, along with + # an optional extras specifier. Anything else is invalid. + project_name = match.group(1) + if not self._project_name_re.match(project_name): + deprecated( + reason=f"{self} contains an egg fragment with a non-PEP 508 name", + replacement="to use the req @ url syntax, and remove the egg fragment", + gone_in="25.0", + issue=11617, + ) + + return project_name + + _subdirectory_fragment_re = re.compile(r"[#&]subdirectory=([^&]*)") + + @property + def subdirectory_fragment(self) -> Optional[str]: + match = self._subdirectory_fragment_re.search(self._url) + if not match: + return None + return match.group(1) + + def metadata_link(self) -> Optional["Link"]: + """Return a link to the associated core metadata file (if any).""" + if self.metadata_file_data is None: + return None + metadata_url = f"{self.url_without_fragment}.metadata" + if self.metadata_file_data.hashes is None: + return Link(metadata_url) + return Link(metadata_url, hashes=self.metadata_file_data.hashes) + + def as_hashes(self) -> Hashes: + return Hashes({k: [v] for k, v in self._hashes.items()}) + + @property + def hash(self) -> Optional[str]: + return next(iter(self._hashes.values()), None) + + @property + def hash_name(self) -> Optional[str]: + return next(iter(self._hashes), None) + + @property + def show_url(self) -> str: + return posixpath.basename(self._url.split("#", 1)[0].split("?", 1)[0]) + + @property + def is_file(self) -> bool: + return self.scheme == "file" + + def is_existing_dir(self) -> bool: + return self.is_file and os.path.isdir(self.file_path) + + @property + def is_wheel(self) -> bool: + return self.ext == WHEEL_EXTENSION + + @property + def is_vcs(self) -> bool: + from pip._internal.vcs import vcs + + return self.scheme in vcs.all_schemes + + @property + def is_yanked(self) -> bool: + return self.yanked_reason is not None + + @property + def has_hash(self) -> bool: + return bool(self._hashes) + + def is_hash_allowed(self, hashes: Optional[Hashes]) -> bool: + """ + Return True if the link has a hash and it is allowed by `hashes`. + """ + if hashes is None: + return False + return any(hashes.is_hash_allowed(k, v) for k, v in self._hashes.items()) + + +class _CleanResult(NamedTuple): + """Convert link for equivalency check. + + This is used in the resolver to check whether two URL-specified requirements + likely point to the same distribution and can be considered equivalent. This + equivalency logic avoids comparing URLs literally, which can be too strict + (e.g. "a=1&b=2" vs "b=2&a=1") and produce conflicts unexpecting to users. + + Currently this does three things: + + 1. Drop the basic auth part. This is technically wrong since a server can + serve different content based on auth, but if it does that, it is even + impossible to guarantee two URLs without auth are equivalent, since + the user can input different auth information when prompted. So the + practical solution is to assume the auth doesn't affect the response. + 2. Parse the query to avoid the ordering issue. Note that ordering under the + same key in the query are NOT cleaned; i.e. "a=1&a=2" and "a=2&a=1" are + still considered different. + 3. Explicitly drop most of the fragment part, except ``subdirectory=`` and + hash values, since it should have no impact the downloaded content. Note + that this drops the "egg=" part historically used to denote the requested + project (and extras), which is wrong in the strictest sense, but too many + people are supplying it inconsistently to cause superfluous resolution + conflicts, so we choose to also ignore them. + """ + + parsed: urllib.parse.SplitResult + query: Dict[str, List[str]] + subdirectory: str + hashes: Dict[str, str] + + +def _clean_link(link: Link) -> _CleanResult: + parsed = link._parsed_url + netloc = parsed.netloc.rsplit("@", 1)[-1] + # According to RFC 8089, an empty host in file: means localhost. + if parsed.scheme == "file" and not netloc: + netloc = "localhost" + fragment = urllib.parse.parse_qs(parsed.fragment) + if "egg" in fragment: + logger.debug("Ignoring egg= fragment in %s", link) + try: + # If there are multiple subdirectory values, use the first one. + # This matches the behavior of Link.subdirectory_fragment. + subdirectory = fragment["subdirectory"][0] + except (IndexError, KeyError): + subdirectory = "" + # If there are multiple hash values under the same algorithm, use the + # first one. This matches the behavior of Link.hash_value. + hashes = {k: fragment[k][0] for k in _SUPPORTED_HASHES if k in fragment} + return _CleanResult( + parsed=parsed._replace(netloc=netloc, query="", fragment=""), + query=urllib.parse.parse_qs(parsed.query), + subdirectory=subdirectory, + hashes=hashes, + ) + + +@functools.lru_cache(maxsize=None) +def links_equivalent(link1: Link, link2: Link) -> bool: + return _clean_link(link1) == _clean_link(link2) diff --git a/virt/lib/python3.9/site-packages/pip/_internal/network/auth 3.py b/virt/lib/python3.9/site-packages/pip/_internal/network/auth 3.py new file mode 100644 index 00000000..e4f00732 --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_internal/network/auth 3.py @@ -0,0 +1,561 @@ +"""Network Authentication Helpers + +Contains interface (MultiDomainBasicAuth) and associated glue code for +providing credentials in the context of network requests. +""" +import logging +import os +import shutil +import subprocess +import sysconfig +import typing +import urllib.parse +from abc import ABC, abstractmethod +from functools import lru_cache +from os.path import commonprefix +from pathlib import Path +from typing import Any, Dict, List, NamedTuple, Optional, Tuple + +from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth +from pip._vendor.requests.models import Request, Response +from pip._vendor.requests.utils import get_netrc_auth + +from pip._internal.utils.logging import getLogger +from pip._internal.utils.misc import ( + ask, + ask_input, + ask_password, + remove_auth_from_url, + split_auth_netloc_from_url, +) +from pip._internal.vcs.versioncontrol import AuthInfo + +logger = getLogger(__name__) + +KEYRING_DISABLED = False + + +class Credentials(NamedTuple): + url: str + username: str + password: str + + +class KeyRingBaseProvider(ABC): + """Keyring base provider interface""" + + has_keyring: bool + + @abstractmethod + def get_auth_info(self, url: str, username: Optional[str]) -> Optional[AuthInfo]: + ... + + @abstractmethod + def save_auth_info(self, url: str, username: str, password: str) -> None: + ... + + +class KeyRingNullProvider(KeyRingBaseProvider): + """Keyring null provider""" + + has_keyring = False + + def get_auth_info(self, url: str, username: Optional[str]) -> Optional[AuthInfo]: + return None + + def save_auth_info(self, url: str, username: str, password: str) -> None: + return None + + +class KeyRingPythonProvider(KeyRingBaseProvider): + """Keyring interface which uses locally imported `keyring`""" + + has_keyring = True + + def __init__(self) -> None: + import keyring + + self.keyring = keyring + + def get_auth_info(self, url: str, username: Optional[str]) -> Optional[AuthInfo]: + # Support keyring's get_credential interface which supports getting + # credentials without a username. This is only available for + # keyring>=15.2.0. + if hasattr(self.keyring, "get_credential"): + logger.debug("Getting credentials from keyring for %s", url) + cred = self.keyring.get_credential(url, username) + if cred is not None: + return cred.username, cred.password + return None + + if username is not None: + logger.debug("Getting password from keyring for %s", url) + password = self.keyring.get_password(url, username) + if password: + return username, password + return None + + def save_auth_info(self, url: str, username: str, password: str) -> None: + self.keyring.set_password(url, username, password) + + +class KeyRingCliProvider(KeyRingBaseProvider): + """Provider which uses `keyring` cli + + Instead of calling the keyring package installed alongside pip + we call keyring on the command line which will enable pip to + use which ever installation of keyring is available first in + PATH. + """ + + has_keyring = True + + def __init__(self, cmd: str) -> None: + self.keyring = cmd + + def get_auth_info(self, url: str, username: Optional[str]) -> Optional[AuthInfo]: + # This is the default implementation of keyring.get_credential + # https://github.com/jaraco/keyring/blob/97689324abcf01bd1793d49063e7ca01e03d7d07/keyring/backend.py#L134-L139 + if username is not None: + password = self._get_password(url, username) + if password is not None: + return username, password + return None + + def save_auth_info(self, url: str, username: str, password: str) -> None: + return self._set_password(url, username, password) + + def _get_password(self, service_name: str, username: str) -> Optional[str]: + """Mirror the implementation of keyring.get_password using cli""" + if self.keyring is None: + return None + + cmd = [self.keyring, "get", service_name, username] + env = os.environ.copy() + env["PYTHONIOENCODING"] = "utf-8" + res = subprocess.run( + cmd, + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + env=env, + ) + if res.returncode: + return None + return res.stdout.decode("utf-8").strip(os.linesep) + + def _set_password(self, service_name: str, username: str, password: str) -> None: + """Mirror the implementation of keyring.set_password using cli""" + if self.keyring is None: + return None + env = os.environ.copy() + env["PYTHONIOENCODING"] = "utf-8" + subprocess.run( + [self.keyring, "set", service_name, username], + input=f"{password}{os.linesep}".encode("utf-8"), + env=env, + check=True, + ) + return None + + +@lru_cache(maxsize=None) +def get_keyring_provider(provider: str) -> KeyRingBaseProvider: + logger.verbose("Keyring provider requested: %s", provider) + + # keyring has previously failed and been disabled + if KEYRING_DISABLED: + provider = "disabled" + if provider in ["import", "auto"]: + try: + impl = KeyRingPythonProvider() + logger.verbose("Keyring provider set: import") + return impl + except ImportError: + pass + except Exception as exc: + # In the event of an unexpected exception + # we should warn the user + msg = "Installed copy of keyring fails with exception %s" + if provider == "auto": + msg = msg + ", trying to find a keyring executable as a fallback" + logger.warning(msg, exc, exc_info=logger.isEnabledFor(logging.DEBUG)) + if provider in ["subprocess", "auto"]: + cli = shutil.which("keyring") + if cli and cli.startswith(sysconfig.get_path("scripts")): + # all code within this function is stolen from shutil.which implementation + @typing.no_type_check + def PATH_as_shutil_which_determines_it() -> str: + path = os.environ.get("PATH", None) + if path is None: + try: + path = os.confstr("CS_PATH") + except (AttributeError, ValueError): + # os.confstr() or CS_PATH is not available + path = os.defpath + # bpo-35755: Don't use os.defpath if the PATH environment variable is + # set to an empty string + + return path + + scripts = Path(sysconfig.get_path("scripts")) + + paths = [] + for path in PATH_as_shutil_which_determines_it().split(os.pathsep): + p = Path(path) + try: + if not p.samefile(scripts): + paths.append(path) + except FileNotFoundError: + pass + + path = os.pathsep.join(paths) + + cli = shutil.which("keyring", path=path) + + if cli: + logger.verbose("Keyring provider set: subprocess with executable %s", cli) + return KeyRingCliProvider(cli) + + logger.verbose("Keyring provider set: disabled") + return KeyRingNullProvider() + + +class MultiDomainBasicAuth(AuthBase): + def __init__( + self, + prompting: bool = True, + index_urls: Optional[List[str]] = None, + keyring_provider: str = "auto", + ) -> None: + self.prompting = prompting + self.index_urls = index_urls + self.keyring_provider = keyring_provider # type: ignore[assignment] + self.passwords: Dict[str, AuthInfo] = {} + # When the user is prompted to enter credentials and keyring is + # available, we will offer to save them. If the user accepts, + # this value is set to the credentials they entered. After the + # request authenticates, the caller should call + # ``save_credentials`` to save these. + self._credentials_to_save: Optional[Credentials] = None + + @property + def keyring_provider(self) -> KeyRingBaseProvider: + return get_keyring_provider(self._keyring_provider) + + @keyring_provider.setter + def keyring_provider(self, provider: str) -> None: + # The free function get_keyring_provider has been decorated with + # functools.cache. If an exception occurs in get_keyring_auth that + # cache will be cleared and keyring disabled, take that into account + # if you want to remove this indirection. + self._keyring_provider = provider + + @property + def use_keyring(self) -> bool: + # We won't use keyring when --no-input is passed unless + # a specific provider is requested because it might require + # user interaction + return self.prompting or self._keyring_provider not in ["auto", "disabled"] + + def _get_keyring_auth( + self, + url: Optional[str], + username: Optional[str], + ) -> Optional[AuthInfo]: + """Return the tuple auth for a given url from keyring.""" + # Do nothing if no url was provided + if not url: + return None + + try: + return self.keyring_provider.get_auth_info(url, username) + except Exception as exc: + logger.warning( + "Keyring is skipped due to an exception: %s", + str(exc), + ) + global KEYRING_DISABLED + KEYRING_DISABLED = True + get_keyring_provider.cache_clear() + return None + + def _get_index_url(self, url: str) -> Optional[str]: + """Return the original index URL matching the requested URL. + + Cached or dynamically generated credentials may work against + the original index URL rather than just the netloc. + + The provided url should have had its username and password + removed already. If the original index url had credentials then + they will be included in the return value. + + Returns None if no matching index was found, or if --no-index + was specified by the user. + """ + if not url or not self.index_urls: + return None + + url = remove_auth_from_url(url).rstrip("/") + "/" + parsed_url = urllib.parse.urlsplit(url) + + candidates = [] + + for index in self.index_urls: + index = index.rstrip("/") + "/" + parsed_index = urllib.parse.urlsplit(remove_auth_from_url(index)) + if parsed_url == parsed_index: + return index + + if parsed_url.netloc != parsed_index.netloc: + continue + + candidate = urllib.parse.urlsplit(index) + candidates.append(candidate) + + if not candidates: + return None + + candidates.sort( + reverse=True, + key=lambda candidate: commonprefix( + [ + parsed_url.path, + candidate.path, + ] + ).rfind("/"), + ) + + return urllib.parse.urlunsplit(candidates[0]) + + def _get_new_credentials( + self, + original_url: str, + *, + allow_netrc: bool = True, + allow_keyring: bool = False, + ) -> AuthInfo: + """Find and return credentials for the specified URL.""" + # Split the credentials and netloc from the url. + url, netloc, url_user_password = split_auth_netloc_from_url( + original_url, + ) + + # Start with the credentials embedded in the url + username, password = url_user_password + if username is not None and password is not None: + logger.debug("Found credentials in url for %s", netloc) + return url_user_password + + # Find a matching index url for this request + index_url = self._get_index_url(url) + if index_url: + # Split the credentials from the url. + index_info = split_auth_netloc_from_url(index_url) + if index_info: + index_url, _, index_url_user_password = index_info + logger.debug("Found index url %s", index_url) + + # If an index URL was found, try its embedded credentials + if index_url and index_url_user_password[0] is not None: + username, password = index_url_user_password + if username is not None and password is not None: + logger.debug("Found credentials in index url for %s", netloc) + return index_url_user_password + + # Get creds from netrc if we still don't have them + if allow_netrc: + netrc_auth = get_netrc_auth(original_url) + if netrc_auth: + logger.debug("Found credentials in netrc for %s", netloc) + return netrc_auth + + # If we don't have a password and keyring is available, use it. + if allow_keyring: + # The index url is more specific than the netloc, so try it first + # fmt: off + kr_auth = ( + self._get_keyring_auth(index_url, username) or + self._get_keyring_auth(netloc, username) + ) + # fmt: on + if kr_auth: + logger.debug("Found credentials in keyring for %s", netloc) + return kr_auth + + return username, password + + def _get_url_and_credentials( + self, original_url: str + ) -> Tuple[str, Optional[str], Optional[str]]: + """Return the credentials to use for the provided URL. + + If allowed, netrc and keyring may be used to obtain the + correct credentials. + + Returns (url_without_credentials, username, password). Note + that even if the original URL contains credentials, this + function may return a different username and password. + """ + url, netloc, _ = split_auth_netloc_from_url(original_url) + + # Try to get credentials from original url + username, password = self._get_new_credentials(original_url) + + # If credentials not found, use any stored credentials for this netloc. + # Do this if either the username or the password is missing. + # This accounts for the situation in which the user has specified + # the username in the index url, but the password comes from keyring. + if (username is None or password is None) and netloc in self.passwords: + un, pw = self.passwords[netloc] + # It is possible that the cached credentials are for a different username, + # in which case the cache should be ignored. + if username is None or username == un: + username, password = un, pw + + if username is not None or password is not None: + # Convert the username and password if they're None, so that + # this netloc will show up as "cached" in the conditional above. + # Further, HTTPBasicAuth doesn't accept None, so it makes sense to + # cache the value that is going to be used. + username = username or "" + password = password or "" + + # Store any acquired credentials. + self.passwords[netloc] = (username, password) + + assert ( + # Credentials were found + (username is not None and password is not None) + # Credentials were not found + or (username is None and password is None) + ), f"Could not load credentials from url: {original_url}" + + return url, username, password + + def __call__(self, req: Request) -> Request: + # Get credentials for this request + url, username, password = self._get_url_and_credentials(req.url) + + # Set the url of the request to the url without any credentials + req.url = url + + if username is not None and password is not None: + # Send the basic auth with this request + req = HTTPBasicAuth(username, password)(req) + + # Attach a hook to handle 401 responses + req.register_hook("response", self.handle_401) + + return req + + # Factored out to allow for easy patching in tests + def _prompt_for_password( + self, netloc: str + ) -> Tuple[Optional[str], Optional[str], bool]: + username = ask_input(f"User for {netloc}: ") if self.prompting else None + if not username: + return None, None, False + if self.use_keyring: + auth = self._get_keyring_auth(netloc, username) + if auth and auth[0] is not None and auth[1] is not None: + return auth[0], auth[1], False + password = ask_password("Password: ") + return username, password, True + + # Factored out to allow for easy patching in tests + def _should_save_password_to_keyring(self) -> bool: + if ( + not self.prompting + or not self.use_keyring + or not self.keyring_provider.has_keyring + ): + return False + return ask("Save credentials to keyring [y/N]: ", ["y", "n"]) == "y" + + def handle_401(self, resp: Response, **kwargs: Any) -> Response: + # We only care about 401 responses, anything else we want to just + # pass through the actual response + if resp.status_code != 401: + return resp + + username, password = None, None + + # Query the keyring for credentials: + if self.use_keyring: + username, password = self._get_new_credentials( + resp.url, + allow_netrc=False, + allow_keyring=True, + ) + + # We are not able to prompt the user so simply return the response + if not self.prompting and not username and not password: + return resp + + parsed = urllib.parse.urlparse(resp.url) + + # Prompt the user for a new username and password + save = False + if not username and not password: + username, password, save = self._prompt_for_password(parsed.netloc) + + # Store the new username and password to use for future requests + self._credentials_to_save = None + if username is not None and password is not None: + self.passwords[parsed.netloc] = (username, password) + + # Prompt to save the password to keyring + if save and self._should_save_password_to_keyring(): + self._credentials_to_save = Credentials( + url=parsed.netloc, + username=username, + password=password, + ) + + # Consume content and release the original connection to allow our new + # request to reuse the same one. + # The result of the assignment isn't used, it's just needed to consume + # the content. + _ = resp.content + resp.raw.release_conn() + + # Add our new username and password to the request + req = HTTPBasicAuth(username or "", password or "")(resp.request) + req.register_hook("response", self.warn_on_401) + + # On successful request, save the credentials that were used to + # keyring. (Note that if the user responded "no" above, this member + # is not set and nothing will be saved.) + if self._credentials_to_save: + req.register_hook("response", self.save_credentials) + + # Send our new request + new_resp = resp.connection.send(req, **kwargs) + new_resp.history.append(resp) + + return new_resp + + def warn_on_401(self, resp: Response, **kwargs: Any) -> None: + """Response callback to warn about incorrect credentials.""" + if resp.status_code == 401: + logger.warning( + "401 Error, Credentials not correct for %s", + resp.request.url, + ) + + def save_credentials(self, resp: Response, **kwargs: Any) -> None: + """Response callback to save credentials on success.""" + assert ( + self.keyring_provider.has_keyring + ), "should never reach here without keyring" + + creds = self._credentials_to_save + self._credentials_to_save = None + if creds and resp.status_code < 400: + try: + logger.info("Saving credentials to keyring") + self.keyring_provider.save_auth_info( + creds.url, creds.username, creds.password + ) + except Exception: + logger.exception("Failed to save credentials") diff --git a/virt/lib/python3.9/site-packages/pip/_internal/req/req_file 3.py b/virt/lib/python3.9/site-packages/pip/_internal/req/req_file 3.py new file mode 100644 index 00000000..8f9568ee --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_internal/req/req_file 3.py @@ -0,0 +1,552 @@ +""" +Requirements file parsing +""" + +import logging +import optparse +import os +import re +import shlex +import urllib.parse +from optparse import Values +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + Generator, + Iterable, + List, + Optional, + Tuple, +) + +from pip._internal.cli import cmdoptions +from pip._internal.exceptions import InstallationError, RequirementsFileParseError +from pip._internal.models.search_scope import SearchScope +from pip._internal.network.session import PipSession +from pip._internal.network.utils import raise_for_status +from pip._internal.utils.encoding import auto_decode +from pip._internal.utils.urls import get_url_scheme + +if TYPE_CHECKING: + # NoReturn introduced in 3.6.2; imported only for type checking to maintain + # pip compatibility with older patch versions of Python 3.6 + from typing import NoReturn + + from pip._internal.index.package_finder import PackageFinder + +__all__ = ["parse_requirements"] + +ReqFileLines = Iterable[Tuple[int, str]] + +LineParser = Callable[[str], Tuple[str, Values]] + +SCHEME_RE = re.compile(r"^(http|https|file):", re.I) +COMMENT_RE = re.compile(r"(^|\s+)#.*$") + +# Matches environment variable-style values in '${MY_VARIABLE_1}' with the +# variable name consisting of only uppercase letters, digits or the '_' +# (underscore). This follows the POSIX standard defined in IEEE Std 1003.1, +# 2013 Edition. +ENV_VAR_RE = re.compile(r"(?P<var>\$\{(?P<name>[A-Z0-9_]+)\})") + +SUPPORTED_OPTIONS: List[Callable[..., optparse.Option]] = [ + cmdoptions.index_url, + cmdoptions.extra_index_url, + cmdoptions.no_index, + cmdoptions.constraints, + cmdoptions.requirements, + cmdoptions.editable, + cmdoptions.find_links, + cmdoptions.no_binary, + cmdoptions.only_binary, + cmdoptions.prefer_binary, + cmdoptions.require_hashes, + cmdoptions.pre, + cmdoptions.trusted_host, + cmdoptions.use_new_feature, +] + +# options to be passed to requirements +SUPPORTED_OPTIONS_REQ: List[Callable[..., optparse.Option]] = [ + cmdoptions.global_options, + cmdoptions.hash, + cmdoptions.config_settings, +] + +# the 'dest' string values +SUPPORTED_OPTIONS_REQ_DEST = [str(o().dest) for o in SUPPORTED_OPTIONS_REQ] + +logger = logging.getLogger(__name__) + + +class ParsedRequirement: + def __init__( + self, + requirement: str, + is_editable: bool, + comes_from: str, + constraint: bool, + options: Optional[Dict[str, Any]] = None, + line_source: Optional[str] = None, + ) -> None: + self.requirement = requirement + self.is_editable = is_editable + self.comes_from = comes_from + self.options = options + self.constraint = constraint + self.line_source = line_source + + +class ParsedLine: + def __init__( + self, + filename: str, + lineno: int, + args: str, + opts: Values, + constraint: bool, + ) -> None: + self.filename = filename + self.lineno = lineno + self.opts = opts + self.constraint = constraint + + if args: + self.is_requirement = True + self.is_editable = False + self.requirement = args + elif opts.editables: + self.is_requirement = True + self.is_editable = True + # We don't support multiple -e on one line + self.requirement = opts.editables[0] + else: + self.is_requirement = False + + +def parse_requirements( + filename: str, + session: PipSession, + finder: Optional["PackageFinder"] = None, + options: Optional[optparse.Values] = None, + constraint: bool = False, +) -> Generator[ParsedRequirement, None, None]: + """Parse a requirements file and yield ParsedRequirement instances. + + :param filename: Path or url of requirements file. + :param session: PipSession instance. + :param finder: Instance of pip.index.PackageFinder. + :param options: cli options. + :param constraint: If true, parsing a constraint file rather than + requirements file. + """ + line_parser = get_line_parser(finder) + parser = RequirementsFileParser(session, line_parser) + + for parsed_line in parser.parse(filename, constraint): + parsed_req = handle_line( + parsed_line, options=options, finder=finder, session=session + ) + if parsed_req is not None: + yield parsed_req + + +def preprocess(content: str) -> ReqFileLines: + """Split, filter, and join lines, and return a line iterator + + :param content: the content of the requirements file + """ + lines_enum: ReqFileLines = enumerate(content.splitlines(), start=1) + lines_enum = join_lines(lines_enum) + lines_enum = ignore_comments(lines_enum) + lines_enum = expand_env_variables(lines_enum) + return lines_enum + + +def handle_requirement_line( + line: ParsedLine, + options: Optional[optparse.Values] = None, +) -> ParsedRequirement: + # preserve for the nested code path + line_comes_from = "{} {} (line {})".format( + "-c" if line.constraint else "-r", + line.filename, + line.lineno, + ) + + assert line.is_requirement + + if line.is_editable: + # For editable requirements, we don't support per-requirement + # options, so just return the parsed requirement. + return ParsedRequirement( + requirement=line.requirement, + is_editable=line.is_editable, + comes_from=line_comes_from, + constraint=line.constraint, + ) + else: + # get the options that apply to requirements + req_options = {} + for dest in SUPPORTED_OPTIONS_REQ_DEST: + if dest in line.opts.__dict__ and line.opts.__dict__[dest]: + req_options[dest] = line.opts.__dict__[dest] + + line_source = f"line {line.lineno} of {line.filename}" + return ParsedRequirement( + requirement=line.requirement, + is_editable=line.is_editable, + comes_from=line_comes_from, + constraint=line.constraint, + options=req_options, + line_source=line_source, + ) + + +def handle_option_line( + opts: Values, + filename: str, + lineno: int, + finder: Optional["PackageFinder"] = None, + options: Optional[optparse.Values] = None, + session: Optional[PipSession] = None, +) -> None: + if opts.hashes: + logger.warning( + "%s line %s has --hash but no requirement, and will be ignored.", + filename, + lineno, + ) + + if options: + # percolate options upward + if opts.require_hashes: + options.require_hashes = opts.require_hashes + if opts.features_enabled: + options.features_enabled.extend( + f for f in opts.features_enabled if f not in options.features_enabled + ) + + # set finder options + if finder: + find_links = finder.find_links + index_urls = finder.index_urls + no_index = finder.search_scope.no_index + if opts.no_index is True: + no_index = True + index_urls = [] + if opts.index_url and not no_index: + index_urls = [opts.index_url] + if opts.extra_index_urls and not no_index: + index_urls.extend(opts.extra_index_urls) + if opts.find_links: + # FIXME: it would be nice to keep track of the source + # of the find_links: support a find-links local path + # relative to a requirements file. + value = opts.find_links[0] + req_dir = os.path.dirname(os.path.abspath(filename)) + relative_to_reqs_file = os.path.join(req_dir, value) + if os.path.exists(relative_to_reqs_file): + value = relative_to_reqs_file + find_links.append(value) + + if session: + # We need to update the auth urls in session + session.update_index_urls(index_urls) + + search_scope = SearchScope( + find_links=find_links, + index_urls=index_urls, + no_index=no_index, + ) + finder.search_scope = search_scope + + if opts.pre: + finder.set_allow_all_prereleases() + + if opts.prefer_binary: + finder.set_prefer_binary() + + if session: + for host in opts.trusted_hosts or []: + source = f"line {lineno} of {filename}" + session.add_trusted_host(host, source=source) + + +def handle_line( + line: ParsedLine, + options: Optional[optparse.Values] = None, + finder: Optional["PackageFinder"] = None, + session: Optional[PipSession] = None, +) -> Optional[ParsedRequirement]: + """Handle a single parsed requirements line; This can result in + creating/yielding requirements, or updating the finder. + + :param line: The parsed line to be processed. + :param options: CLI options. + :param finder: The finder - updated by non-requirement lines. + :param session: The session - updated by non-requirement lines. + + Returns a ParsedRequirement object if the line is a requirement line, + otherwise returns None. + + For lines that contain requirements, the only options that have an effect + are from SUPPORTED_OPTIONS_REQ, and they are scoped to the + requirement. Other options from SUPPORTED_OPTIONS may be present, but are + ignored. + + For lines that do not contain requirements, the only options that have an + effect are from SUPPORTED_OPTIONS. Options from SUPPORTED_OPTIONS_REQ may + be present, but are ignored. These lines may contain multiple options + (although our docs imply only one is supported), and all our parsed and + affect the finder. + """ + + if line.is_requirement: + parsed_req = handle_requirement_line(line, options) + return parsed_req + else: + handle_option_line( + line.opts, + line.filename, + line.lineno, + finder, + options, + session, + ) + return None + + +class RequirementsFileParser: + def __init__( + self, + session: PipSession, + line_parser: LineParser, + ) -> None: + self._session = session + self._line_parser = line_parser + + def parse( + self, filename: str, constraint: bool + ) -> Generator[ParsedLine, None, None]: + """Parse a given file, yielding parsed lines.""" + yield from self._parse_and_recurse(filename, constraint) + + def _parse_and_recurse( + self, filename: str, constraint: bool + ) -> Generator[ParsedLine, None, None]: + for line in self._parse_file(filename, constraint): + if not line.is_requirement and ( + line.opts.requirements or line.opts.constraints + ): + # parse a nested requirements file + if line.opts.requirements: + req_path = line.opts.requirements[0] + nested_constraint = False + else: + req_path = line.opts.constraints[0] + nested_constraint = True + + # original file is over http + if SCHEME_RE.search(filename): + # do a url join so relative paths work + req_path = urllib.parse.urljoin(filename, req_path) + # original file and nested file are paths + elif not SCHEME_RE.search(req_path): + # do a join so relative paths work + req_path = os.path.join( + os.path.dirname(filename), + req_path, + ) + + yield from self._parse_and_recurse(req_path, nested_constraint) + else: + yield line + + def _parse_file( + self, filename: str, constraint: bool + ) -> Generator[ParsedLine, None, None]: + _, content = get_file_content(filename, self._session) + + lines_enum = preprocess(content) + + for line_number, line in lines_enum: + try: + args_str, opts = self._line_parser(line) + except OptionParsingError as e: + # add offending line + msg = f"Invalid requirement: {line}\n{e.msg}" + raise RequirementsFileParseError(msg) + + yield ParsedLine( + filename, + line_number, + args_str, + opts, + constraint, + ) + + +def get_line_parser(finder: Optional["PackageFinder"]) -> LineParser: + def parse_line(line: str) -> Tuple[str, Values]: + # Build new parser for each line since it accumulates appendable + # options. + parser = build_parser() + defaults = parser.get_default_values() + defaults.index_url = None + if finder: + defaults.format_control = finder.format_control + + args_str, options_str = break_args_options(line) + + try: + options = shlex.split(options_str) + except ValueError as e: + raise OptionParsingError(f"Could not split options: {options_str}") from e + + opts, _ = parser.parse_args(options, defaults) + + return args_str, opts + + return parse_line + + +def break_args_options(line: str) -> Tuple[str, str]: + """Break up the line into an args and options string. We only want to shlex + (and then optparse) the options, not the args. args can contain markers + which are corrupted by shlex. + """ + tokens = line.split(" ") + args = [] + options = tokens[:] + for token in tokens: + if token.startswith("-") or token.startswith("--"): + break + else: + args.append(token) + options.pop(0) + return " ".join(args), " ".join(options) + + +class OptionParsingError(Exception): + def __init__(self, msg: str) -> None: + self.msg = msg + + +def build_parser() -> optparse.OptionParser: + """ + Return a parser for parsing requirement lines + """ + parser = optparse.OptionParser(add_help_option=False) + + option_factories = SUPPORTED_OPTIONS + SUPPORTED_OPTIONS_REQ + for option_factory in option_factories: + option = option_factory() + parser.add_option(option) + + # By default optparse sys.exits on parsing errors. We want to wrap + # that in our own exception. + def parser_exit(self: Any, msg: str) -> "NoReturn": + raise OptionParsingError(msg) + + # NOTE: mypy disallows assigning to a method + # https://github.com/python/mypy/issues/2427 + parser.exit = parser_exit # type: ignore + + return parser + + +def join_lines(lines_enum: ReqFileLines) -> ReqFileLines: + """Joins a line ending in '\' with the previous line (except when following + comments). The joined line takes on the index of the first line. + """ + primary_line_number = None + new_line: List[str] = [] + for line_number, line in lines_enum: + if not line.endswith("\\") or COMMENT_RE.match(line): + if COMMENT_RE.match(line): + # this ensures comments are always matched later + line = " " + line + if new_line: + new_line.append(line) + assert primary_line_number is not None + yield primary_line_number, "".join(new_line) + new_line = [] + else: + yield line_number, line + else: + if not new_line: + primary_line_number = line_number + new_line.append(line.strip("\\")) + + # last line contains \ + if new_line: + assert primary_line_number is not None + yield primary_line_number, "".join(new_line) + + # TODO: handle space after '\'. + + +def ignore_comments(lines_enum: ReqFileLines) -> ReqFileLines: + """ + Strips comments and filter empty lines. + """ + for line_number, line in lines_enum: + line = COMMENT_RE.sub("", line) + line = line.strip() + if line: + yield line_number, line + + +def expand_env_variables(lines_enum: ReqFileLines) -> ReqFileLines: + """Replace all environment variables that can be retrieved via `os.getenv`. + + The only allowed format for environment variables defined in the + requirement file is `${MY_VARIABLE_1}` to ensure two things: + + 1. Strings that contain a `$` aren't accidentally (partially) expanded. + 2. Ensure consistency across platforms for requirement files. + + These points are the result of a discussion on the `github pull + request #3514 <https://github.com/pypa/pip/pull/3514>`_. + + Valid characters in variable names follow the `POSIX standard + <http://pubs.opengroup.org/onlinepubs/9699919799/>`_ and are limited + to uppercase letter, digits and the `_` (underscore). + """ + for line_number, line in lines_enum: + for env_var, var_name in ENV_VAR_RE.findall(line): + value = os.getenv(var_name) + if not value: + continue + + line = line.replace(env_var, value) + + yield line_number, line + + +def get_file_content(url: str, session: PipSession) -> Tuple[str, str]: + """Gets the content of a file; it may be a filename, file: URL, or + http: URL. Returns (location, content). Content is unicode. + Respects # -*- coding: declarations on the retrieved files. + + :param url: File path or url. + :param session: PipSession instance. + """ + scheme = get_url_scheme(url) + + # Pip has special support for file:// URLs (LocalFSAdapter). + if scheme in ["http", "https", "file"]: + resp = session.get(url) + raise_for_status(resp) + return resp.url, resp.text + + # Assume this is a bare path. + try: + with open(url, "rb") as f: + content = auto_decode(f.read()) + except OSError as exc: + raise InstallationError(f"Could not open requirements file: {exc}") + return url, content diff --git a/virt/lib/python3.9/site-packages/pip/_internal/vcs/git 3.py b/virt/lib/python3.9/site-packages/pip/_internal/vcs/git 3.py new file mode 100644 index 00000000..8d2c8087 --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_internal/vcs/git 3.py @@ -0,0 +1,526 @@ +import logging +import os.path +import pathlib +import re +import urllib.parse +import urllib.request +from typing import List, Optional, Tuple + +from pip._internal.exceptions import BadCommand, InstallationError +from pip._internal.utils.misc import HiddenText, display_path, hide_url +from pip._internal.utils.subprocess import make_command +from pip._internal.vcs.versioncontrol import ( + AuthInfo, + RemoteNotFoundError, + RemoteNotValidError, + RevOptions, + VersionControl, + find_path_to_project_root_from_repo_root, + vcs, +) + +urlsplit = urllib.parse.urlsplit +urlunsplit = urllib.parse.urlunsplit + + +logger = logging.getLogger(__name__) + + +GIT_VERSION_REGEX = re.compile( + r"^git version " # Prefix. + r"(\d+)" # Major. + r"\.(\d+)" # Dot, minor. + r"(?:\.(\d+))?" # Optional dot, patch. + r".*$" # Suffix, including any pre- and post-release segments we don't care about. +) + +HASH_REGEX = re.compile("^[a-fA-F0-9]{40}$") + +# SCP (Secure copy protocol) shorthand. e.g. 'git@example.com:foo/bar.git' +SCP_REGEX = re.compile( + r"""^ + # Optional user, e.g. 'git@' + (\w+@)? + # Server, e.g. 'github.com'. + ([^/:]+): + # The server-side path. e.g. 'user/project.git'. Must start with an + # alphanumeric character so as not to be confusable with a Windows paths + # like 'C:/foo/bar' or 'C:\foo\bar'. + (\w[^:]*) + $""", + re.VERBOSE, +) + + +def looks_like_hash(sha: str) -> bool: + return bool(HASH_REGEX.match(sha)) + + +class Git(VersionControl): + name = "git" + dirname = ".git" + repo_name = "clone" + schemes = ( + "git+http", + "git+https", + "git+ssh", + "git+git", + "git+file", + ) + # Prevent the user's environment variables from interfering with pip: + # https://github.com/pypa/pip/issues/1130 + unset_environ = ("GIT_DIR", "GIT_WORK_TREE") + default_arg_rev = "HEAD" + + @staticmethod + def get_base_rev_args(rev: str) -> List[str]: + return [rev] + + def is_immutable_rev_checkout(self, url: str, dest: str) -> bool: + _, rev_options = self.get_url_rev_options(hide_url(url)) + if not rev_options.rev: + return False + if not self.is_commit_id_equal(dest, rev_options.rev): + # the current commit is different from rev, + # which means rev was something else than a commit hash + return False + # return False in the rare case rev is both a commit hash + # and a tag or a branch; we don't want to cache in that case + # because that branch/tag could point to something else in the future + is_tag_or_branch = bool(self.get_revision_sha(dest, rev_options.rev)[0]) + return not is_tag_or_branch + + def get_git_version(self) -> Tuple[int, ...]: + version = self.run_command( + ["version"], + command_desc="git version", + show_stdout=False, + stdout_only=True, + ) + match = GIT_VERSION_REGEX.match(version) + if not match: + logger.warning("Can't parse git version: %s", version) + return () + return tuple(int(c) for c in match.groups()) + + @classmethod + def get_current_branch(cls, location: str) -> Optional[str]: + """ + Return the current branch, or None if HEAD isn't at a branch + (e.g. detached HEAD). + """ + # git-symbolic-ref exits with empty stdout if "HEAD" is a detached + # HEAD rather than a symbolic ref. In addition, the -q causes the + # command to exit with status code 1 instead of 128 in this case + # and to suppress the message to stderr. + args = ["symbolic-ref", "-q", "HEAD"] + output = cls.run_command( + args, + extra_ok_returncodes=(1,), + show_stdout=False, + stdout_only=True, + cwd=location, + ) + ref = output.strip() + + if ref.startswith("refs/heads/"): + return ref[len("refs/heads/") :] + + return None + + @classmethod + def get_revision_sha(cls, dest: str, rev: str) -> Tuple[Optional[str], bool]: + """ + Return (sha_or_none, is_branch), where sha_or_none is a commit hash + if the revision names a remote branch or tag, otherwise None. + + Args: + dest: the repository directory. + rev: the revision name. + """ + # Pass rev to pre-filter the list. + output = cls.run_command( + ["show-ref", rev], + cwd=dest, + show_stdout=False, + stdout_only=True, + on_returncode="ignore", + ) + refs = {} + # NOTE: We do not use splitlines here since that would split on other + # unicode separators, which can be maliciously used to install a + # different revision. + for line in output.strip().split("\n"): + line = line.rstrip("\r") + if not line: + continue + try: + ref_sha, ref_name = line.split(" ", maxsplit=2) + except ValueError: + # Include the offending line to simplify troubleshooting if + # this error ever occurs. + raise ValueError(f"unexpected show-ref line: {line!r}") + + refs[ref_name] = ref_sha + + branch_ref = f"refs/remotes/origin/{rev}" + tag_ref = f"refs/tags/{rev}" + + sha = refs.get(branch_ref) + if sha is not None: + return (sha, True) + + sha = refs.get(tag_ref) + + return (sha, False) + + @classmethod + def _should_fetch(cls, dest: str, rev: str) -> bool: + """ + Return true if rev is a ref or is a commit that we don't have locally. + + Branches and tags are not considered in this method because they are + assumed to be always available locally (which is a normal outcome of + ``git clone`` and ``git fetch --tags``). + """ + if rev.startswith("refs/"): + # Always fetch remote refs. + return True + + if not looks_like_hash(rev): + # Git fetch would fail with abbreviated commits. + return False + + if cls.has_commit(dest, rev): + # Don't fetch if we have the commit locally. + return False + + return True + + @classmethod + def resolve_revision( + cls, dest: str, url: HiddenText, rev_options: RevOptions + ) -> RevOptions: + """ + Resolve a revision to a new RevOptions object with the SHA1 of the + branch, tag, or ref if found. + + Args: + rev_options: a RevOptions object. + """ + rev = rev_options.arg_rev + # The arg_rev property's implementation for Git ensures that the + # rev return value is always non-None. + assert rev is not None + + sha, is_branch = cls.get_revision_sha(dest, rev) + + if sha is not None: + rev_options = rev_options.make_new(sha) + rev_options.branch_name = rev if is_branch else None + + return rev_options + + # Do not show a warning for the common case of something that has + # the form of a Git commit hash. + if not looks_like_hash(rev): + logger.warning( + "Did not find branch or tag '%s', assuming revision or ref.", + rev, + ) + + if not cls._should_fetch(dest, rev): + return rev_options + + # fetch the requested revision + cls.run_command( + make_command("fetch", "-q", url, rev_options.to_args()), + cwd=dest, + ) + # Change the revision to the SHA of the ref we fetched + sha = cls.get_revision(dest, rev="FETCH_HEAD") + rev_options = rev_options.make_new(sha) + + return rev_options + + @classmethod + def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool: + """ + Return whether the current commit hash equals the given name. + + Args: + dest: the repository directory. + name: a string name. + """ + if not name: + # Then avoid an unnecessary subprocess call. + return False + + return cls.get_revision(dest) == name + + def fetch_new( + self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int + ) -> None: + rev_display = rev_options.to_display() + logger.info("Cloning %s%s to %s", url, rev_display, display_path(dest)) + if verbosity <= 0: + flags: Tuple[str, ...] = ("--quiet",) + elif verbosity == 1: + flags = () + else: + flags = ("--verbose", "--progress") + if self.get_git_version() >= (2, 17): + # Git added support for partial clone in 2.17 + # https://git-scm.com/docs/partial-clone + # Speeds up cloning by functioning without a complete copy of repository + self.run_command( + make_command( + "clone", + "--filter=blob:none", + *flags, + url, + dest, + ) + ) + else: + self.run_command(make_command("clone", *flags, url, dest)) + + if rev_options.rev: + # Then a specific revision was requested. + rev_options = self.resolve_revision(dest, url, rev_options) + branch_name = getattr(rev_options, "branch_name", None) + logger.debug("Rev options %s, branch_name %s", rev_options, branch_name) + if branch_name is None: + # Only do a checkout if the current commit id doesn't match + # the requested revision. + if not self.is_commit_id_equal(dest, rev_options.rev): + cmd_args = make_command( + "checkout", + "-q", + rev_options.to_args(), + ) + self.run_command(cmd_args, cwd=dest) + elif self.get_current_branch(dest) != branch_name: + # Then a specific branch was requested, and that branch + # is not yet checked out. + track_branch = f"origin/{branch_name}" + cmd_args = [ + "checkout", + "-b", + branch_name, + "--track", + track_branch, + ] + self.run_command(cmd_args, cwd=dest) + else: + sha = self.get_revision(dest) + rev_options = rev_options.make_new(sha) + + logger.info("Resolved %s to commit %s", url, rev_options.rev) + + #: repo may contain submodules + self.update_submodules(dest) + + def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + self.run_command( + make_command("config", "remote.origin.url", url), + cwd=dest, + ) + cmd_args = make_command("checkout", "-q", rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + + self.update_submodules(dest) + + def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + # First fetch changes from the default remote + if self.get_git_version() >= (1, 9): + # fetch tags in addition to everything else + self.run_command(["fetch", "-q", "--tags"], cwd=dest) + else: + self.run_command(["fetch", "-q"], cwd=dest) + # Then reset to wanted revision (maybe even origin/master) + rev_options = self.resolve_revision(dest, url, rev_options) + cmd_args = make_command("reset", "--hard", "-q", rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + #: update submodules + self.update_submodules(dest) + + @classmethod + def get_remote_url(cls, location: str) -> str: + """ + Return URL of the first remote encountered. + + Raises RemoteNotFoundError if the repository does not have a remote + url configured. + """ + # We need to pass 1 for extra_ok_returncodes since the command + # exits with return code 1 if there are no matching lines. + stdout = cls.run_command( + ["config", "--get-regexp", r"remote\..*\.url"], + extra_ok_returncodes=(1,), + show_stdout=False, + stdout_only=True, + cwd=location, + ) + remotes = stdout.splitlines() + try: + found_remote = remotes[0] + except IndexError: + raise RemoteNotFoundError + + for remote in remotes: + if remote.startswith("remote.origin.url "): + found_remote = remote + break + url = found_remote.split(" ")[1] + return cls._git_remote_to_pip_url(url.strip()) + + @staticmethod + def _git_remote_to_pip_url(url: str) -> str: + """ + Convert a remote url from what git uses to what pip accepts. + + There are 3 legal forms **url** may take: + + 1. A fully qualified url: ssh://git@example.com/foo/bar.git + 2. A local project.git folder: /path/to/bare/repository.git + 3. SCP shorthand for form 1: git@example.com:foo/bar.git + + Form 1 is output as-is. Form 2 must be converted to URI and form 3 must + be converted to form 1. + + See the corresponding test test_git_remote_url_to_pip() for examples of + sample inputs/outputs. + """ + if re.match(r"\w+://", url): + # This is already valid. Pass it though as-is. + return url + if os.path.exists(url): + # A local bare remote (git clone --mirror). + # Needs a file:// prefix. + return pathlib.PurePath(url).as_uri() + scp_match = SCP_REGEX.match(url) + if scp_match: + # Add an ssh:// prefix and replace the ':' with a '/'. + return scp_match.expand(r"ssh://\1\2/\3") + # Otherwise, bail out. + raise RemoteNotValidError(url) + + @classmethod + def has_commit(cls, location: str, rev: str) -> bool: + """ + Check if rev is a commit that is available in the local repository. + """ + try: + cls.run_command( + ["rev-parse", "-q", "--verify", "sha^" + rev], + cwd=location, + log_failed_cmd=False, + ) + except InstallationError: + return False + else: + return True + + @classmethod + def get_revision(cls, location: str, rev: Optional[str] = None) -> str: + if rev is None: + rev = "HEAD" + current_rev = cls.run_command( + ["rev-parse", rev], + show_stdout=False, + stdout_only=True, + cwd=location, + ) + return current_rev.strip() + + @classmethod + def get_subdirectory(cls, location: str) -> Optional[str]: + """ + Return the path to Python project root, relative to the repo root. + Return None if the project root is in the repo root. + """ + # find the repo root + git_dir = cls.run_command( + ["rev-parse", "--git-dir"], + show_stdout=False, + stdout_only=True, + cwd=location, + ).strip() + if not os.path.isabs(git_dir): + git_dir = os.path.join(location, git_dir) + repo_root = os.path.abspath(os.path.join(git_dir, "..")) + return find_path_to_project_root_from_repo_root(location, repo_root) + + @classmethod + def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]: + """ + Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'. + That's required because although they use SSH they sometimes don't + work with a ssh:// scheme (e.g. GitHub). But we need a scheme for + parsing. Hence we remove it again afterwards and return it as a stub. + """ + # Works around an apparent Git bug + # (see https://article.gmane.org/gmane.comp.version-control.git/146500) + scheme, netloc, path, query, fragment = urlsplit(url) + if scheme.endswith("file"): + initial_slashes = path[: -len(path.lstrip("/"))] + newpath = initial_slashes + urllib.request.url2pathname(path).replace( + "\\", "/" + ).lstrip("/") + after_plus = scheme.find("+") + 1 + url = scheme[:after_plus] + urlunsplit( + (scheme[after_plus:], netloc, newpath, query, fragment), + ) + + if "://" not in url: + assert "file:" not in url + url = url.replace("git+", "git+ssh://") + url, rev, user_pass = super().get_url_rev_and_auth(url) + url = url.replace("ssh://", "") + else: + url, rev, user_pass = super().get_url_rev_and_auth(url) + + return url, rev, user_pass + + @classmethod + def update_submodules(cls, location: str) -> None: + if not os.path.exists(os.path.join(location, ".gitmodules")): + return + cls.run_command( + ["submodule", "update", "--init", "--recursive", "-q"], + cwd=location, + ) + + @classmethod + def get_repository_root(cls, location: str) -> Optional[str]: + loc = super().get_repository_root(location) + if loc: + return loc + try: + r = cls.run_command( + ["rev-parse", "--show-toplevel"], + cwd=location, + show_stdout=False, + stdout_only=True, + on_returncode="raise", + log_failed_cmd=False, + ) + except BadCommand: + logger.debug( + "could not determine if %s is under git control " + "because git is not available", + location, + ) + return None + except InstallationError: + return None + return os.path.normpath(r.rstrip("\r\n")) + + @staticmethod + def should_add_vcs_url_prefix(repo_url: str) -> bool: + """In either https or ssh form, requirements must be prefixed with git+.""" + return True + + +vcs.register(Git) diff --git a/virt/lib/python3.9/site-packages/pip/_internal/vcs/versioncontrol 3.py b/virt/lib/python3.9/site-packages/pip/_internal/vcs/versioncontrol 3.py new file mode 100644 index 00000000..2491f3c9 --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_internal/vcs/versioncontrol 3.py @@ -0,0 +1,705 @@ +"""Handles all VCS (version control) support""" + +import logging +import os +import shutil +import sys +import urllib.parse +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Iterable, + Iterator, + List, + Mapping, + Optional, + Tuple, + Type, + Union, +) + +from pip._internal.cli.spinners import SpinnerInterface +from pip._internal.exceptions import BadCommand, InstallationError +from pip._internal.utils.misc import ( + HiddenText, + ask_path_exists, + backup_dir, + display_path, + hide_url, + hide_value, + is_installable_dir, + rmtree, +) +from pip._internal.utils.subprocess import ( + CommandArgs, + call_subprocess, + format_command_args, + make_command, +) +from pip._internal.utils.urls import get_url_scheme + +if TYPE_CHECKING: + # Literal was introduced in Python 3.8. + # + # TODO: Remove `if TYPE_CHECKING` when dropping support for Python 3.7. + from typing import Literal + + +__all__ = ["vcs"] + + +logger = logging.getLogger(__name__) + +AuthInfo = Tuple[Optional[str], Optional[str]] + + +def is_url(name: str) -> bool: + """ + Return true if the name looks like a URL. + """ + scheme = get_url_scheme(name) + if scheme is None: + return False + return scheme in ["http", "https", "file", "ftp"] + vcs.all_schemes + + +def make_vcs_requirement_url( + repo_url: str, rev: str, project_name: str, subdir: Optional[str] = None +) -> str: + """ + Return the URL for a VCS requirement. + + Args: + repo_url: the remote VCS url, with any needed VCS prefix (e.g. "git+"). + project_name: the (unescaped) project name. + """ + egg_project_name = project_name.replace("-", "_") + req = f"{repo_url}@{rev}#egg={egg_project_name}" + if subdir: + req += f"&subdirectory={subdir}" + + return req + + +def find_path_to_project_root_from_repo_root( + location: str, repo_root: str +) -> Optional[str]: + """ + Find the the Python project's root by searching up the filesystem from + `location`. Return the path to project root relative to `repo_root`. + Return None if the project root is `repo_root`, or cannot be found. + """ + # find project root. + orig_location = location + while not is_installable_dir(location): + last_location = location + location = os.path.dirname(location) + if location == last_location: + # We've traversed up to the root of the filesystem without + # finding a Python project. + logger.warning( + "Could not find a Python project for directory %s (tried all " + "parent directories)", + orig_location, + ) + return None + + if os.path.samefile(repo_root, location): + return None + + return os.path.relpath(location, repo_root) + + +class RemoteNotFoundError(Exception): + pass + + +class RemoteNotValidError(Exception): + def __init__(self, url: str): + super().__init__(url) + self.url = url + + +class RevOptions: + + """ + Encapsulates a VCS-specific revision to install, along with any VCS + install options. + + Instances of this class should be treated as if immutable. + """ + + def __init__( + self, + vc_class: Type["VersionControl"], + rev: Optional[str] = None, + extra_args: Optional[CommandArgs] = None, + ) -> None: + """ + Args: + vc_class: a VersionControl subclass. + rev: the name of the revision to install. + extra_args: a list of extra options. + """ + if extra_args is None: + extra_args = [] + + self.extra_args = extra_args + self.rev = rev + self.vc_class = vc_class + self.branch_name: Optional[str] = None + + def __repr__(self) -> str: + return f"<RevOptions {self.vc_class.name}: rev={self.rev!r}>" + + @property + def arg_rev(self) -> Optional[str]: + if self.rev is None: + return self.vc_class.default_arg_rev + + return self.rev + + def to_args(self) -> CommandArgs: + """ + Return the VCS-specific command arguments. + """ + args: CommandArgs = [] + rev = self.arg_rev + if rev is not None: + args += self.vc_class.get_base_rev_args(rev) + args += self.extra_args + + return args + + def to_display(self) -> str: + if not self.rev: + return "" + + return f" (to revision {self.rev})" + + def make_new(self, rev: str) -> "RevOptions": + """ + Make a copy of the current instance, but with a new rev. + + Args: + rev: the name of the revision for the new object. + """ + return self.vc_class.make_rev_options(rev, extra_args=self.extra_args) + + +class VcsSupport: + _registry: Dict[str, "VersionControl"] = {} + schemes = ["ssh", "git", "hg", "bzr", "sftp", "svn"] + + def __init__(self) -> None: + # Register more schemes with urlparse for various version control + # systems + urllib.parse.uses_netloc.extend(self.schemes) + super().__init__() + + def __iter__(self) -> Iterator[str]: + return self._registry.__iter__() + + @property + def backends(self) -> List["VersionControl"]: + return list(self._registry.values()) + + @property + def dirnames(self) -> List[str]: + return [backend.dirname for backend in self.backends] + + @property + def all_schemes(self) -> List[str]: + schemes: List[str] = [] + for backend in self.backends: + schemes.extend(backend.schemes) + return schemes + + def register(self, cls: Type["VersionControl"]) -> None: + if not hasattr(cls, "name"): + logger.warning("Cannot register VCS %s", cls.__name__) + return + if cls.name not in self._registry: + self._registry[cls.name] = cls() + logger.debug("Registered VCS backend: %s", cls.name) + + def unregister(self, name: str) -> None: + if name in self._registry: + del self._registry[name] + + def get_backend_for_dir(self, location: str) -> Optional["VersionControl"]: + """ + Return a VersionControl object if a repository of that type is found + at the given directory. + """ + vcs_backends = {} + for vcs_backend in self._registry.values(): + repo_path = vcs_backend.get_repository_root(location) + if not repo_path: + continue + logger.debug("Determine that %s uses VCS: %s", location, vcs_backend.name) + vcs_backends[repo_path] = vcs_backend + + if not vcs_backends: + return None + + # Choose the VCS in the inner-most directory. Since all repository + # roots found here would be either `location` or one of its + # parents, the longest path should have the most path components, + # i.e. the backend representing the inner-most repository. + inner_most_repo_path = max(vcs_backends, key=len) + return vcs_backends[inner_most_repo_path] + + def get_backend_for_scheme(self, scheme: str) -> Optional["VersionControl"]: + """ + Return a VersionControl object or None. + """ + for vcs_backend in self._registry.values(): + if scheme in vcs_backend.schemes: + return vcs_backend + return None + + def get_backend(self, name: str) -> Optional["VersionControl"]: + """ + Return a VersionControl object or None. + """ + name = name.lower() + return self._registry.get(name) + + +vcs = VcsSupport() + + +class VersionControl: + name = "" + dirname = "" + repo_name = "" + # List of supported schemes for this Version Control + schemes: Tuple[str, ...] = () + # Iterable of environment variable names to pass to call_subprocess(). + unset_environ: Tuple[str, ...] = () + default_arg_rev: Optional[str] = None + + @classmethod + def should_add_vcs_url_prefix(cls, remote_url: str) -> bool: + """ + Return whether the vcs prefix (e.g. "git+") should be added to a + repository's remote url when used in a requirement. + """ + return not remote_url.lower().startswith(f"{cls.name}:") + + @classmethod + def get_subdirectory(cls, location: str) -> Optional[str]: + """ + Return the path to Python project root, relative to the repo root. + Return None if the project root is in the repo root. + """ + return None + + @classmethod + def get_requirement_revision(cls, repo_dir: str) -> str: + """ + Return the revision string that should be used in a requirement. + """ + return cls.get_revision(repo_dir) + + @classmethod + def get_src_requirement(cls, repo_dir: str, project_name: str) -> str: + """ + Return the requirement string to use to redownload the files + currently at the given repository directory. + + Args: + project_name: the (unescaped) project name. + + The return value has a form similar to the following: + + {repository_url}@{revision}#egg={project_name} + """ + repo_url = cls.get_remote_url(repo_dir) + + if cls.should_add_vcs_url_prefix(repo_url): + repo_url = f"{cls.name}+{repo_url}" + + revision = cls.get_requirement_revision(repo_dir) + subdir = cls.get_subdirectory(repo_dir) + req = make_vcs_requirement_url(repo_url, revision, project_name, subdir=subdir) + + return req + + @staticmethod + def get_base_rev_args(rev: str) -> List[str]: + """ + Return the base revision arguments for a vcs command. + + Args: + rev: the name of a revision to install. Cannot be None. + """ + raise NotImplementedError + + def is_immutable_rev_checkout(self, url: str, dest: str) -> bool: + """ + Return true if the commit hash checked out at dest matches + the revision in url. + + Always return False, if the VCS does not support immutable commit + hashes. + + This method does not check if there are local uncommitted changes + in dest after checkout, as pip currently has no use case for that. + """ + return False + + @classmethod + def make_rev_options( + cls, rev: Optional[str] = None, extra_args: Optional[CommandArgs] = None + ) -> RevOptions: + """ + Return a RevOptions object. + + Args: + rev: the name of a revision to install. + extra_args: a list of extra options. + """ + return RevOptions(cls, rev, extra_args=extra_args) + + @classmethod + def _is_local_repository(cls, repo: str) -> bool: + """ + posix absolute paths start with os.path.sep, + win32 ones start with drive (like c:\\folder) + """ + drive, tail = os.path.splitdrive(repo) + return repo.startswith(os.path.sep) or bool(drive) + + @classmethod + def get_netloc_and_auth( + cls, netloc: str, scheme: str + ) -> Tuple[str, Tuple[Optional[str], Optional[str]]]: + """ + Parse the repository URL's netloc, and return the new netloc to use + along with auth information. + + Args: + netloc: the original repository URL netloc. + scheme: the repository URL's scheme without the vcs prefix. + + This is mainly for the Subversion class to override, so that auth + information can be provided via the --username and --password options + instead of through the URL. For other subclasses like Git without + such an option, auth information must stay in the URL. + + Returns: (netloc, (username, password)). + """ + return netloc, (None, None) + + @classmethod + def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]: + """ + Parse the repository URL to use, and return the URL, revision, + and auth info to use. + + Returns: (url, rev, (username, password)). + """ + scheme, netloc, path, query, frag = urllib.parse.urlsplit(url) + if "+" not in scheme: + raise ValueError( + "Sorry, {!r} is a malformed VCS url. " + "The format is <vcs>+<protocol>://<url>, " + "e.g. svn+http://myrepo/svn/MyApp#egg=MyApp".format(url) + ) + # Remove the vcs prefix. + scheme = scheme.split("+", 1)[1] + netloc, user_pass = cls.get_netloc_and_auth(netloc, scheme) + rev = None + if "@" in path: + path, rev = path.rsplit("@", 1) + if not rev: + raise InstallationError( + "The URL {!r} has an empty revision (after @) " + "which is not supported. Include a revision after @ " + "or remove @ from the URL.".format(url) + ) + url = urllib.parse.urlunsplit((scheme, netloc, path, query, "")) + return url, rev, user_pass + + @staticmethod + def make_rev_args( + username: Optional[str], password: Optional[HiddenText] + ) -> CommandArgs: + """ + Return the RevOptions "extra arguments" to use in obtain(). + """ + return [] + + def get_url_rev_options(self, url: HiddenText) -> Tuple[HiddenText, RevOptions]: + """ + Return the URL and RevOptions object to use in obtain(), + as a tuple (url, rev_options). + """ + secret_url, rev, user_pass = self.get_url_rev_and_auth(url.secret) + username, secret_password = user_pass + password: Optional[HiddenText] = None + if secret_password is not None: + password = hide_value(secret_password) + extra_args = self.make_rev_args(username, password) + rev_options = self.make_rev_options(rev, extra_args=extra_args) + + return hide_url(secret_url), rev_options + + @staticmethod + def normalize_url(url: str) -> str: + """ + Normalize a URL for comparison by unquoting it and removing any + trailing slash. + """ + return urllib.parse.unquote(url).rstrip("/") + + @classmethod + def compare_urls(cls, url1: str, url2: str) -> bool: + """ + Compare two repo URLs for identity, ignoring incidental differences. + """ + return cls.normalize_url(url1) == cls.normalize_url(url2) + + def fetch_new( + self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int + ) -> None: + """ + Fetch a revision from a repository, in the case that this is the + first fetch from the repository. + + Args: + dest: the directory to fetch the repository to. + rev_options: a RevOptions object. + verbosity: verbosity level. + """ + raise NotImplementedError + + def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + """ + Switch the repo at ``dest`` to point to ``URL``. + + Args: + rev_options: a RevOptions object. + """ + raise NotImplementedError + + def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + """ + Update an already-existing repo to the given ``rev_options``. + + Args: + rev_options: a RevOptions object. + """ + raise NotImplementedError + + @classmethod + def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool: + """ + Return whether the id of the current commit equals the given name. + + Args: + dest: the repository directory. + name: a string name. + """ + raise NotImplementedError + + def obtain(self, dest: str, url: HiddenText, verbosity: int) -> None: + """ + Install or update in editable mode the package represented by this + VersionControl object. + + :param dest: the repository directory in which to install or update. + :param url: the repository URL starting with a vcs prefix. + :param verbosity: verbosity level. + """ + url, rev_options = self.get_url_rev_options(url) + + if not os.path.exists(dest): + self.fetch_new(dest, url, rev_options, verbosity=verbosity) + return + + rev_display = rev_options.to_display() + if self.is_repository_directory(dest): + existing_url = self.get_remote_url(dest) + if self.compare_urls(existing_url, url.secret): + logger.debug( + "%s in %s exists, and has correct URL (%s)", + self.repo_name.title(), + display_path(dest), + url, + ) + if not self.is_commit_id_equal(dest, rev_options.rev): + logger.info( + "Updating %s %s%s", + display_path(dest), + self.repo_name, + rev_display, + ) + self.update(dest, url, rev_options) + else: + logger.info("Skipping because already up-to-date.") + return + + logger.warning( + "%s %s in %s exists with URL %s", + self.name, + self.repo_name, + display_path(dest), + existing_url, + ) + prompt = ("(s)witch, (i)gnore, (w)ipe, (b)ackup ", ("s", "i", "w", "b")) + else: + logger.warning( + "Directory %s already exists, and is not a %s %s.", + dest, + self.name, + self.repo_name, + ) + # https://github.com/python/mypy/issues/1174 + prompt = ("(i)gnore, (w)ipe, (b)ackup ", ("i", "w", "b")) # type: ignore + + logger.warning( + "The plan is to install the %s repository %s", + self.name, + url, + ) + response = ask_path_exists("What to do? {}".format(prompt[0]), prompt[1]) + + if response == "a": + sys.exit(-1) + + if response == "w": + logger.warning("Deleting %s", display_path(dest)) + rmtree(dest) + self.fetch_new(dest, url, rev_options, verbosity=verbosity) + return + + if response == "b": + dest_dir = backup_dir(dest) + logger.warning("Backing up %s to %s", display_path(dest), dest_dir) + shutil.move(dest, dest_dir) + self.fetch_new(dest, url, rev_options, verbosity=verbosity) + return + + # Do nothing if the response is "i". + if response == "s": + logger.info( + "Switching %s %s to %s%s", + self.repo_name, + display_path(dest), + url, + rev_display, + ) + self.switch(dest, url, rev_options) + + def unpack(self, location: str, url: HiddenText, verbosity: int) -> None: + """ + Clean up current location and download the url repository + (and vcs infos) into location + + :param url: the repository URL starting with a vcs prefix. + :param verbosity: verbosity level. + """ + if os.path.exists(location): + rmtree(location) + self.obtain(location, url=url, verbosity=verbosity) + + @classmethod + def get_remote_url(cls, location: str) -> str: + """ + Return the url used at location + + Raises RemoteNotFoundError if the repository does not have a remote + url configured. + """ + raise NotImplementedError + + @classmethod + def get_revision(cls, location: str) -> str: + """ + Return the current commit id of the files at the given location. + """ + raise NotImplementedError + + @classmethod + def run_command( + cls, + cmd: Union[List[str], CommandArgs], + show_stdout: bool = True, + cwd: Optional[str] = None, + on_returncode: 'Literal["raise", "warn", "ignore"]' = "raise", + extra_ok_returncodes: Optional[Iterable[int]] = None, + command_desc: Optional[str] = None, + extra_environ: Optional[Mapping[str, Any]] = None, + spinner: Optional[SpinnerInterface] = None, + log_failed_cmd: bool = True, + stdout_only: bool = False, + ) -> str: + """ + Run a VCS subcommand + This is simply a wrapper around call_subprocess that adds the VCS + command name, and checks that the VCS is available + """ + cmd = make_command(cls.name, *cmd) + if command_desc is None: + command_desc = format_command_args(cmd) + try: + return call_subprocess( + cmd, + show_stdout, + cwd, + on_returncode=on_returncode, + extra_ok_returncodes=extra_ok_returncodes, + command_desc=command_desc, + extra_environ=extra_environ, + unset_environ=cls.unset_environ, + spinner=spinner, + log_failed_cmd=log_failed_cmd, + stdout_only=stdout_only, + ) + except FileNotFoundError: + # errno.ENOENT = no such file or directory + # In other words, the VCS executable isn't available + raise BadCommand( + f"Cannot find command {cls.name!r} - do you have " + f"{cls.name!r} installed and in your PATH?" + ) + except PermissionError: + # errno.EACCES = Permission denied + # This error occurs, for instance, when the command is installed + # only for another user. So, the current user don't have + # permission to call the other user command. + raise BadCommand( + f"No permission to execute {cls.name!r} - install it " + f"locally, globally (ask admin), or check your PATH. " + f"See possible solutions at " + f"https://pip.pypa.io/en/latest/reference/pip_freeze/" + f"#fixing-permission-denied." + ) + + @classmethod + def is_repository_directory(cls, path: str) -> bool: + """ + Return whether a directory path is a repository directory. + """ + logger.debug("Checking in %s for %s (%s)...", path, cls.dirname, cls.name) + return os.path.exists(os.path.join(path, cls.dirname)) + + @classmethod + def get_repository_root(cls, location: str) -> Optional[str]: + """ + Return the "root" (top-level) directory controlled by the vcs, + or `None` if the directory is not in any. + + It is meant to be overridden to implement smarter detection + mechanisms for specific vcs. + + This can do more than is_repository_directory() alone. For + example, the Git override checks that Git is actually available. + """ + if cls.is_repository_directory(location): + return location + return None diff --git a/virt/lib/python3.9/site-packages/pip/_vendor/certifi/cacert 3.pem b/virt/lib/python3.9/site-packages/pip/_vendor/certifi/cacert 3.pem new file mode 100644 index 00000000..9d1e61f9 --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_vendor/certifi/cacert 3.pem @@ -0,0 +1,4589 @@ + +# Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Label: "GlobalSign Root CA" +# Serial: 4835703278459707669005204 +# MD5 Fingerprint: 3e:45:52:15:09:51:92:e1:b7:5d:37:9f:b1:87:29:8a +# SHA1 Fingerprint: b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c +# SHA256 Fingerprint: eb:d4:10:40:e4:bb:3e:c7:42:c9:e3:81:d3:1e:f2:a4:1a:48:b6:68:5c:96:e7:ce:f3:c1:df:6c:d4:33:1c:99 +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Label: "Entrust.net Premium 2048 Secure Server CA" +# Serial: 946069240 +# MD5 Fingerprint: ee:29:31:bc:32:7e:9a:e6:e8:b5:f7:51:b4:34:71:90 +# SHA1 Fingerprint: 50:30:06:09:1d:97:d4:f5:ae:39:f7:cb:e7:92:7d:7d:65:2d:34:31 +# SHA256 Fingerprint: 6d:c4:71:72:e0:1c:bc:b0:bf:62:58:0d:89:5f:e2:b8:ac:9a:d4:f8:73:80:1e:0c:10:b9:c8:37:d2:1e:b1:77 +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 +MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 +LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp +YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG +A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq +K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe +sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX +MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT +XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ +HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub +j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo +U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b +u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ +bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er +fF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +# Issuer: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Subject: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Label: "Baltimore CyberTrust Root" +# Serial: 33554617 +# MD5 Fingerprint: ac:b6:94:a5:9c:17:e0:d7:91:52:9b:b1:97:06:a6:e4 +# SHA1 Fingerprint: d4:de:20:d0:5e:66:fc:53:fe:1a:50:88:2c:78:db:28:52:ca:e4:74 +# SHA256 Fingerprint: 16:af:57:a9:f6:76:b0:ab:12:60:95:aa:5e:ba:de:f2:2a:b3:11:19:d6:44:ac:95:cd:4b:93:db:f3:f2:6a:eb +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Label: "Entrust Root Certification Authority" +# Serial: 1164660820 +# MD5 Fingerprint: d6:a5:c3:ed:5d:dd:3e:00:c1:3d:87:92:1f:1d:3f:e4 +# SHA1 Fingerprint: b3:1e:b1:b7:40:e3:6c:84:02:da:dc:37:d4:4d:f5:d4:67:49:52:f9 +# SHA256 Fingerprint: 73:c1:76:43:4f:1b:c6:d5:ad:f4:5b:0e:76:e7:27:28:7c:8d:e5:76:16:c1:e6:e6:14:1a:2b:2c:bc:7d:8e:4c +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 +Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW +KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw +NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw +NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy +ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV +BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo +Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 +4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 +KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI +rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi +94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB +sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi +gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo +kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE +vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t +O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua +AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP +9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ +eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m +0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +# Issuer: CN=AAA Certificate Services O=Comodo CA Limited +# Subject: CN=AAA Certificate Services O=Comodo CA Limited +# Label: "Comodo AAA Services root" +# Serial: 1 +# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0 +# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49 +# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4 +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2" +# Serial: 1289 +# MD5 Fingerprint: 5e:39:7b:dd:f8:ba:ec:82:e9:ac:62:ba:0c:54:00:2b +# SHA1 Fingerprint: ca:3a:fb:cf:12:40:36:4b:44:b2:16:20:88:80:48:39:19:93:7c:f7 +# SHA256 Fingerprint: 85:a0:dd:7d:d7:20:ad:b7:ff:05:f8:3d:54:2b:20:9d:c7:ff:45:28:f7:d6:77:b1:83:89:fe:a5:e5:c4:9e:86 +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa +GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg +Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J +WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB +rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp ++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 +ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i +Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz +PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og +/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH +oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI +yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud +EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 +A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL +MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f +BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn +g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl +fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K +WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha +B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc +hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR +TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD +mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z +ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y +4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza +8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3" +# Serial: 1478 +# MD5 Fingerprint: 31:85:3c:62:94:97:63:b9:aa:fd:89:4e:af:6f:e0:cf +# SHA1 Fingerprint: 1f:49:14:f7:d8:74:95:1d:dd:ae:02:c0:be:fd:3a:2d:82:75:51:85 +# SHA256 Fingerprint: 18:f1:fc:7f:20:5d:f8:ad:dd:eb:7f:e0:07:dd:57:e3:af:37:5a:9c:4d:8d:73:54:6b:f4:f1:fe:d1:e1:8d:35 +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM +V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB +4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr +H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd +8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv +vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT +mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe +btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc +T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt +WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ +c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A +4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD +VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG +CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 +aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu +dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw +czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G +A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg +Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 +7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem +d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd ++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B +4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN +t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x +DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 +k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s +zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j +Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT +mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK +4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust.net OU=Security Communication RootCA1 +# Subject: O=SECOM Trust.net OU=Security Communication RootCA1 +# Label: "Security Communication Root CA" +# Serial: 0 +# MD5 Fingerprint: f1:bc:63:6a:54:e0:b5:27:f5:cd:e7:1a:e3:4d:6e:4a +# SHA1 Fingerprint: 36:b1:2b:49:f9:81:9e:d7:4c:9e:bc:38:0f:c6:56:8f:5d:ac:b2:f7 +# SHA256 Fingerprint: e7:5e:72:ed:9f:56:0e:ec:6e:b4:80:00:73:a4:3f:c3:ad:19:19:5a:39:22:82:01:78:95:97:4a:99:02:6b:6c +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY +MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t +dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5 +WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD +VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8 +9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ +DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9 +Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N +QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ +xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G +A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG +kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr +Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 +Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU +JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot +RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== +-----END CERTIFICATE----- + +# Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Label: "XRamp Global CA Root" +# Serial: 107108908803651509692980124233745014957 +# MD5 Fingerprint: a1:0b:44:b3:ca:10:d8:00:6e:9d:0f:d8:0f:92:0a:d1 +# SHA1 Fingerprint: b8:01:86:d1:eb:9c:86:a5:41:04:cf:30:54:f3:4c:52:b7:e5:58:c6 +# SHA256 Fingerprint: ce:cd:dc:90:50:99:d8:da:df:c5:b1:d2:09:b7:37:cb:e2:c1:8c:fb:2c:10:c0:ff:0b:cf:0d:32:86:fc:1a:a2 +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB +gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk +MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY +UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx +NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 +dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy +dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 +38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP +KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q +DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 +qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa +JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi +PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P +BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs +jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 +eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR +vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa +IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy +i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ +O+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Label: "Go Daddy Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67 +# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4 +# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4 +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE----- + +# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Label: "Starfield Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24 +# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a +# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58 +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root CA" +# Serial: 17154717934120587862167794914071425081 +# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72 +# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43 +# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root CA" +# Serial: 10944719598952040374951832963794454346 +# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e +# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36 +# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61 +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert High Assurance EV Root CA" +# Serial: 3553400076410547919724730734378100087 +# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a +# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25 +# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Label: "SwissSign Gold CA - G2" +# Serial: 13492815561806991280 +# MD5 Fingerprint: 24:77:d9:a8:91:d1:3b:fa:88:2d:c2:ff:f8:cd:33:93 +# SHA1 Fingerprint: d8:c5:38:8a:b7:30:1b:1b:6e:d4:7a:e6:45:25:3a:6f:9f:1a:27:61 +# SHA256 Fingerprint: 62:dd:0b:e9:b9:f5:0a:16:3e:a0:f8:e7:5c:05:3b:1e:ca:57:ea:55:c8:68:8f:64:7c:68:81:f2:c8:35:7b:95 +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln +biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF +MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT +d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8 +76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+ +bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c +6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE +emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd +MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt +MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y +MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y +FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi +aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM +gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB +qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7 +lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn +8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6 +45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO +UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5 +O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC +bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv +GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a +77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC +hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3 +92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp +Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w +ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt +Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Label: "SwissSign Silver CA - G2" +# Serial: 5700383053117599563 +# MD5 Fingerprint: e0:06:a1:c9:7d:cf:c9:fc:0d:c0:56:75:96:d8:62:13 +# SHA1 Fingerprint: 9b:aa:e5:9f:56:ee:21:cb:43:5a:be:25:93:df:a7:f0:40:d1:1d:cb +# SHA256 Fingerprint: be:6c:4d:a2:bb:b9:ba:59:b6:f3:93:97:68:37:42:46:c3:c0:05:99:3f:a9:8f:02:0d:1d:ed:be:d4:8a:81:d5 +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE +BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu +IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow +RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY +U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv +Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br +YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF +nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH +6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt +eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/ +c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ +MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH +HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf +jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6 +5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB +rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c +wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB +AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp +WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9 +xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ +2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ +IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8 +aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X +em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR +dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/ +OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+ +hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy +tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- + +# Issuer: CN=SecureTrust CA O=SecureTrust Corporation +# Subject: CN=SecureTrust CA O=SecureTrust Corporation +# Label: "SecureTrust CA" +# Serial: 17199774589125277788362757014266862032 +# MD5 Fingerprint: dc:32:c3:a7:6d:25:57:c7:68:09:9d:ea:2d:a9:a2:d1 +# SHA1 Fingerprint: 87:82:c6:c3:04:35:3b:cf:d2:96:92:d2:59:3e:7d:44:d9:34:ff:11 +# SHA256 Fingerprint: f1:c1:b5:0a:e5:a2:0d:d8:03:0e:c9:f6:bc:24:82:3d:d3:67:b5:25:57:59:b4:e7:1b:61:fc:e9:f7:37:5d:73 +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz +MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv +cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz +Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO +0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao +wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj +7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS +8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT +BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg +JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 +6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/ +3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm +D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS +CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +# Issuer: CN=Secure Global CA O=SecureTrust Corporation +# Subject: CN=Secure Global CA O=SecureTrust Corporation +# Label: "Secure Global CA" +# Serial: 9751836167731051554232119481456978597 +# MD5 Fingerprint: cf:f4:27:0d:d4:ed:dc:65:16:49:6d:3d:da:bf:6e:de +# SHA1 Fingerprint: 3a:44:73:5a:e5:81:90:1f:24:86:61:46:1e:3b:9c:c4:5f:f5:3a:1b +# SHA256 Fingerprint: 42:00:f5:04:3a:c8:59:0e:bb:52:7d:20:9e:d1:50:30:29:fb:cb:d4:1c:a1:b5:06:ec:27:f1:5a:de:7d:ac:69 +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx +MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg +Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ +iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa +/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ +jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI +HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7 +sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w +gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw +KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG +AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L +URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO +H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm +I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY +iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO Certification Authority O=COMODO CA Limited +# Label: "COMODO Certification Authority" +# Serial: 104350513648249232941998508985834464573 +# MD5 Fingerprint: 5c:48:dc:f7:42:72:ec:56:94:6d:1c:cc:71:35:80:75 +# SHA1 Fingerprint: 66:31:bf:9e:f7:4f:9e:b6:c9:d5:a6:0c:ba:6a:be:d1:f7:bd:ef:7b +# SHA256 Fingerprint: 0c:2c:d6:3d:f7:80:6f:a3:99:ed:e8:09:11:6b:57:5b:f8:79:89:f0:65:18:f9:80:8c:86:05:03:17:8b:af:66 +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB +gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV +BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw +MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl +YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P +RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 +UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI +2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 +Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp ++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ +DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O +nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW +/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g +PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u +QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY +SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv +IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 +zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd +BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB +ZQ== +-----END CERTIFICATE----- + +# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Label: "COMODO ECC Certification Authority" +# Serial: 41578283867086692638256921589707938090 +# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23 +# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11 +# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7 +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +# Issuer: CN=Certigna O=Dhimyotis +# Subject: CN=Certigna O=Dhimyotis +# Label: "Certigna" +# Serial: 18364802974209362175 +# MD5 Fingerprint: ab:57:a6:5b:7d:42:82:19:b5:d8:58:26:28:5e:fd:ff +# SHA1 Fingerprint: b1:2e:13:63:45:86:a4:6f:1a:b2:60:68:37:58:2d:c4:ac:fd:94:97 +# SHA256 Fingerprint: e3:b6:a2:db:2e:d7:ce:48:84:2f:7a:c5:32:41:c7:b7:1d:54:14:4b:fb:40:c1:1f:3f:1d:0b:42:f5:ee:a1:2d +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X +DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ +BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 +QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny +gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw +zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q +130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 +JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw +ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT +AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj +AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG +9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h +bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc +fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu +HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w +t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +# Issuer: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Subject: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Label: "ePKI Root Certification Authority" +# Serial: 28956088682735189655030529057352760477 +# MD5 Fingerprint: 1b:2e:00:ca:26:06:90:3d:ad:fe:6f:15:68:d3:6b:b3 +# SHA1 Fingerprint: 67:65:0d:f1:7e:8e:7e:5b:82:40:a4:f4:56:4b:cf:e2:3d:69:c6:f0 +# SHA256 Fingerprint: c0:a6:f4:dc:63:a2:4b:fd:cf:54:ef:2a:6a:08:2a:0a:72:de:35:80:3e:2f:f5:ff:52:7a:e5:d8:72:06:df:d5 +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL +SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH +SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh +ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X +DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 +TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ +fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA +sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU +WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS +nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH +dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip +NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC +AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF +MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB +uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl +PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP +JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ +gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 +j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 +5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB +o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS +/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z +Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE +W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D +hNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +# Issuer: O=certSIGN OU=certSIGN ROOT CA +# Subject: O=certSIGN OU=certSIGN ROOT CA +# Label: "certSIGN ROOT CA" +# Serial: 35210227249154 +# MD5 Fingerprint: 18:98:c0:d6:e9:3a:fc:f9:b0:f5:0c:f7:4b:01:44:17 +# SHA1 Fingerprint: fa:b7:ee:36:97:26:62:fb:2d:b0:2a:f6:bf:03:fd:e8:7c:4b:2f:9b +# SHA256 Fingerprint: ea:a9:62:c4:fa:4a:6b:af:eb:e4:15:19:6d:35:1c:cd:88:8d:4f:53:f3:fa:8a:e6:d7:c4:66:a9:4e:60:42:bb +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT +AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD +QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP +MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do +0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ +UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d +RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ +OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv +JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C +AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O +BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ +LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY +MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ +44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I +Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw +i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN +9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Subject: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Label: "NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny" +# Serial: 80544274841616 +# MD5 Fingerprint: c5:a1:b7:ff:73:dd:d6:d7:34:32:18:df:fc:3c:ad:88 +# SHA1 Fingerprint: 06:08:3f:59:3f:15:a1:04:a0:69:a4:6b:a9:03:d0:06:b7:97:09:91 +# SHA256 Fingerprint: 6c:61:da:c3:a2:de:f0:31:50:6b:e0:36:d2:a6:fe:40:19:94:fb:d1:3d:f9:c8:d4:66:59:92:74:c4:46:ec:98 +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG +EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 +MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR +dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB +pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM +b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz +IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT +lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz +AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 +VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG +ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 +BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG +AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M +U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh +bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C ++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F +uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 +XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +# Issuer: CN=Hongkong Post Root CA 1 O=Hongkong Post +# Subject: CN=Hongkong Post Root CA 1 O=Hongkong Post +# Label: "Hongkong Post Root CA 1" +# Serial: 1000 +# MD5 Fingerprint: a8:0d:6f:39:78:b9:43:6d:77:42:6d:98:5a:cc:23:ca +# SHA1 Fingerprint: d6:da:a8:20:8d:09:d2:15:4d:24:b5:2f:cb:34:6e:b2:58:b2:8a:58 +# SHA256 Fingerprint: f9:e6:7d:33:6c:51:00:2a:c0:54:c6:32:02:2d:66:dd:a2:e7:e3:ff:f1:0a:d0:61:ed:31:d8:bb:b4:10:cf:b2 +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx +FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg +Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG +A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr +b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ +jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn +PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh +ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9 +nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h +q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED +MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC +mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3 +7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB +oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs +EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO +fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi +AmvZWg== +-----END CERTIFICATE----- + +# Issuer: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Subject: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Label: "SecureSign RootCA11" +# Serial: 1 +# MD5 Fingerprint: b7:52:74:e2:92:b4:80:93:f2:75:e4:cc:d7:f2:ea:26 +# SHA1 Fingerprint: 3b:c4:9f:48:f8:f3:73:a0:9c:1e:bd:f8:5b:b1:c3:65:c7:d8:11:b3 +# SHA256 Fingerprint: bf:0f:ee:fb:9e:3a:58:1a:d5:f9:e9:db:75:89:98:57:43:d2:61:08:5c:4d:31:4f:6f:5d:72:59:aa:42:16:12 +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr +MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG +A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0 +MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp +Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD +QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz +i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8 +h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV +MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9 +UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni +8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC +h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm +KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ +X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr +QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5 +pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN +QSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +# Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Label: "Microsec e-Szigno Root CA 2009" +# Serial: 14014712776195784473 +# MD5 Fingerprint: f8:49:f4:03:bc:44:2d:83:be:48:69:7d:29:64:fc:b1 +# SHA1 Fingerprint: 89:df:74:fe:5c:f4:0f:4a:80:f9:e3:37:7d:54:da:91:e1:01:31:8e +# SHA256 Fingerprint: 3c:5f:81:fe:a5:fa:b8:2c:64:bf:a2:ea:ec:af:cd:e8:e0:77:fc:86:20:a7:ca:e5:37:16:3d:f3:6e:db:f3:78 +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G +CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y +OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx +FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp +Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP +kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc +cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U +fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 +N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC +xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 ++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM +Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG +SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h +mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk +ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c +2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t +HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Label: "GlobalSign Root CA - R3" +# Serial: 4835703278459759426209954 +# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28 +# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad +# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" +# Serial: 6047274297262753887 +# MD5 Fingerprint: 73:3a:74:7a:ec:bb:a3:96:a6:c2:e4:e2:c8:9b:c0:c3 +# SHA1 Fingerprint: ae:c5:fb:3f:c8:e1:bf:c4:e5:4f:03:07:5a:9a:e8:00:b7:f7:b6:fa +# SHA256 Fingerprint: 04:04:80:28:bf:1f:28:64:d4:8f:9a:d4:d8:32:94:36:6a:82:88:56:55:3f:3b:14:30:3f:90:14:7f:5d:40:ef +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy +MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD +VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv +ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl +AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF +661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9 +am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1 +ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481 +PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS +3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k +SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF +3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM +ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g +StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz +Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB +jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +# Issuer: CN=Izenpe.com O=IZENPE S.A. +# Subject: CN=Izenpe.com O=IZENPE S.A. +# Label: "Izenpe.com" +# Serial: 917563065490389241595536686991402621 +# MD5 Fingerprint: a6:b0:cd:85:80:da:5c:50:34:a3:39:90:2f:55:67:73 +# SHA1 Fingerprint: 2f:78:3d:25:52:18:a7:4a:65:39:71:b5:2c:a2:9c:45:15:6f:e9:19 +# SHA256 Fingerprint: 25:30:cc:8e:98:32:15:02:ba:d9:6f:9b:1f:ba:1b:09:9e:2d:29:9e:0f:45:48:bb:91:4f:36:3b:c0:d4:53:1f +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 +MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 +ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD +VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq +scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO +xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H +LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX +uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD +yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ +JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q +rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN +BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L +hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB +QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ +HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu +Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg +QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB +BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA +A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb +laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 +awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo +JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw +LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT +VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk +LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb +UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ +QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ +naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls +QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Label: "Go Daddy Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 +# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b +# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 +# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e +# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Services Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2 +# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f +# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5 +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Commercial O=AffirmTrust +# Subject: CN=AffirmTrust Commercial O=AffirmTrust +# Label: "AffirmTrust Commercial" +# Serial: 8608355977964138876 +# MD5 Fingerprint: 82:92:ba:5b:ef:cd:8a:6f:a6:3d:55:f9:84:f6:d6:b7 +# SHA1 Fingerprint: f9:b5:b6:32:45:5f:9c:be:ec:57:5f:80:dc:e9:6e:2c:c7:b2:78:b7 +# SHA256 Fingerprint: 03:76:ab:1d:54:c5:f9:80:3c:e4:b2:e2:01:a0:ee:7e:ef:7b:57:b6:36:e8:a9:3c:9b:8d:48:60:c9:6f:5f:a7 +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP +Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr +ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL +MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 +yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr +VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ +nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG +XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj +vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt +Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g +N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC +nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Networking O=AffirmTrust +# Subject: CN=AffirmTrust Networking O=AffirmTrust +# Label: "AffirmTrust Networking" +# Serial: 8957382827206547757 +# MD5 Fingerprint: 42:65:ca:be:01:9a:9a:4c:a9:8c:41:49:cd:c0:d5:7f +# SHA1 Fingerprint: 29:36:21:02:8b:20:ed:02:f5:66:c5:32:d1:d6:ed:90:9f:45:00:2f +# SHA256 Fingerprint: 0a:81:ec:5a:92:97:77:f1:45:90:4a:f3:8d:5d:50:9f:66:b5:e2:c5:8f:cd:b5:31:05:8b:0e:17:f3:f0:b4:1b +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y +YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua +kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL +QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp +6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG +yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i +QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO +tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu +QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ +Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u +olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 +x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium O=AffirmTrust +# Subject: CN=AffirmTrust Premium O=AffirmTrust +# Label: "AffirmTrust Premium" +# Serial: 7893706540734352110 +# MD5 Fingerprint: c4:5d:0e:48:b6:ac:28:30:4e:0a:bc:f9:38:16:87:57 +# SHA1 Fingerprint: d8:a6:33:2c:e0:03:6f:b1:85:f6:63:4f:7d:6a:06:65:26:32:28:27 +# SHA256 Fingerprint: 70:a7:3f:7f:37:6b:60:07:42:48:90:45:34:b1:14:82:d5:bf:0e:69:8e:cc:49:8d:f5:25:77:eb:f2:e9:3b:9a +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz +dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG +A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U +cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf +qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ +JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ ++jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS +s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 +HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 +70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG +V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S +qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S +5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia +C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX +OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE +FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 +KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B +8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ +MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc +0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ +u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF +u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH +YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 +GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO +RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e +KeC2uAloGRwYQw== +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium ECC O=AffirmTrust +# Subject: CN=AffirmTrust Premium ECC O=AffirmTrust +# Label: "AffirmTrust Premium ECC" +# Serial: 8401224907861490260 +# MD5 Fingerprint: 64:b0:09:55:cf:b1:d5:99:e2:be:13:ab:a6:5d:ea:4d +# SHA1 Fingerprint: b8:23:6b:00:2f:1d:16:86:53:01:55:6c:11:a4:37:ca:eb:ff:c3:bb +# SHA256 Fingerprint: bd:71:fd:f6:da:97:e4:cf:62:d1:64:7a:dd:25:81:b0:7d:79:ad:f8:39:7e:b4:ec:ba:9c:5e:84:88:82:14:23 +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC +VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ +cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ +BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt +VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D +0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 +ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G +A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs +aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I +flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA" +# Serial: 279744 +# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78 +# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e +# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Label: "TWCA Root Certification Authority" +# Serial: 1 +# MD5 Fingerprint: aa:08:8f:f6:f9:7b:b7:f2:b1:a7:1e:9b:ea:ea:bd:79 +# SHA1 Fingerprint: cf:9e:87:6d:d3:eb:fc:42:26:97:a3:b5:a3:7a:a0:76:a9:06:23:48 +# SHA256 Fingerprint: bf:d8:8f:e1:10:1c:41:ae:3e:80:1b:f8:be:56:35:0e:e9:ba:d1:a6:b9:bd:51:5e:dc:5c:6d:5b:87:11:ac:44 +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES +MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU +V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz +WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO +LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE +AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH +K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX +RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z +rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx +3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq +hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC +MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls +XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D +lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn +aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ +YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Label: "Security Communication RootCA2" +# Serial: 0 +# MD5 Fingerprint: 6c:39:7d:a4:0e:55:59:b2:3f:d6:41:b1:12:50:de:43 +# SHA1 Fingerprint: 5f:3b:8c:f2:f8:10:b3:7d:78:b4:ce:ec:19:19:c3:73:34:b9:c7:74 +# SHA256 Fingerprint: 51:3b:2c:ec:b8:10:d4:cd:e5:dd:85:39:1a:df:c6:c2:dd:60:d8:7b:b7:36:d2:b5:21:48:4a:a4:7a:0e:be:f6 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Label: "Actalis Authentication Root CA" +# Serial: 6271844772424770508 +# MD5 Fingerprint: 69:c1:0d:4f:07:a3:1b:c3:fe:56:3d:04:bc:11:f6:a6 +# SHA1 Fingerprint: f3:73:b3:87:06:5a:28:84:8a:f2:f3:4a:ce:19:2b:dd:c7:8e:9c:ac +# SHA256 Fingerprint: 55:92:60:84:ec:96:3a:64:b9:6e:2a:be:01:ce:0b:a8:6a:64:fb:fe:bc:c7:aa:b5:af:c1:55:b3:7f:d7:60:66 +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 2 Root CA" +# Serial: 2 +# MD5 Fingerprint: 46:a7:d2:fe:45:fb:64:5a:a8:59:90:9b:78:44:9b:29 +# SHA1 Fingerprint: 49:0a:75:74:de:87:0a:47:fe:58:ee:f6:c7:6b:eb:c6:0b:12:40:99 +# SHA256 Fingerprint: 9a:11:40:25:19:7c:5b:b9:5d:94:e6:3d:55:cd:43:79:08:47:b6:46:b2:3c:df:11:ad:a4:a0:0e:ff:15:fb:48 +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr +6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV +L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 +1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx +MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ +QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB +arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr +Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi +FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS +P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN +9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz +uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h +9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t +OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo ++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 +KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 +DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us +H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ +I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 +5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h +3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz +Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 3 Root CA" +# Serial: 2 +# MD5 Fingerprint: 3d:3b:18:9e:2c:64:5a:e8:d5:88:ce:0e:f9:37:c2:ec +# SHA1 Fingerprint: da:fa:f7:fa:66:84:ec:06:8f:14:50:bd:c7:c2:81:a5:bc:a9:64:57 +# SHA256 Fingerprint: ed:f7:eb:bc:a2:7a:2a:38:4d:38:7b:7d:40:10:c6:66:e2:ed:b4:84:3e:4c:29:b4:ae:1d:5b:93:32:e6:b2:4d +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y +ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E +N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 +tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX +0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c +/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X +KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY +zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS +O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D +34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP +K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv +Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj +QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS +IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 +HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa +O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv +033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u +dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE +kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 +3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD +u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq +4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 3" +# Serial: 1 +# MD5 Fingerprint: ca:fb:40:a8:4e:39:92:8a:1d:fe:8e:2f:c4:27:ea:ef +# SHA1 Fingerprint: 55:a6:72:3e:cb:f2:ec:cd:c3:23:74:70:19:9d:2a:be:11:e3:81:d1 +# SHA256 Fingerprint: fd:73:da:d3:1c:64:4f:f1:b4:3b:ef:0c:cd:da:96:71:0b:9c:d9:87:5e:ca:7e:31:70:7a:f3:e9:6d:52:2b:bd +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN +8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ +RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 +hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 +ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM +EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 +A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy +WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ +1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 +6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT +91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p +TpPDpFQUWw== +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 2009" +# Serial: 623603 +# MD5 Fingerprint: cd:e0:25:69:8d:47:ac:9c:89:35:90:f7:fd:51:3d:2f +# SHA1 Fingerprint: 58:e8:ab:b0:36:15:33:fb:80:f7:9b:1b:6d:29:d3:ff:8d:5f:00:f0 +# SHA256 Fingerprint: 49:e7:a4:42:ac:f0:ea:62:87:05:00:54:b5:25:64:b6:50:e4:f4:9e:42:e3:48:d6:aa:38:e0:39:e9:57:b1:c1 +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 EV 2009" +# Serial: 623604 +# MD5 Fingerprint: aa:c6:43:2c:5e:2d:cd:c4:34:c0:50:4f:11:02:4f:b6 +# SHA1 Fingerprint: 96:c9:1b:0b:95:b4:10:98:42:fa:d0:d8:22:79:fe:60:fa:b9:16:83 +# SHA256 Fingerprint: ee:c5:49:6b:98:8c:e9:86:25:b9:34:09:2e:ec:29:08:be:d0:b0:f3:16:c2:d4:73:0c:84:ea:f1:f3:d3:48:81 +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig Root R2 O=Disig a.s. +# Subject: CN=CA Disig Root R2 O=Disig a.s. +# Label: "CA Disig Root R2" +# Serial: 10572350602393338211 +# MD5 Fingerprint: 26:01:fb:d8:27:a7:17:9a:45:54:38:1a:43:01:3b:03 +# SHA1 Fingerprint: b5:61:eb:ea:a4:de:e4:25:4b:69:1a:98:a5:57:47:c2:34:c7:d9:71 +# SHA256 Fingerprint: e2:3d:4a:03:6d:7b:70:e9:f5:95:b1:42:20:79:d2:b9:1e:df:bb:1f:b6:51:a0:63:3e:aa:8a:9d:c5:f8:07:03 +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy +MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe +NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH +PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I +x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe +QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR +yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO +QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 +H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ +QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD +i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs +nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 +rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI +hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf +GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb +lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka ++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal +TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i +nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 +gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr +G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os +zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x +L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +# Issuer: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Subject: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Label: "ACCVRAIZ1" +# Serial: 6828503384748696800 +# MD5 Fingerprint: d0:a0:5a:ee:05:b6:09:94:21:a1:7d:f1:b2:29:82:02 +# SHA1 Fingerprint: 93:05:7a:88:15:c6:4f:ce:88:2f:fa:91:16:52:28:78:bc:53:64:17 +# SHA256 Fingerprint: 9a:6e:c0:12:e1:a7:da:9d:be:34:19:4d:47:8a:d7:c0:db:18:22:fb:07:1d:f1:29:81:49:6e:d1:04:38:41:13 +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE +AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw +CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ +BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND +VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb +qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY +HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo +G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA +lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr +IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ +0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH +k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 +4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO +m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa +cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl +uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI +KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls +ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG +AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT +VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG +CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA +cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA +QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA +7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA +cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA +QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA +czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu +aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt +aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud +DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF +BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp +D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU +JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m +AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD +vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms +tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH +7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA +h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF +d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H +pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Label: "TWCA Global Root CA" +# Serial: 3262 +# MD5 Fingerprint: f9:03:7e:cf:e6:9e:3c:73:7a:2a:90:07:69:ff:2b:96 +# SHA1 Fingerprint: 9c:bb:48:53:f6:a4:f6:d3:52:a4:e8:32:52:55:60:13:f5:ad:af:65 +# SHA256 Fingerprint: 59:76:90:07:f7:68:5d:0f:cd:50:87:2f:9f:95:d5:75:5a:5b:2b:45:7d:81:f3:69:2b:61:0a:98:67:2f:0e:1b +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- + +# Issuer: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Subject: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Label: "TeliaSonera Root CA v1" +# Serial: 199041966741090107964904287217786801558 +# MD5 Fingerprint: 37:41:49:1b:18:56:9a:26:f5:ad:c2:66:fb:40:a5:4c +# SHA1 Fingerprint: 43:13:bb:96:f1:d5:86:9b:c1:4e:6a:92:f6:cf:f6:34:69:87:82:37 +# SHA256 Fingerprint: dd:69:36:fe:21:f8:f0:77:c1:23:a1:a5:21:c1:22:24:f7:22:55:b7:3e:03:a7:26:06:93:e8:a2:4b:0f:a3:89 +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw +NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv +b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD +VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F +VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 +7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X +Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ +/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs +81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm +dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe +Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu +sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 +pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs +slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ +arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD +VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG +9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl +dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj +TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed +Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 +Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI +OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 +vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW +t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn +HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx +SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +# Issuer: CN=E-Tugra Certification Authority O=E-Tu\u011fra EBG Bili\u015fim Teknolojileri ve Hizmetleri A.\u015e. OU=E-Tugra Sertifikasyon Merkezi +# Subject: CN=E-Tugra Certification Authority O=E-Tu\u011fra EBG Bili\u015fim Teknolojileri ve Hizmetleri A.\u015e. OU=E-Tugra Sertifikasyon Merkezi +# Label: "E-Tugra Certification Authority" +# Serial: 7667447206703254355 +# MD5 Fingerprint: b8:a1:03:63:b0:bd:21:71:70:8a:6f:13:3a:bb:79:49 +# SHA1 Fingerprint: 51:c6:e7:08:49:06:6e:f3:92:d4:5c:a0:0d:6d:a3:62:8f:c3:52:39 +# SHA256 Fingerprint: b0:bf:d5:2b:b0:d7:d9:bd:92:bf:5d:4d:c1:3d:a2:55:c0:2c:54:2f:37:83:65:ea:89:39:11:f5:5e:55:f2:3c +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV +BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC +aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV +BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1 +Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz +MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+ +BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp +em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY +B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH +D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF +Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo +q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D +k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH +fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut +dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM +ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8 +zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX +U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6 +Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5 +XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF +Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR +HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY +GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c +77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3 ++GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK +vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6 +FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl +yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P +AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD +y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d +NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 2" +# Serial: 1 +# MD5 Fingerprint: 2b:9b:9e:e4:7b:6c:1f:00:72:1a:cc:c1:77:79:df:6a +# SHA1 Fingerprint: 59:0d:2d:7d:88:4f:40:2e:61:7e:a5:62:32:17:65:cf:17:d8:94:e9 +# SHA256 Fingerprint: 91:e2:f5:78:8d:58:10:eb:a7:ba:58:73:7d:e1:54:8a:8e:ca:cd:01:45:98:bc:0b:14:3e:04:1b:17:05:25:52 +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot 2011 O=Atos +# Subject: CN=Atos TrustedRoot 2011 O=Atos +# Label: "Atos TrustedRoot 2011" +# Serial: 6643877497813316402 +# MD5 Fingerprint: ae:b9:c4:32:4b:ac:7f:5d:66:cc:77:94:bb:2a:77:56 +# SHA1 Fingerprint: 2b:b1:f5:3e:55:0c:1d:c5:f1:d4:e6:b7:6a:46:4b:55:06:02:ac:21 +# SHA256 Fingerprint: f3:56:be:a2:44:b7:a9:1e:b3:5d:53:ca:9a:d7:86:4a:ce:01:8e:2d:35:d5:f8:f9:6d:df:68:a6:f4:1a:a4:74 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE +AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG +EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM +FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC +REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp +Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM +VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ +SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ +4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L +cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi +eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG +A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j +vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP +DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc +maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D +lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv +KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 1 G3" +# Serial: 687049649626669250736271037606554624078720034195 +# MD5 Fingerprint: a4:bc:5b:3f:fe:37:9a:fa:64:f0:e2:fa:05:3d:0b:ab +# SHA1 Fingerprint: 1b:8e:ea:57:96:29:1a:c9:39:ea:b8:0a:81:1a:73:73:c0:93:79:67 +# SHA256 Fingerprint: 8a:86:6f:d1:b2:76:b5:7e:57:8e:92:1c:65:82:8a:2b:ed:58:e9:f2:f2:88:05:41:34:b7:f1:f4:bf:c9:cc:74 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2 G3" +# Serial: 390156079458959257446133169266079962026824725800 +# MD5 Fingerprint: af:0c:86:6e:bf:40:2d:7f:0b:3e:12:50:ba:12:3d:06 +# SHA1 Fingerprint: 09:3c:61:f3:8b:8b:dc:7d:55:df:75:38:02:05:00:e1:25:f5:c8:36 +# SHA256 Fingerprint: 8f:e4:fb:0a:f9:3a:4d:0d:67:db:0b:eb:b2:3e:37:c7:1b:f3:25:dc:bc:dd:24:0e:a0:4d:af:58:b4:7e:18:40 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3 G3" +# Serial: 268090761170461462463995952157327242137089239581 +# MD5 Fingerprint: df:7d:b9:ad:54:6f:68:a1:df:89:57:03:97:43:b0:d7 +# SHA1 Fingerprint: 48:12:bd:92:3c:a8:c4:39:06:e7:30:6d:27:96:e6:a4:cf:22:2e:7d +# SHA256 Fingerprint: 88:ef:81:de:20:2e:b0:18:45:2e:43:f8:64:72:5c:ea:5f:bd:1f:c2:d9:d2:05:73:07:09:c5:d8:b8:69:0f:46 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G2" +# Serial: 15385348160840213938643033620894905419 +# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d +# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f +# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85 +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G3" +# Serial: 15459312981008553731928384953135426796 +# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb +# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89 +# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2 +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G2" +# Serial: 4293743540046975378534879503202253541 +# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 +# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 +# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G3" +# Serial: 7089244469030293291760083333884364146 +# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca +# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e +# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Trusted Root G4" +# Serial: 7451500558977370777930084869016614236 +# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49 +# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4 +# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88 +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- + +# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Label: "COMODO RSA Certification Authority" +# Serial: 101909084537582093308941363524873193117 +# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18 +# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4 +# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34 +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Label: "USERTrust RSA Certification Authority" +# Serial: 2645093764781058787591871645665788717 +# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 +# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e +# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Label: "USERTrust ECC Certification Authority" +# Serial: 123013823720199481456569720443997572134 +# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1 +# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0 +# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Label: "GlobalSign ECC Root CA - R5" +# Serial: 32785792099990507226680698011560947931244 +# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08 +# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa +# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24 +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Label: "IdenTrust Commercial Root CA 1" +# Serial: 13298821034946342390520003877796839426 +# MD5 Fingerprint: b3:3e:77:73:75:ee:a0:d3:e3:7e:49:63:49:59:bb:c7 +# SHA1 Fingerprint: df:71:7e:aa:4a:d9:4e:c9:55:84:99:60:2d:48:de:5f:bc:f0:3a:25 +# SHA256 Fingerprint: 5d:56:49:9b:e4:d2:e0:8b:cf:ca:d0:8a:3e:38:72:3d:50:50:3b:de:70:69:48:e4:2f:55:60:30:19:e5:28:ae +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Label: "IdenTrust Public Sector Root CA 1" +# Serial: 13298821034946342390521976156843933698 +# MD5 Fingerprint: 37:06:a5:b0:fc:89:9d:ba:f4:6b:8c:1a:64:cd:d5:ba +# SHA1 Fingerprint: ba:29:41:60:77:98:3f:f4:f3:ef:f2:31:05:3b:2e:ea:6d:4d:45:fd +# SHA256 Fingerprint: 30:d0:89:5a:9a:44:8a:26:20:91:63:55:22:d1:f5:20:10:b5:86:7a:ca:e1:2c:78:ef:95:8f:d4:f4:38:9f:2f +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - G2" +# Serial: 1246989352 +# MD5 Fingerprint: 4b:e2:c9:91:96:65:0c:f4:0e:5a:93:92:a0:0a:fe:b2 +# SHA1 Fingerprint: 8c:f4:27:fd:79:0c:3a:d1:66:06:8d:e8:1e:57:ef:bb:93:22:72:d4 +# SHA256 Fingerprint: 43:df:57:74:b0:3e:7f:ef:5f:e4:0d:93:1a:7b:ed:f1:bb:2e:6b:42:73:8c:4e:6d:38:41:10:3d:3a:a7:f3:39 +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy +NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T +RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN +cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW +wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 +U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 +jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN +BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ +jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v +1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R +nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH +VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - EC1" +# Serial: 51543124481930649114116133369 +# MD5 Fingerprint: b6:7e:1d:f0:58:c5:49:6c:24:3b:3d:ed:98:18:ed:bc +# SHA1 Fingerprint: 20:d8:06:40:df:9b:25:f5:12:25:3a:11:ea:f7:59:8a:eb:14:b5:47 +# SHA256 Fingerprint: 02:ed:0e:b2:8c:14:da:45:16:5c:56:67:91:70:0d:64:51:d7:fb:56:f0:b2:ab:1d:3b:8e:b0:70:e5:6e:df:f5 +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 +d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu +dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq +RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy +MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD +VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g +Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi +A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt +ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH +Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC +R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX +hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +# Issuer: CN=CFCA EV ROOT O=China Financial Certification Authority +# Subject: CN=CFCA EV ROOT O=China Financial Certification Authority +# Label: "CFCA EV ROOT" +# Serial: 407555286 +# MD5 Fingerprint: 74:e1:b6:ed:26:7a:7a:44:30:33:94:ab:7b:27:81:30 +# SHA1 Fingerprint: e2:b8:29:4b:55:84:ab:6b:58:c2:90:46:6c:ac:3f:b8:39:8f:84:83 +# SHA256 Fingerprint: 5c:c3:d7:8e:4e:1d:5e:45:54:7a:04:e6:87:3e:64:f9:0c:f9:53:6d:1c:cc:2e:f8:00:f3:55:c4:c5:fd:70:fd +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx +MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP +T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 +sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL +TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 +/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp +7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz +EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt +hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP +a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot +aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg +TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV +PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv +cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL +tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT +ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL +jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS +ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy +P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 +xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d +Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN +5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe +/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z +AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ +5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GB CA" +# Serial: 157768595616588414422159278966750757568 +# MD5 Fingerprint: a4:eb:b9:61:28:2e:b7:2f:98:b0:35:26:90:99:51:1d +# SHA1 Fingerprint: 0f:f9:40:76:18:d3:d7:6a:4b:98:f0:a8:35:9e:0c:fd:27:ac:cc:ed +# SHA256 Fingerprint: 6b:9c:08:e8:6e:b0:f7:67:cf:ad:65:cd:98:b6:21:49:e5:49:4a:67:f5:84:5e:7b:d1:ed:01:9f:27:b8:6b:d6 +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt +MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg +Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i +YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x +CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG +b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3 +HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx +WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX +1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk +u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P +99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r +M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB +BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh +cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5 +gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO +ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf +aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- + +# Issuer: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Subject: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Label: "SZAFIR ROOT CA2" +# Serial: 357043034767186914217277344587386743377558296292 +# MD5 Fingerprint: 11:64:c1:89:b0:24:b1:8c:b1:07:7e:89:9e:51:9e:99 +# SHA1 Fingerprint: e2:52:fa:95:3f:ed:db:24:60:bd:6e:28:f3:9c:cc:cf:5e:b3:3f:de +# SHA256 Fingerprint: a1:33:9d:33:28:1a:0b:56:e5:57:d3:d3:2b:1c:e7:f9:36:7e:b0:94:bd:5f:a7:2a:7e:50:04:c8:de:d7:ca:fe +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6 +ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw +NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L +cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg +Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN +QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT +3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw +3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6 +3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5 +BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN +XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF +AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw +8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG +nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP +oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy +d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg +LvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA 2" +# Serial: 44979900017204383099463764357512596969 +# MD5 Fingerprint: 6d:46:9e:d9:25:6d:08:23:5b:5e:74:7d:1e:27:db:f2 +# SHA1 Fingerprint: d3:dd:48:3e:2b:bf:4c:05:e8:af:10:f5:fa:76:26:cf:d3:dc:30:92 +# SHA256 Fingerprint: b6:76:f2:ed:da:e8:77:5c:d3:6c:b0:f6:3c:d1:d4:60:39:61:f4:9e:62:65:ba:01:3a:2f:03:07:b6:d0:b8:04 +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB +gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu +QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG +A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz +OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ +VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3 +b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA +DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn +0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB +OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE +fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E +Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m +o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i +sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW +OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez +Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS +adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n +3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ +F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf +CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29 +XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm +djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/ +WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb +AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq +P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko +b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj +XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P +5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi +DrW5viSP +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: ca:ff:e2:db:03:d9:cb:4b:e9:0f:ad:84:fd:7b:18:ce +# SHA1 Fingerprint: 01:0c:06:95:a6:98:19:14:ff:bf:5f:c6:b0:b6:95:ea:29:e9:12:a6 +# SHA256 Fingerprint: a0:40:92:9a:02:ce:53:b4:ac:f4:f2:ff:c6:98:1c:e4:49:6f:75:5e:6d:45:fe:0b:2a:69:2b:cd:52:52:3f:36 +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix +DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k +IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT +N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v +dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG +A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh +ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx +QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA +4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0 +AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10 +4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C +ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV +9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD +gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6 +Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq +NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko +LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd +ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I +XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI +M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot +9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V +Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea +j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh +X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ +l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf +bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4 +pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK +e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0 +vm9qp/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions ECC RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: 81:e5:b4:17:eb:c2:f5:e1:4b:0d:41:7b:49:92:fe:ef +# SHA1 Fingerprint: 9f:f1:71:8d:92:d5:9a:f3:7d:74:97:b4:bc:6f:84:68:0b:ba:b6:66 +# SHA256 Fingerprint: 44:b5:45:aa:8a:25:e6:5a:73:ca:15:dc:27:fc:36:d2:4c:1c:b9:95:3a:06:65:39:b1:15:82:dc:48:7b:48:33 +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN +BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl +bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv +b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ +BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj +YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5 +MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0 +dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg +QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa +jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi +C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep +lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof +TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +# Issuer: CN=ISRG Root X1 O=Internet Security Research Group +# Subject: CN=ISRG Root X1 O=Internet Security Research Group +# Label: "ISRG Root X1" +# Serial: 172886928669790476064670243504169061120 +# MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e +# SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8 +# SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6 +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +# Issuer: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Subject: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Label: "AC RAIZ FNMT-RCM" +# Serial: 485876308206448804701554682760554759 +# MD5 Fingerprint: e2:09:04:b4:d3:bd:d1:a0:14:fd:1a:d2:47:c4:57:1d +# SHA1 Fingerprint: ec:50:35:07:b2:15:c4:95:62:19:e2:a8:9a:5b:42:99:2c:4c:2c:20 +# SHA256 Fingerprint: eb:c5:57:0c:29:01:8c:4d:67:b1:aa:12:7b:af:12:f7:03:b4:61:1e:bc:17:b7:da:b5:57:38:94:17:9b:93:fa +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx +CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ +WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ +BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG +Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/ +yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf +BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz +WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF +tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z +374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC +IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL +mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7 +wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS +MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2 +ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet +UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H +YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3 +LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1 +RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM +LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf +77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N +JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm +fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp +6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp +1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B +9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok +RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv +uu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 1 O=Amazon +# Subject: CN=Amazon Root CA 1 O=Amazon +# Label: "Amazon Root CA 1" +# Serial: 143266978916655856878034712317230054538369994 +# MD5 Fingerprint: 43:c6:bf:ae:ec:fe:ad:2f:18:c6:88:68:30:fc:c8:e6 +# SHA1 Fingerprint: 8d:a7:f9:65:ec:5e:fc:37:91:0f:1c:6e:59:fd:c1:cc:6a:6e:de:16 +# SHA256 Fingerprint: 8e:cd:e6:88:4f:3d:87:b1:12:5b:a3:1a:c3:fc:b1:3d:70:16:de:7f:57:cc:90:4f:e1:cb:97:c6:ae:98:19:6e +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 2 O=Amazon +# Subject: CN=Amazon Root CA 2 O=Amazon +# Label: "Amazon Root CA 2" +# Serial: 143266982885963551818349160658925006970653239 +# MD5 Fingerprint: c8:e5:8d:ce:a8:42:e2:7a:c0:2a:5c:7c:9e:26:bf:66 +# SHA1 Fingerprint: 5a:8c:ef:45:d7:a6:98:59:76:7a:8c:8b:44:96:b5:78:cf:47:4b:1a +# SHA256 Fingerprint: 1b:a5:b2:aa:8c:65:40:1a:82:96:01:18:f8:0b:ec:4f:62:30:4d:83:ce:c4:71:3a:19:c3:9c:01:1e:a4:6d:b4 +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK +gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ +W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg +1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K +8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r +2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me +z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR +8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj +mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz +7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 ++XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI +0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm +UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 +LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS +k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl +7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm +btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl +urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ +fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 +n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE +76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H +9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT +4PsJYGw= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 3 O=Amazon +# Subject: CN=Amazon Root CA 3 O=Amazon +# Label: "Amazon Root CA 3" +# Serial: 143266986699090766294700635381230934788665930 +# MD5 Fingerprint: a0:d4:ef:0b:f7:b5:d8:49:95:2a:ec:f5:c4:fc:81:87 +# SHA1 Fingerprint: 0d:44:dd:8c:3c:8c:1a:1a:58:75:64:81:e9:0f:2e:2a:ff:b3:d2:6e +# SHA256 Fingerprint: 18:ce:6c:fe:7b:f1:4e:60:b2:e3:47:b8:df:e8:68:cb:31:d0:2e:bb:3a:da:27:15:69:f5:03:43:b4:6d:b3:a4 +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl +ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr +ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr +BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM +YyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 4 O=Amazon +# Subject: CN=Amazon Root CA 4 O=Amazon +# Label: "Amazon Root CA 4" +# Serial: 143266989758080763974105200630763877849284878 +# MD5 Fingerprint: 89:bc:27:d5:eb:17:8d:06:6a:69:d5:fd:89:47:b4:cd +# SHA1 Fingerprint: f6:10:84:07:d6:f8:bb:67:98:0c:c2:e2:44:c2:eb:ae:1c:ef:63:be +# SHA256 Fingerprint: e3:5d:28:41:9e:d0:20:25:cf:a6:90:38:cd:62:39:62:45:8d:a5:c6:95:fb:de:a3:c2:2b:0b:fb:25:89:70:92 +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi +9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk +M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB +MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw +CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW +1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +# Issuer: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Subject: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Label: "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1" +# Serial: 1 +# MD5 Fingerprint: dc:00:81:dc:69:2f:3e:2f:b0:3b:f6:3d:5a:91:8e:49 +# SHA1 Fingerprint: 31:43:64:9b:ec:ce:27:ec:ed:3a:3f:0b:8f:0d:e4:e8:91:dd:ee:ca +# SHA256 Fingerprint: 46:ed:c3:68:90:46:d5:3a:45:3f:b3:10:4a:b8:0d:ca:ec:65:8b:26:60:ea:16:29:dd:7e:86:79:90:64:87:16 +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx +GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp +bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w +KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0 +BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy +dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG +EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll +IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU +QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT +TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg +LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7 +a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr +LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr +N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X +YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/ +iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f +AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH +V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf +IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4 +lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c +8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf +lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- + +# Issuer: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Subject: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Label: "GDCA TrustAUTH R5 ROOT" +# Serial: 9009899650740120186 +# MD5 Fingerprint: 63:cc:d9:3d:34:35:5c:6f:53:a3:e2:08:70:48:1f:b4 +# SHA1 Fingerprint: 0f:36:38:5b:81:1a:25:c3:9b:31:4e:83:ca:e9:34:66:70:cc:74:b4 +# SHA256 Fingerprint: bf:ff:8f:d0:44:33:48:7d:6a:8a:a6:0c:1a:29:76:7a:9f:c2:bb:b0:5e:42:0f:71:3a:13:b9:92:89:1d:38:93 +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE +BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0 +MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w +HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj +Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj +TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u +KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj +qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm +MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12 +ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP +zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk +L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC +jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA +HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC +AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm +DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5 +COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry +L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf +JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg +IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io +2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV +09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ +XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq +T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe +MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Label: "SSL.com Root Certification Authority RSA" +# Serial: 8875640296558310041 +# MD5 Fingerprint: 86:69:12:c0:70:f1:ec:ac:ac:c2:d5:bc:a5:5b:a1:29 +# SHA1 Fingerprint: b7:ab:33:08:d1:ea:44:77:ba:14:80:12:5a:6f:bd:a9:36:49:0c:bb +# SHA256 Fingerprint: 85:66:6a:56:2e:e0:be:5c:e9:25:c1:d8:89:0a:6f:76:a8:7e:c1:6d:4d:7d:5f:29:ea:74:19:cf:20:12:3b:69 +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE +BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK +DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz +OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R +xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX +qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC +C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3 +6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh +/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF +YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E +JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc +US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8 +ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm ++Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi +M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G +A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV +cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc +Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs +PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/ +q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0 +cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr +a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I +H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y +K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu +nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf +oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY +Ic2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com Root Certification Authority ECC" +# Serial: 8495723813297216424 +# MD5 Fingerprint: 2e:da:e4:39:7f:9c:8f:37:d1:70:9f:26:17:51:3a:8e +# SHA1 Fingerprint: c3:19:7c:39:24:e6:54:af:1b:c4:ab:20:95:7a:e2:c3:0e:13:02:6a +# SHA256 Fingerprint: 34:17:bb:06:cc:60:07:da:1b:96:1c:92:0b:8a:b4:ce:3f:ad:82:0e:4a:a3:0b:9a:cb:c4:a7:4e:bd:ce:bc:65 +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz +WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0 +b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS +b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI +7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg +CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud +EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD +VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T +kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ +gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority RSA R2" +# Serial: 6248227494352943350 +# MD5 Fingerprint: e1:1e:31:58:1a:ae:54:53:02:f6:17:6a:11:7b:4d:95 +# SHA1 Fingerprint: 74:3a:f0:52:9b:d0:32:a0:f4:4a:83:cd:d4:ba:a9:7b:7c:2e:c4:9a +# SHA256 Fingerprint: 2e:7b:f1:6c:c2:24:85:a7:bb:e2:aa:86:96:75:07:61:b0:ae:39:be:3b:2f:e9:d0:cc:6d:4e:f7:34:91:42:5c +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV +BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE +CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy +MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G +A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD +DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq +M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf +OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa +4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9 +HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR +aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA +b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ +Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV +PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO +pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu +UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY +MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4 +9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW +s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5 +Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg +cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM +79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz +/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt +ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm +Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK +QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ +w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi +S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07 +mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority ECC" +# Serial: 3182246526754555285 +# MD5 Fingerprint: 59:53:22:65:83:42:01:54:c0:ce:42:b9:5a:7c:f2:90 +# SHA1 Fingerprint: 4c:dd:51:a3:d1:f5:20:32:14:b0:c6:c5:32:23:03:91:c7:46:42:6d +# SHA256 Fingerprint: 22:a2:c1:f7:bd:ed:70:4c:c1:e7:01:b5:f4:08:c3:10:88:0f:e9:56:b5:de:2a:4a:44:f9:9c:87:3a:25:a7:c8 +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx +NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv +bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA +VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku +WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX +5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ +ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg +h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Label: "GlobalSign Root CA - R6" +# Serial: 1417766617973444989252670301619537 +# MD5 Fingerprint: 4f:dd:07:e4:d4:22:64:39:1e:0c:37:42:ea:d1:c6:ae +# SHA1 Fingerprint: 80:94:64:0e:b5:a7:a1:ca:11:9c:1f:dd:d5:9f:81:02:63:a7:fb:d1 +# SHA256 Fingerprint: 2c:ab:ea:fe:37:d0:6c:a2:2a:ba:73:91:c0:03:3d:25:98:29:52:c4:53:64:73:49:76:3a:3a:b5:ad:6c:cf:69 +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg +MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx +MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET +MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI +xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k +ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD +aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw +LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw +1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX +k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2 +SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h +bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n +WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY +rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce +MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu +bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt +Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61 +55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj +vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf +cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz +oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp +nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs +pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v +JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R +8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4 +5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GC CA" +# Serial: 44084345621038548146064804565436152554 +# MD5 Fingerprint: a9:d6:b9:2d:2f:93:64:f8:a5:69:ca:91:e9:68:07:23 +# SHA1 Fingerprint: e0:11:84:5e:34:de:be:88:81:b9:9c:f6:16:26:d1:96:1f:c3:b9:31 +# SHA256 Fingerprint: 85:60:f9:1c:36:24:da:ba:95:70:b5:fe:a0:db:e3:6f:f1:1a:83:23:be:94:86:85:4f:b3:f3:4a:55:71:19:8d +-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw +CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91 +bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg +Um9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJ +BgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3Vu +ZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2JhbCBS +b290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4ni +eUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4W +p2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T +rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV +57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg +Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE----- + +# Issuer: CN=UCA Global G2 Root O=UniTrust +# Subject: CN=UCA Global G2 Root O=UniTrust +# Label: "UCA Global G2 Root" +# Serial: 124779693093741543919145257850076631279 +# MD5 Fingerprint: 80:fe:f0:c4:4a:f0:5c:62:32:9f:1c:ba:78:a9:50:f8 +# SHA1 Fingerprint: 28:f9:78:16:19:7a:ff:18:25:18:aa:44:fe:c1:a0:ce:5c:b6:4c:8a +# SHA256 Fingerprint: 9b:ea:11:c9:76:fe:01:47:64:c1:be:56:a6:f9:14:b5:a5:60:31:7a:bd:99:88:39:33:82:e5:16:1a:a0:49:3c +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9 +MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBH +bG9iYWwgRzIgUm9vdDAeFw0xNjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0x +CzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEbMBkGA1UEAwwSVUNBIEds +b2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxeYr +b3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmToni9 +kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzm +VHqUwCoV8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/R +VogvGjqNO7uCEeBHANBSh6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDc +C/Vkw85DvG1xudLeJ1uK6NjGruFZfc8oLTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIj +tm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/R+zvWr9LesGtOxdQXGLY +D0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBeKW4bHAyv +j5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6Dl +NaBa4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6 +iIis7nCs+dwp4wwcOxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznP +O6Q0ibd5Ei9Hxeepl2n8pndntd978XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIHEjMz15DD/pQwIX4wV +ZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo5sOASD0Ee/oj +L3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 +1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl +1qnN3e92mI0ADs0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oU +b3n09tDh05S60FdRvScFDcH9yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LV +PtateJLbXDzz2K36uGt/xDYotgIVilQsnLAXc47QN6MUPJiVAAwpBVueSUmxX8fj +y88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHojhJi6IjMtX9Gl8Cb +EGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZkbxqg +DMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI ++Vg7RE+xygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGy +YiGqhkCyLmTTX8jjfhFnRR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bX +UB+K+wb1whnw0A== +-----END CERTIFICATE----- + +# Issuer: CN=UCA Extended Validation Root O=UniTrust +# Subject: CN=UCA Extended Validation Root O=UniTrust +# Label: "UCA Extended Validation Root" +# Serial: 106100277556486529736699587978573607008 +# MD5 Fingerprint: a1:f3:5f:43:c6:34:9b:da:bf:8c:7e:05:53:ad:96:e2 +# SHA1 Fingerprint: a3:a1:b0:6f:24:61:23:4a:e3:36:a5:c2:37:fc:a6:ff:dd:f0:d7:3a +# SHA256 Fingerprint: d4:3a:f9:b3:54:73:75:5c:96:84:fc:06:d7:d8:cb:70:ee:5c:28:e7:73:fb:29:4e:b4:1e:e7:17:22:92:4d:24 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBH +MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBF +eHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMx +MDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNV +BAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrsiWog +D4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvS +sPGP2KxFRv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aop +O2z6+I9tTcg1367r3CTueUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dk +sHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR59mzLC52LqGj3n5qiAno8geK+LLNEOfi +c0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH0mK1lTnj8/FtDw5lhIpj +VMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KRel7sFsLz +KuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/ +TuDvB0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41G +sx2VYVdWf6/wFlthWG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs +1+lvK9JKBZP8nm9rZ/+I8U6laUpSNwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQD +fwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS3H5aBZ8eNJr34RQwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBADaN +l8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR +ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQ +VBcZEhrxH9cMaVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5 +c6sq1WnIeJEmMX3ixzDx/BR4dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp +4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb+7lsq+KePRXBOy5nAliRn+/4Qh8s +t2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOWF3sGPjLtx7dCvHaj +2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwiGpWO +vpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2C +xR9GUeOcGMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmx +cmtpzyKEC2IPrNkZAJSidjzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbM +fjKaiJUINlK73nZfdklJrX+9ZSCyycErdhh2n1ax +-----END CERTIFICATE----- + +# Issuer: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 +# Subject: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 +# Label: "Certigna Root CA" +# Serial: 269714418870597844693661054334862075617 +# MD5 Fingerprint: 0e:5c:30:62:27:eb:5b:bc:d7:ae:62:ba:e9:d5:df:77 +# SHA1 Fingerprint: 2d:0d:52:14:ff:9e:ad:99:24:01:74:20:47:6e:6c:85:27:27:f5:43 +# SHA256 Fingerprint: d4:8d:3d:23:ee:db:50:a4:59:e5:51:97:60:1c:27:77:4b:9d:7b:18:c9:4d:5a:05:95:11:a1:02:50:b9:31:68 +-----BEGIN CERTIFICATE----- +MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw +WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw +MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x +MzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjdaMFoxCzAJBgNVBAYTAkZSMRIwEAYD +VQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYzMDgxMDAwMzYxGTAX +BgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sO +ty3tRQgXstmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9M +CiBtnyN6tMbaLOQdLNyzKNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPu +I9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8JXrJhFwLrN1CTivngqIkicuQstDuI7pm +TLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16XdG+RCYyKfHx9WzMfgIh +C59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq4NYKpkDf +ePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3Yz +IoejwpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWT +Co/1VTp2lc5ZmIoJlXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1k +JWumIWmbat10TWuXekG9qxf5kBdIjzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5 +hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp//TBt2dzhauH8XwIDAQABo4IB +GjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of +1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczov +L3d3d3cuY2VydGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilo +dHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYr +aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hcm9vdGNhLmNybDANBgkq +hkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOItOoldaDgvUSILSo3L +6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxPTGRG +HVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH6 +0BGM+RFq7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncB +lA2c5uk5jR+mUYyZDDl34bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdi +o2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1 +gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS6Cvu5zHbugRqh5jnxV/v +faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63 +Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh +jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw +3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= +-----END CERTIFICATE----- + +# Issuer: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI +# Subject: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI +# Label: "emSign Root CA - G1" +# Serial: 235931866688319308814040 +# MD5 Fingerprint: 9c:42:84:57:dd:cb:0b:a7:2e:95:ad:b6:f3:da:bc:ac +# SHA1 Fingerprint: 8a:c7:ad:8f:73:ac:4e:c1:b5:75:4d:a5:40:f4:fc:cf:7c:b5:8e:8c +# SHA256 Fingerprint: 40:f6:af:03:46:a9:9a:a1:cd:1d:55:5a:4e:9c:ce:62:c7:f9:63:46:03:ee:40:66:15:83:3d:c8:c8:d0:03:67 +-----BEGIN CERTIFICATE----- +MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD +VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU +ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH +MTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklO +MRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRlY2hub2xv +Z2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQz +f2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO +8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq +d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhM +tTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSzt +Od9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQAB +o0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31x +PaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjM +wiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6d +GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH +6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby +RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx +iN66zB+Afko= +-----END CERTIFICATE----- + +# Issuer: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI +# Subject: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI +# Label: "emSign ECC Root CA - G3" +# Serial: 287880440101571086945156 +# MD5 Fingerprint: ce:0b:72:d1:9f:88:8e:d0:50:03:e8:e3:b8:8b:67:40 +# SHA1 Fingerprint: 30:43:fa:4f:f2:57:dc:a0:c3:80:ee:2e:58:ea:78:b2:3f:e6:bb:c1 +# SHA256 Fingerprint: 86:a1:ec:ba:08:9c:4a:8d:3b:be:27:34:c6:12:ba:34:1d:81:3e:04:3c:f9:e8:a8:62:cd:5c:57:a3:6b:be:6b +-----BEGIN CERTIFICATE----- +MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG +EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo +bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g +RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ +TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s +b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0 +WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS +fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB +zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq +hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB +CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD ++JbNR6iC8hZVdyR+EhCVBCyj +-----END CERTIFICATE----- + +# Issuer: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI +# Subject: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI +# Label: "emSign Root CA - C1" +# Serial: 825510296613316004955058 +# MD5 Fingerprint: d8:e3:5d:01:21:fa:78:5a:b0:df:ba:d2:ee:2a:5f:68 +# SHA1 Fingerprint: e7:2e:f1:df:fc:b2:09:28:cf:5d:d4:d5:67:37:b1:51:cb:86:4f:01 +# SHA256 Fingerprint: 12:56:09:aa:30:1d:a0:a2:49:b9:7a:82:39:cb:6a:34:21:6f:44:dc:ac:9f:39:54:b1:42:92:f2:e8:c8:60:8f +-----BEGIN CERTIFICATE----- +MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkG +A1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg +SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAw +MFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln +biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNpZ24gUm9v +dCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+upufGZ +BczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZ +HdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH +3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH +GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+c +xSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1 +aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiq +TbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87 +/kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4 +kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrG +YQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcxHpvOJpkT ++xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo +WXzhriKi4gp6D/piq1JM4fHfyr6DDUI= +-----END CERTIFICATE----- + +# Issuer: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI +# Subject: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI +# Label: "emSign ECC Root CA - C3" +# Serial: 582948710642506000014504 +# MD5 Fingerprint: 3e:53:b3:a3:81:ee:d7:10:f8:d3:b0:1d:17:92:f5:d5 +# SHA1 Fingerprint: b6:af:43:c2:9b:81:53:7d:f6:ef:6b:c3:1f:1f:60:15:0c:ee:48:66 +# SHA256 Fingerprint: bc:4d:80:9b:15:18:9d:78:db:3e:1d:8c:f4:f9:72:6a:79:5d:a1:64:3c:a5:f1:35:8e:1d:db:0e:dc:0d:7e:b3 +-----BEGIN CERTIFICATE----- +MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQG +EwJVUzETMBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMx +IDAeBgNVBAMTF2VtU2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAw +MFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln +biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQDExdlbVNpZ24gRUND +IFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd6bci +MK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4Ojavti +sIGJAnB9SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0O +BBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB +Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c +3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J +0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ== +-----END CERTIFICATE----- + +# Issuer: CN=Hongkong Post Root CA 3 O=Hongkong Post +# Subject: CN=Hongkong Post Root CA 3 O=Hongkong Post +# Label: "Hongkong Post Root CA 3" +# Serial: 46170865288971385588281144162979347873371282084 +# MD5 Fingerprint: 11:fc:9f:bd:73:30:02:8a:fd:3f:f3:58:b9:cb:20:f0 +# SHA1 Fingerprint: 58:a2:d0:ec:20:52:81:5b:c1:f3:f8:64:02:24:4e:c2:8e:02:4b:02 +# SHA256 Fingerprint: 5a:2f:c0:3f:0c:83:b0:90:bb:fa:40:60:4b:09:88:44:6c:76:36:18:3d:f9:84:6e:17:10:1a:44:7f:b8:ef:d6 +-----BEGIN CERTIFICATE----- +MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQEL +BQAwbzELMAkGA1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJ +SG9uZyBLb25nMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25n +a29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2MDMwMjI5NDZaFw00MjA2MDMwMjI5 +NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtvbmcxEjAQBgNVBAcT +CUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMXSG9u +Z2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCziNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFO +dem1p+/l6TWZ5Mwc50tfjTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mI +VoBc+L0sPOFMV4i707mV78vH9toxdCim5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV +9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOesL4jpNrcyCse2m5FHomY +2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj0mRiikKY +vLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+Tt +bNe/JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZb +x39ri1UbSsUgYT2uy1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+ +l2oBlKN8W4UdKjk60FSh0Tlxnf0h+bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YK +TE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsGxVd7GYYKecsAyVKvQv83j+Gj +Hno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwIDAQABo2MwYTAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e +i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEw +DQYJKoZIhvcNAQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG +7BJ8dNVI0lkUmcDrudHr9EgwW62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCk +MpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWldy8joRTnU+kLBEUx3XZL7av9YROXr +gZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov+BS5gLNdTaqX4fnk +GMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDceqFS +3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJm +Ozj/2ZQw9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+ +l6mc1X5VTMbeRRAc6uk7nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6c +JfTzPV4e0hz5sy229zdcxsshTrD3mUcYhcErulWuBurQB7Lcq9CClnXO0lD+mefP +L5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB60PZ2Pierc+xYw5F9KBa +LJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEG +mpv0 +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - G4 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2015 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - G4 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2015 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - G4" +# Serial: 289383649854506086828220374796556676440 +# MD5 Fingerprint: 89:53:f1:83:23:b7:7c:8e:05:f1:8c:71:38:4e:1f:88 +# SHA1 Fingerprint: 14:88:4e:86:26:37:b0:26:af:59:62:5c:40:77:ec:35:29:ba:96:01 +# SHA256 Fingerprint: db:35:17:d1:f6:73:2a:2d:5a:b9:7c:53:3e:c7:07:79:ee:32:70:a6:2f:b4:ac:42:38:37:24:60:e6:f0:1e:88 +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAw +gb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL +Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg +MjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAw +BgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0 +MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1 +c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJ +bmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3Qg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3DumSXbcr3DbVZwbPLqGgZ +2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV3imz/f3E +T+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j +5pds8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAM +C1rlLAHGVK/XqsEQe9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73T +DtTUXm6Hnmo9RR3RXRv06QqsYJn7ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNX +wbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5XxNMhIWNlUpEbsZmOeX7m640A +2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV7rtNOzK+mndm +nqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8 +dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwl +N4y6mACXi0mWHv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNj +c0kCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9nMA0GCSqGSIb3DQEBCwUAA4ICAQAS +5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4QjbRaZIxowLByQzTS +Gwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht7LGr +hFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/ +B7NTeLUKYvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uI +AeV8KEsD+UmDfLJ/fOPtjqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbw +H5Lk6rWS02FREAutp9lfx1/cH6NcjKF+m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+ +b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKWRGhXxNUzzxkvFMSUHHuk +2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjAJOgc47Ol +IQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk +5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuY +n/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw== +-----END CERTIFICATE----- + +# Issuer: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation +# Subject: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation +# Label: "Microsoft ECC Root Certificate Authority 2017" +# Serial: 136839042543790627607696632466672567020 +# MD5 Fingerprint: dd:a1:03:e6:4a:93:10:d1:bf:f0:19:42:cb:fe:ed:67 +# SHA1 Fingerprint: 99:9a:64:c3:7f:f4:7d:9f:ab:95:f1:47:69:89:14:60:ee:c4:c3:c5 +# SHA256 Fingerprint: 35:8d:f3:9d:76:4a:f9:e1:b7:66:e9:c9:72:df:35:2e:e1:5c:fa:c2:27:af:6a:d1:d7:0e:8e:4a:6e:dc:ba:02 +-----BEGIN CERTIFICATE----- +MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD +VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw +MTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJV +UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy +b3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZR +ogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYb +hGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3 +FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV +L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB +iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= +-----END CERTIFICATE----- + +# Issuer: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation +# Subject: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation +# Label: "Microsoft RSA Root Certificate Authority 2017" +# Serial: 40975477897264996090493496164228220339 +# MD5 Fingerprint: 10:ff:00:ff:cf:c9:f8:c7:7a:c0:ee:35:8e:c9:0f:47 +# SHA1 Fingerprint: 73:a5:e6:4a:3b:ff:83:16:ff:0e:dc:cc:61:8a:90:6e:4e:ae:4d:74 +# SHA256 Fingerprint: c7:41:f7:0f:4b:2a:8d:88:bf:2e:71:c1:41:22:ef:53:ef:10:eb:a0:cf:a5:e6:4c:fa:20:f4:18:85:30:73:e0 +-----BEGIN CERTIFICATE----- +MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl +MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw +NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQG +EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1N +aWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZ +Nt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0 +ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1 +HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztm +gGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJ +jEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUc +aDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaG +YaRSMLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6 +W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4K +UGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH ++FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avNJVgyeY+Q +W5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZC +LgLNFgVZJ8og6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OC +gMNPOsduET/m4xaRhPtthH80dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6 +tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk+ONVFT24bcMKpBLBaYVu32TxU5nh +SnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex/2kskZGT4d9Mozd2 +TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDyAmH3 +pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGR +xpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp +GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9 +dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN +AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB +RA+GsCyRxj3qrg+E +-----END CERTIFICATE----- + +# Issuer: CN=e-Szigno Root CA 2017 O=Microsec Ltd. +# Subject: CN=e-Szigno Root CA 2017 O=Microsec Ltd. +# Label: "e-Szigno Root CA 2017" +# Serial: 411379200276854331539784714 +# MD5 Fingerprint: de:1f:f6:9e:84:ae:a7:b4:21:ce:1e:58:7d:d1:84:98 +# SHA1 Fingerprint: 89:d4:83:03:4f:9e:9a:48:80:5f:72:37:d4:a9:a6:ef:cb:7c:1f:d1 +# SHA256 Fingerprint: be:b0:0b:30:83:9b:9b:c3:2c:32:e4:44:79:05:95:06:41:f2:64:21:b1:5e:d0:89:19:8b:51:8a:e2:ea:1b:99 +-----BEGIN CERTIFICATE----- +MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV +BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk +LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv +b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ +BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg +THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v +IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv +xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H +Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB +eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo +jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ ++efcMQ== +-----END CERTIFICATE----- + +# Issuer: O=CERTSIGN SA OU=certSIGN ROOT CA G2 +# Subject: O=CERTSIGN SA OU=certSIGN ROOT CA G2 +# Label: "certSIGN Root CA G2" +# Serial: 313609486401300475190 +# MD5 Fingerprint: 8c:f1:75:8a:c6:19:cf:94:b7:f7:65:20:87:c3:97:c7 +# SHA1 Fingerprint: 26:f9:93:b4:ed:3d:28:27:b0:b9:4b:a7:e9:15:1d:a3:8d:92:e5:32 +# SHA256 Fingerprint: 65:7c:fe:2f:a7:3f:aa:38:46:25:71:f3:32:a2:36:3a:46:fc:e7:02:09:51:71:07:02:cd:fb:b6:ee:da:33:05 +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV +BAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04g +Uk9PVCBDQSBHMjAeFw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJ +BgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJ +R04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDF +dRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05N0Iw +vlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZ +uIt4ImfkabBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhp +n+Sc8CnTXPnGFiWeI8MgwT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKs +cpc/I1mbySKEwQdPzH/iV8oScLumZfNpdWO9lfsbl83kqK/20U6o2YpxJM02PbyW +xPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91QqhngLjYl/rNUssuHLoPj1P +rCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732jcZZroiF +DsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fx +DTvf95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgy +LcsUDFDYg2WD7rlcz8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6C +eWRgKRM+o/1Pcmqr4tTluCRVLERLiohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSCIS1mxteg4BXrzkwJ +d8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOBywaK8SJJ6ejq +kX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC +b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQl +qiCA2ClV9+BB/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0 +OJD7uNGzcgbJceaBxXntC6Z58hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+c +NywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5BiKDUyUM/FHE5r7iOZULJK2v0ZXk +ltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklWatKcsWMy5WHgUyIO +pwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tUSxfj +03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZk +PuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE +1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MX +QRBdJ3NghVdJIgc= +-----END CERTIFICATE----- + +# Issuer: CN=Trustwave Global Certification Authority O=Trustwave Holdings, Inc. +# Subject: CN=Trustwave Global Certification Authority O=Trustwave Holdings, Inc. +# Label: "Trustwave Global Certification Authority" +# Serial: 1846098327275375458322922162 +# MD5 Fingerprint: f8:1c:18:2d:2f:ba:5f:6d:a1:6c:bc:c7:ab:91:c7:0e +# SHA1 Fingerprint: 2f:8f:36:4f:e1:58:97:44:21:59:87:a5:2a:9a:d0:69:95:26:7f:b5 +# SHA256 Fingerprint: 97:55:20:15:f5:dd:fc:3c:87:88:c0:06:94:45:55:40:88:94:45:00:84:f1:00:86:70:86:bc:1a:2b:b5:8d:c8 +-----BEGIN CERTIFICATE----- +MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQsw +CQYDVQQGEwJVUzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28x +ITAfBgNVBAoMGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1 +c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMx +OTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJVUzERMA8GA1UECAwI +SWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2ZSBI +b2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +ALldUShLPDeS0YLOvR29zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0Xzn +swuvCAAJWX/NKSqIk4cXGIDtiLK0thAfLdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu +7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4BqstTnoApTAbqOl5F2brz8 +1Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9oWN0EACyW +80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotP +JqX+OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1l +RtzuzWniTY+HKE40Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfw +hI0Vcnyh78zyiGG69Gm7DIwLdVcEuE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10 +coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm+9jaJXLE9gCxInm943xZYkqc +BW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqjifLJS3tBEW1n +twiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1Ud +DwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W +0OhUKDtkLSGm+J1WE2pIPU/HPinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfe +uyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0HZJDmHvUqoai7PF35owgLEQzxPy0Q +lG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla4gt5kNdXElE1GYhB +aCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5RvbbE +sLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPT +MaCm/zjdzyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qe +qu5AvzSxnI9O4fKSTx+O856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxh +VicGaeVyQYHTtgGJoC86cnn+OjC/QezHYj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8 +h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu3R3y4G5OBVixwJAWKqQ9 +EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP29FpHOTK +yeC2nOnOcXHebD8WpHk= +-----END CERTIFICATE----- + +# Issuer: CN=Trustwave Global ECC P256 Certification Authority O=Trustwave Holdings, Inc. +# Subject: CN=Trustwave Global ECC P256 Certification Authority O=Trustwave Holdings, Inc. +# Label: "Trustwave Global ECC P256 Certification Authority" +# Serial: 4151900041497450638097112925 +# MD5 Fingerprint: 5b:44:e3:8d:5d:36:86:26:e8:0d:05:d2:59:a7:83:54 +# SHA1 Fingerprint: b4:90:82:dd:45:0c:be:8b:5b:b1:66:d3:e2:a4:08:26:cd:ed:42:cf +# SHA256 Fingerprint: 94:5b:bc:82:5e:a5:54:f4:89:d1:fd:51:a7:3d:df:2e:a6:24:ac:70:19:a0:52:05:22:5c:22:a7:8c:cf:a8:b4 +-----BEGIN CERTIFICATE----- +MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf +BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 +YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x +NzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYDVQQGEwJVUzERMA8G +A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0 +d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF +Q0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqG +SM49AwEHA0IABH77bOYj43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoN +FWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqmP62jQzBBMA8GA1UdEwEB/wQFMAMBAf8w +DwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt0UrrdaVKEJmzsaGLSvcw +CgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjzRM4q3wgh +DDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7 +-----END CERTIFICATE----- + +# Issuer: CN=Trustwave Global ECC P384 Certification Authority O=Trustwave Holdings, Inc. +# Subject: CN=Trustwave Global ECC P384 Certification Authority O=Trustwave Holdings, Inc. +# Label: "Trustwave Global ECC P384 Certification Authority" +# Serial: 2704997926503831671788816187 +# MD5 Fingerprint: ea:cf:60:c4:3b:b9:15:29:40:a1:97:ed:78:27:93:d6 +# SHA1 Fingerprint: e7:f3:a3:c8:cf:6f:c3:04:2e:6d:0e:67:32:c5:9e:68:95:0d:5e:d2 +# SHA256 Fingerprint: 55:90:38:59:c8:c0:c3:eb:b8:75:9e:ce:4e:25:57:22:5f:f5:75:8b:bd:38:eb:d4:82:76:60:1e:1b:d5:80:97 +-----BEGIN CERTIFICATE----- +MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf +BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 +YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x +NzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYDVQQGEwJVUzERMA8G +A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0 +d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF +Q0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuB +BAAiA2IABGvaDXU1CDFHBa5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJ +j9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr/TklZvFe/oyujUF5nQlgziip04pt89ZF +1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwYAMB0G +A1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNnADBkAjA3 +AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsC +MGclCrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVu +Sw== +-----END CERTIFICATE----- + +# Issuer: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. +# Subject: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. +# Label: "NAVER Global Root Certification Authority" +# Serial: 9013692873798656336226253319739695165984492813 +# MD5 Fingerprint: c8:7e:41:f6:25:3b:f5:09:b3:17:e8:46:3d:bf:d0:9b +# SHA1 Fingerprint: 8f:6b:f2:a9:27:4a:da:14:a0:c4:f4:8e:61:27:f9:c0:1e:78:5d:d1 +# SHA256 Fingerprint: 88:f4:38:dc:f8:ff:d1:fa:8f:42:91:15:ff:e5:f8:2a:e1:e0:6e:0c:70:c3:75:fa:ad:71:7b:34:a4:9e:72:65 +-----BEGIN CERTIFICATE----- +MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEM +BQAwaTELMAkGA1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRG +T1JNIENvcnAuMTIwMAYDVQQDDClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4NDJaFw0zNzA4MTgyMzU5NTlaMGkx +CzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVTUyBQTEFURk9STSBD +b3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVA +iQqrDZBbUGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH +38dq6SZeWYp34+hInDEW+j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lE +HoSTGEq0n+USZGnQJoViAbbJAh2+g1G7XNr4rRVqmfeSVPc0W+m/6imBEtRTkZaz +kVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2aacp+yPOiNgSnABIqKYP +szuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4Yb8Obtoq +vC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHf +nZ3zVHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaG +YQ5fG8Ir4ozVu53BA0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo +0es+nPxdGoMuK8u180SdOqcXYZaicdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3a +CJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejyYhbLgGvtPe31HzClrkvJE+2K +AQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNVHQ4EFgQU0p+I +36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB +Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoN +qo0hV4/GPnrK21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatj +cu3cvuzHV+YwIHHW1xDBE1UBjCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm ++LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bxhYTeodoS76TiEJd6eN4MUZeoIUCL +hr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTgE34h5prCy8VCZLQe +lHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTHD8z7 +p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8 +piKCk5XQA76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLR +LBT/DShycpWbXgnbiUSYqqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX +5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oGI/hGoiLtk/bdmuYqh7GYVPEi92tF4+KO +dh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmgkpzNNIaRkPpkUZ3+/uul +9XXeifdy +-----END CERTIFICATE----- + +# Issuer: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres +# Subject: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres +# Label: "AC RAIZ FNMT-RCM SERVIDORES SEGUROS" +# Serial: 131542671362353147877283741781055151509 +# MD5 Fingerprint: 19:36:9c:52:03:2f:d2:d1:bb:23:cc:dd:1e:12:55:bb +# SHA1 Fingerprint: 62:ff:d9:9e:c0:65:0d:03:ce:75:93:d2:ed:3f:2d:32:c9:e3:e5:4a +# SHA256 Fingerprint: 55:41:53:b1:3d:2c:f9:dd:b7:53:bf:be:1a:4e:0a:e0:8d:0a:a4:18:70:58:fe:60:a2:b8:62:b2:e4:b8:7b:cb +-----BEGIN CERTIFICATE----- +MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQsw +CQYDVQQGEwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgw +FgYDVQRhDA9WQVRFUy1RMjgyNjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1S +Q00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4MTIyMDA5MzczM1oXDTQzMTIyMDA5 +MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQtUkNNMQ4wDAYDVQQL +DAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNBQyBS +QUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LH +sbI6GA60XYyzZl2hNPk2LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oK +Um8BA06Oi6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqGSM49BAMDA2kAMGYCMQCu +SuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoDzBOQn5IC +MQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJy +v+c= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign Root R46 O=GlobalSign nv-sa +# Subject: CN=GlobalSign Root R46 O=GlobalSign nv-sa +# Label: "GlobalSign Root R46" +# Serial: 1552617688466950547958867513931858518042577 +# MD5 Fingerprint: c4:14:30:e4:fa:66:43:94:2a:6a:1b:24:5f:19:d0:ef +# SHA1 Fingerprint: 53:a2:b0:4b:ca:6b:d6:45:e6:39:8a:8e:c4:0d:d2:bf:77:c3:a2:90 +# SHA256 Fingerprint: 4f:a3:12:6d:8d:3a:11:d1:c4:85:5a:4f:80:7c:ba:d6:cf:91:9d:3a:5a:88:b0:3b:ea:2c:63:72:d9:3c:40:c9 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA +MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD +VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy +MDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt +c2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ +OaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG +vGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud +316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo +0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE +y132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF +zXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE ++cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN +I/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs +x2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa +ByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC +4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4 +7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg +JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti +2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk +pnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF +FRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt +rWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk +ZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5 +u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP +4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6 +N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3 +vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6 +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign Root E46 O=GlobalSign nv-sa +# Subject: CN=GlobalSign Root E46 O=GlobalSign nv-sa +# Label: "GlobalSign Root E46" +# Serial: 1552617690338932563915843282459653771421763 +# MD5 Fingerprint: b5:b8:66:ed:de:08:83:e3:c9:e2:01:34:06:ac:51:6f +# SHA1 Fingerprint: 39:b4:6c:d5:fe:80:06:eb:e2:2f:4a:bb:08:33:a0:af:db:b9:dd:84 +# SHA256 Fingerprint: cb:b9:c4:4d:84:b8:04:3e:10:50:ea:31:a6:9f:51:49:55:d7:bf:d2:e2:c6:b4:93:01:01:9a:d6:1d:9f:50:58 +-----BEGIN CERTIFICATE----- +MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx +CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD +ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw +MDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex +HDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkBjtjq +R+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGdd +yXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ +7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8 ++RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A= +-----END CERTIFICATE----- + +# Issuer: CN=GLOBALTRUST 2020 O=e-commerce monitoring GmbH +# Subject: CN=GLOBALTRUST 2020 O=e-commerce monitoring GmbH +# Label: "GLOBALTRUST 2020" +# Serial: 109160994242082918454945253 +# MD5 Fingerprint: 8a:c7:6f:cb:6d:e3:cc:a2:f1:7c:83:fa:0e:78:d7:e8 +# SHA1 Fingerprint: d0:67:c1:13:51:01:0c:aa:d0:c7:6a:65:37:31:16:26:4f:53:71:a2 +# SHA256 Fingerprint: 9a:29:6a:51:82:d1:d4:51:a2:e3:7f:43:9b:74:da:af:a2:67:52:33:29:f9:0f:9a:0d:20:07:c3:34:e2:3c:9a +-----BEGIN CERTIFICATE----- +MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkG +A1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkw +FwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYx +MDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9u +aXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMIICIjANBgkq +hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWiD59b +RatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9Z +YybNpyrOVPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3 +QWPKzv9pj2gOlTblzLmMCcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPw +yJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCmfecqQjuCgGOlYx8ZzHyyZqjC0203b+J+ +BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKAA1GqtH6qRNdDYfOiaxaJ +SaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9ORJitHHmkH +r96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj0 +4KlGDfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9Me +dKZssCz3AwyIDMvUclOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIw +q7ejMZdnrY8XD2zHc+0klGvIg5rQmjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2 +nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1UdIwQYMBaAFNwu +H9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA +VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJC +XtzoRlgHNQIw4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd +6IwPS3BD0IL/qMy/pJTAvoe9iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf ++I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS8cE54+X1+NZK3TTN+2/BT+MAi1bi +kvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2HcqtbepBEX4tdJP7 +wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxSvTOB +TI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6C +MUO+1918oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn +4rnvyOL2NSl6dPrFf4IFYqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+I +aFvowdlxfv1k7/9nR4hYJS8+hge9+6jlgqispdNpQ80xiEmEU5LAsTkbOYMBMMTy +qfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg== +-----END CERTIFICATE----- + +# Issuer: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz +# Subject: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz +# Label: "ANF Secure Server Root CA" +# Serial: 996390341000653745 +# MD5 Fingerprint: 26:a6:44:5a:d9:af:4e:2f:b2:1d:b6:65:b0:4e:e8:96 +# SHA1 Fingerprint: 5b:6e:68:d0:cc:15:b6:a0:5f:1e:c1:5f:ae:02:fc:6b:2f:5d:6f:74 +# SHA256 Fingerprint: fb:8f:ec:75:91:69:b9:10:6b:1e:51:16:44:c6:18:c5:13:04:37:3f:6c:06:43:08:8d:8b:ef:fd:1b:99:75:99 +-----BEGIN CERTIFICATE----- +MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNV +BAUTCUc2MzI4NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlk +YWQgZGUgQ2VydGlmaWNhY2lvbjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNV +BAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3QgQ0EwHhcNMTkwOTA0MTAwMDM4WhcN +MzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEwMQswCQYDVQQGEwJF +UzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQwEgYD +VQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9v +dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCj +cqQZAZ2cC4Ffc0m6p6zzBE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9q +yGFOtibBTI3/TO80sh9l2Ll49a2pcbnvT1gdpd50IJeh7WhM3pIXS7yr/2WanvtH +2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcvB2VSAKduyK9o7PQUlrZX +H1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXsezx76W0OL +zc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyR +p1RMVwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQz +W7i1o0TJrH93PB0j7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/ +SiOL9V8BY9KHcyi1Swr1+KuCLH5zJTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJn +LNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe8TZBAQIvfXOn3kLMTOmJDVb3 +n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVOHj1tyRRM4y5B +u8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj +o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC +AgEATh65isagmD9uw2nAalxJUqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L +9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzxj6ptBZNscsdW699QIyjlRRA96Gej +rw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDtdD+4E5UGUcjohybK +pFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM5gf0 +vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjq +OknkJjCb5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ +/zo1PqVUSlJZS2Db7v54EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ9 +2zg/LFis6ELhDtjTO0wugumDLmsx2d1Hhk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI ++PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGyg77FGr8H6lnco4g175x2 +MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3r5+qPeoo +tt7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw= +-----END CERTIFICATE----- + +# Issuer: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Subject: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Label: "Certum EC-384 CA" +# Serial: 160250656287871593594747141429395092468 +# MD5 Fingerprint: b6:65:b3:96:60:97:12:a1:ec:4e:e1:3d:a3:c6:c9:f1 +# SHA1 Fingerprint: f3:3e:78:3c:ac:df:f4:a2:cc:ac:67:55:69:56:d7:e5:16:3c:e1:ed +# SHA256 Fingerprint: 6b:32:80:85:62:53:18:aa:50:d1:73:c9:8d:8b:da:09:d5:7e:27:41:3d:11:4c:f7:87:a0:f5:d0:6c:03:0c:f6 +-----BEGIN CERTIFICATE----- +MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQsw +CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScw +JQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMT +EENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2MDcyNDU0WhcNNDMwMzI2MDcyNDU0 +WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBT +LkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAX +BgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATE +KI6rGFtqvm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7Tm +Fy8as10CW4kjPMIRBSqniBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68Kj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI0GZnQkdjrzife81r1HfS+8 +EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjADVS2m5hjEfO/J +UG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0QoSZ/6vn +nvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Root CA" +# Serial: 40870380103424195783807378461123655149 +# MD5 Fingerprint: 51:e1:c2:e7:fe:4c:84:af:59:0e:2f:f4:54:6f:ea:29 +# SHA1 Fingerprint: c8:83:44:c0:18:ae:9f:cc:f1:87:b7:8f:22:d1:c5:d7:45:84:ba:e5 +# SHA256 Fingerprint: fe:76:96:57:38:55:77:3e:37:a9:5e:7a:d4:d9:cc:96:c3:01:57:c1:5d:31:76:5b:a9:b1:57:04:e1:ae:78:fd +-----BEGIN CERTIFICATE----- +MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6 +MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu +MScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNV +BAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwHhcNMTgwMzE2MTIxMDEzWhcNNDMw +MzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEg +U3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZ +n0EGze2jusDbCSzBfN8pfktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/q +p1x4EaTByIVcJdPTsuclzxFUl6s1wB52HO8AU5853BSlLCIls3Jy/I2z5T4IHhQq +NwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2fJmItdUDmj0VDT06qKhF +8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGtg/BKEiJ3 +HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGa +mqi4NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi +7VdNIuJGmj8PkTQkfVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSF +ytKAQd8FqKPVhJBPC/PgP5sZ0jeJP/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0P +qafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSYnjYJdmZm/Bo/6khUHL4wvYBQ +v3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHKHRzQ+8S1h9E6 +Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1 +vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQAD +ggIBAEii1QALLtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4 +WxmB82M+w85bj/UvXgF2Ez8sALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvo +zMrnadyHncI013nR03e4qllY/p0m+jiGPp2Kh2RX5Rc64vmNueMzeMGQ2Ljdt4NR +5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8CYyqOhNf6DR5UMEQ +GfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA4kZf +5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq +0Uc9NneoWWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7D +P78v3DSk+yshzWePS/Tj6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTM +qJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmTOPQD8rv7gmsHINFSH5pkAnuYZttcTVoP +0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZckbxJF0WddCajJFdr60qZf +E2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb +-----END CERTIFICATE----- + +# Issuer: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique +# Subject: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique +# Label: "TunTrust Root CA" +# Serial: 108534058042236574382096126452369648152337120275 +# MD5 Fingerprint: 85:13:b9:90:5b:36:5c:b6:5e:b8:5a:f8:e0:31:57:b4 +# SHA1 Fingerprint: cf:e9:70:84:0f:e0:73:0f:9d:f6:0c:7f:2c:4b:ee:20:46:34:9c:bb +# SHA256 Fingerprint: 2e:44:10:2a:b5:8c:b8:54:19:45:1c:8e:19:d9:ac:f3:66:2c:af:bc:61:4b:6a:53:96:0a:30:f7:d0:e2:eb:41 +-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQEL +BQAwYTELMAkGA1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUg +Q2VydGlmaWNhdGlvbiBFbGVjdHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJv +b3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQwNDI2MDg1NzU2WjBhMQswCQYDVQQG +EwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBDZXJ0aWZpY2F0aW9u +IEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZ +n56eY+hz2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd +2JQDoOw05TDENX37Jk0bbjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgF +VwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZ +GoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAdgjH8KcwAWJeRTIAAHDOF +li/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViWVSHbhlnU +r8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2 +eY8fTpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIb +MlEsPvLfe/ZdeikZjuXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISg +jwBUFfyRbVinljvrS5YnzWuioYasDXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB +7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwSVXAkPcvCFDVDXSdOvsC9qnyW +5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI04Y+oXNZtPdE +ITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0 +90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+z +xiD2BkewhpMl0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYu +QEkHDVneixCwSQXi/5E/S7fdAo74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4 +FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRYYdZ2vyJ/0Adqp2RT8JeNnYA/u8EH +22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJpadbGNjHh/PqAulxP +xOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65xxBzn +dFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5 +Xc0yGYuPjCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7b +nV2UqL1g52KAdoGDDIzMMEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQ +CvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9zZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZH +u/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3rAZ3r2OvEhJn7wAzMMujj +d9qDRIueVSjAi1jTkD5OGwDxFa2DK5o= +-----END CERTIFICATE----- + +# Issuer: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Subject: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Label: "HARICA TLS RSA Root CA 2021" +# Serial: 76817823531813593706434026085292783742 +# MD5 Fingerprint: 65:47:9b:58:86:dd:2c:f0:fc:a2:84:1f:1e:96:c4:91 +# SHA1 Fingerprint: 02:2d:05:82:fa:88:ce:14:0c:06:79:de:7f:14:10:e9:45:d7:a5:6d +# SHA256 Fingerprint: d9:5d:0e:8e:da:79:52:5b:f9:be:b1:1b:14:d2:10:0d:32:94:98:5f:0c:62:d9:fa:bd:9c:d9:99:ec:cb:7b:1d +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBs +MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0Eg +Um9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUzOFoXDTQ1MDIxMzEwNTUzN1owbDEL +MAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl +YXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNBIFJv +b3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569l +mwVnlskNJLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE +4VGC/6zStGndLuwRo0Xua2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uv +a9of08WRiFukiZLRgeaMOVig1mlDqa2YUlhu2wr7a89o+uOkXjpFc5gH6l8Cct4M +pbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K5FrZx40d/JiZ+yykgmvw +Kh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEvdmn8kN3b +LW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcY +AuUR0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqB +AGMUuTNe3QvboEUHGjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYq +E613TBoYm5EPWNgGVMWX+Ko/IIqmhaZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHr +W2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQCPxrvrNQKlr9qEgYRtaQQJKQ +CoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAU +X15QvWiWkKQUEapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3 +f5Z2EMVGpdAgS1D0NTsY9FVqQRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxaja +H6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxDQpSbIPDRzbLrLFPCU3hKTwSUQZqP +JzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcRj88YxeMn/ibvBZ3P +zzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5vZSt +jBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0 +/L5H9MG0qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pT +BGIBnfHAT+7hOtSLIBD6Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79 +aPib8qXPMThcFarmlwDB31qlpzmq6YR/PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YW +xw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnnkf3/W9b3raYvAwtt41dU +63ZTGI0RmLo= +-----END CERTIFICATE----- + +# Issuer: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Subject: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Label: "HARICA TLS ECC Root CA 2021" +# Serial: 137515985548005187474074462014555733966 +# MD5 Fingerprint: ae:f7:4c:e5:66:35:d1:b7:9b:8c:22:93:74:d3:4b:b0 +# SHA1 Fingerprint: bc:b0:c1:9d:e9:98:92:70:19:38:57:e9:8d:a7:b4:5d:6e:ee:01:48 +# SHA256 Fingerprint: 3f:99:cc:47:4a:cf:ce:4d:fe:d5:87:94:66:5e:47:8d:15:47:73:9f:2e:78:0f:1b:b4:ca:9b:13:30:97:d4:01 +-----BEGIN CERTIFICATE----- +MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQsw +CQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2Vh +cmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9v +dCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoXDTQ1MDIxMzExMDEwOVowbDELMAkG +A1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj +aCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJvb3Qg +Q0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7 +KKrxcm1lAEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9Y +STHMmE5gEYd103KUkE+bECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQD +AgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAircJRQO9gcS3ujwLEXQNw +SaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/QwCZ61IygN +nxS2PFOiTAZpffpskcYqSUXm7LcT4Tps +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" +# Serial: 1977337328857672817 +# MD5 Fingerprint: 4e:6e:9b:54:4c:ca:b7:fa:48:e4:90:b1:15:4b:1c:a3 +# SHA1 Fingerprint: 0b:be:c2:27:22:49:cb:39:aa:db:35:5c:53:e3:8c:ae:78:ff:b6:fe +# SHA256 Fingerprint: 57:de:05:83:ef:d2:b2:6e:03:61:da:99:da:9d:f4:64:8d:ef:7e:e8:44:1c:3b:72:8a:fa:9b:cd:e0:f9:b2:6a +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1 +MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1UdDgQWBBRlzeurNR4APn7VdMAc +tHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4wgZswgZgGBFUd +IAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j +b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABC +AG8AbgBhAG4AbwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAw +ADEANzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9m +iWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL4QjbEwj4KKE1soCzC1HA01aajTNF +Sa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDbLIpgD7dvlAceHabJ +hfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1ilI45P +Vf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZE +EAEeiGaPcjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV +1aUsIC+nmCjuRfzxuIgALI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2t +CsvMo2ebKHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR +5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH9IBk9W6VULgRfhVwOEqw +f9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9 +ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNK +GbqEZycPvEJdvSRUDewdcAZfpLz6IHxV +-----END CERTIFICATE----- + +# Issuer: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd. +# Subject: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd. +# Label: "vTrus ECC Root CA" +# Serial: 630369271402956006249506845124680065938238527194 +# MD5 Fingerprint: de:4b:c1:f5:52:8c:9b:43:e1:3e:8f:55:54:17:8d:85 +# SHA1 Fingerprint: f6:9c:db:b0:fc:f6:02:13:b6:52:32:a6:a3:91:3f:16:70:da:c3:e1 +# SHA256 Fingerprint: 30:fb:ba:2c:32:23:8e:2a:98:54:7a:f9:79:31:e5:50:42:8b:9b:3f:1c:8e:eb:66:33:dc:fa:86:c5:b2:7d:d3 +-----BEGIN CERTIFICATE----- +MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMw +RzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAY +BgNVBAMTEXZUcnVzIEVDQyBSb290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDcz +MTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28u +LEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+cToL0 +v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUd +e4BdS49nTPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIw +V53dVvHH4+m4SVBrm2nDb+zDfSXkV5UTQJtS0zvzQBm8JsctBp61ezaf9SXUY2sA +AjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQLYgmRWAD5Tfs0aNoJrSEG +GJTO +-----END CERTIFICATE----- + +# Issuer: CN=vTrus Root CA O=iTrusChina Co.,Ltd. +# Subject: CN=vTrus Root CA O=iTrusChina Co.,Ltd. +# Label: "vTrus Root CA" +# Serial: 387574501246983434957692974888460947164905180485 +# MD5 Fingerprint: b8:c9:37:df:fa:6b:31:84:64:c5:ea:11:6a:1b:75:fc +# SHA1 Fingerprint: 84:1a:69:fb:f5:cd:1a:25:34:13:3d:e3:f8:fc:b8:99:d0:c9:14:b7 +# SHA256 Fingerprint: 8a:71:de:65:59:33:6f:42:6c:26:e5:38:80:d0:0d:88:a1:8d:a4:c6:a9:1f:0d:cb:61:94:e2:06:c5:c9:63:87 +-----BEGIN CERTIFICATE----- +MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQEL +BQAwQzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4x +FjAUBgNVBAMTDXZUcnVzIFJvb3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMx +MDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoGA1UEChMTaVRydXNDaGluYSBDby4s +THRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZotsSKYc +IrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykU +AyyNJJrIZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+ +GrPSbcKvdmaVayqwlHeFXgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z9 +8Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KAYPxMvDVTAWqXcoKv8R1w6Jz1717CbMdH +flqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70kLJrxLT5ZOrpGgrIDajt +J8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2AXPKBlim +0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZN +pGvu/9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQ +UqqzApVg+QxMaPnu1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHW +OXSuTEGC2/KmSNGzm/MzqvOmwMVO9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMB +AAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYgscasGrz2iTAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAKbqSSaet +8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd +nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1j +bhd47F18iMjrjld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvM +Kar5CKXiNxTKsbhm7xqC5PD48acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIiv +TDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJnxDHO2zTlJQNgJXtxmOTAGytfdELS +S8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554WgicEFOwE30z9J4nfr +I8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4sEb9 +b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNB +UvupLnKWnyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1P +Ti07NEPhmg4NpGaXutIcSkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929ven +sBxXVsFy6K2ir40zSbofitzmdHxghm+Hl3s= +-----END CERTIFICATE----- + +# Issuer: CN=ISRG Root X2 O=Internet Security Research Group +# Subject: CN=ISRG Root X2 O=Internet Security Research Group +# Label: "ISRG Root X2" +# Serial: 87493402998870891108772069816698636114 +# MD5 Fingerprint: d3:9e:c4:1e:23:3c:a6:df:cf:a3:7e:6d:e0:14:e6:e5 +# SHA1 Fingerprint: bd:b1:b9:3c:d5:97:8d:45:c6:26:14:55:f8:db:95:c7:5a:d1:53:af +# SHA256 Fingerprint: 69:72:9b:8e:15:a8:6e:fc:17:7a:57:af:b7:17:1d:fc:64:ad:d2:8c:2f:ca:8c:f1:50:7e:34:45:3c:cb:14:70 +-----BEGIN CERTIFICATE----- +MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw +CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg +R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 +MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT +ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW ++1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 +ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI +zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW +tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 +/q4AaOeMSQ+2b1tbFfLn +-----END CERTIFICATE----- + +# Issuer: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd. +# Subject: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd. +# Label: "HiPKI Root CA - G1" +# Serial: 60966262342023497858655262305426234976 +# MD5 Fingerprint: 69:45:df:16:65:4b:e8:68:9a:8f:76:5f:ff:80:9e:d3 +# SHA1 Fingerprint: 6a:92:e4:a8:ee:1b:ec:96:45:37:e3:29:57:49:cd:96:e3:e5:d2:60 +# SHA256 Fingerprint: f0:15:ce:3c:c2:39:bf:ef:06:4b:e9:f1:d2:c4:17:e1:a0:26:4a:0a:94:be:1f:0c:8d:12:18:64:eb:69:49:cc +-----BEGIN CERTIFICATE----- +MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBP +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xGzAZBgNVBAMMEkhpUEtJIFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRa +Fw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3 +YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kgUm9vdCBDQSAtIEcx +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0o9Qw +qNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twv +Vcg3Px+kwJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6 +lZgRZq2XNdZ1AYDgr/SEYYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnz +Qs7ZngyzsHeXZJzA9KMuH5UHsBffMNsAGJZMoYFL3QRtU6M9/Aes1MU3guvklQgZ +KILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfdhSi8MEyr48KxRURHH+CK +FgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj1jOXTyFj +HluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDr +y+K49a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ +/W3c1pzAtH2lsN0/Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgM +a/aOEmem8rJY5AIJEzypuxC00jBF8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6 +fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQDAgGGMA0GCSqG +SIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi +7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqc +SE5XCV0vrPSltJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6Fza +ZsT0pPBWGTMpWmWSBUdGSquEwx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9Tc +XzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07QJNBAsNB1CI69aO4I1258EHBGG3zg +iLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv5wiZqAxeJoBF1Pho +L5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+GpzjLrF +Ne85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wr +kkVbbiVghUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+ +vhV4nYWBSipX3tUZQ9rbyltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQU +YDksswBVLuT1sw5XxJFBAJw/6KXf6vb/yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Label: "GlobalSign ECC Root CA - R4" +# Serial: 159662223612894884239637590694 +# MD5 Fingerprint: 26:29:f8:6d:e1:88:bf:a2:65:7f:aa:c4:cd:0f:7f:fc +# SHA1 Fingerprint: 6b:a0:b0:98:e1:71:ef:5a:ad:fe:48:15:80:77:10:f4:bd:6f:0b:28 +# SHA256 Fingerprint: b0:85:d7:0b:96:4f:19:1a:73:e4:af:0d:54:ae:7a:0e:07:aa:fd:af:9b:71:dd:08:62:13:8a:b7:32:5a:24:a2 +-----BEGIN CERTIFICATE----- +MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYD +VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgw +MTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0g +UjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wWTAT +BgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkWymOx +uYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNV +HQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/ ++wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147 +bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R1 O=Google Trust Services LLC +# Subject: CN=GTS Root R1 O=Google Trust Services LLC +# Label: "GTS Root R1" +# Serial: 159662320309726417404178440727 +# MD5 Fingerprint: 05:fe:d0:bf:71:a8:a3:76:63:da:01:e0:d8:52:dc:40 +# SHA1 Fingerprint: e5:8c:1c:c4:91:3b:38:63:4b:e9:10:6e:e3:ad:8e:6b:9d:d9:81:4a +# SHA256 Fingerprint: d9:47:43:2a:bd:e7:b7:fa:90:fc:2e:6b:59:10:1b:12:80:e0:e1:c7:e4:e4:0f:a3:c6:88:7f:ff:57:a7:f4:cf +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo +27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w +Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw +TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl +qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH +szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8 +Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk +MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p +aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN +VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb +C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe +QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy +h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4 +7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J +ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef +MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/ +Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT +6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ +0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm +2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb +bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R2 O=Google Trust Services LLC +# Subject: CN=GTS Root R2 O=Google Trust Services LLC +# Label: "GTS Root R2" +# Serial: 159662449406622349769042896298 +# MD5 Fingerprint: 1e:39:c0:53:e6:1e:29:82:0b:ca:52:55:36:5d:57:dc +# SHA1 Fingerprint: 9a:44:49:76:32:db:de:fa:d0:bc:fb:5a:7b:17:bd:9e:56:09:24:94 +# SHA256 Fingerprint: 8d:25:cd:97:22:9d:bf:70:35:6b:da:4e:b3:cc:73:40:31:e2:4c:f0:0f:af:cf:d3:2d:c7:6e:b5:84:1c:7e:a8 +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3LvCvpt +nfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY +6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAu +MC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7k +RXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWg +f9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1mKPV ++3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K8Yzo +dDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW +Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKa +G73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCq +gc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBAB/Kzt3H +vqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8 +0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyC +B19m3H0Q/gxhswWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2u +NmSRXbBoGOqKYcl3qJfEycel/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMg +yALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVnjWQye+mew4K6Ki3pHrTgSAai/Gev +HyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y59PYjJbigapordwj6 +xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M7YNR +TOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924Sg +JPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV +7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl +6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjWHYbL +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R3 O=Google Trust Services LLC +# Subject: CN=GTS Root R3 O=Google Trust Services LLC +# Label: "GTS Root R3" +# Serial: 159662495401136852707857743206 +# MD5 Fingerprint: 3e:e7:9d:58:02:94:46:51:94:e5:e0:22:4a:8b:e7:73 +# SHA1 Fingerprint: ed:e5:71:80:2b:c8:92:b9:5b:83:3c:d2:32:68:3f:09:cd:a0:1e:46 +# SHA256 Fingerprint: 34:d8:a7:3e:e2:08:d9:bc:db:0d:95:65:20:93:4b:4e:40:e6:94:82:59:6e:8b:6f:73:c8:42:6b:01:0a:6f:48 +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout736G +jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2 +4CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7 +VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm +ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R4 O=Google Trust Services LLC +# Subject: CN=GTS Root R4 O=Google Trust Services LLC +# Label: "GTS Root R4" +# Serial: 159662532700760215368942768210 +# MD5 Fingerprint: 43:96:83:77:19:4d:76:b3:9d:65:52:e4:1d:22:a5:e8 +# SHA1 Fingerprint: 77:d3:03:67:b5:e0:0c:15:f6:0c:38:61:df:7c:e1:3b:92:46:4d:47 +# SHA256 Fingerprint: 34:9d:fa:40:58:c5:e2:63:12:3b:39:8a:e7:95:57:3c:4e:13:13:c8:3f:e6:8f:93:55:6c:d5:e8:03:1b:3c:7d +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi +QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR +HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D +9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8 +p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD +-----END CERTIFICATE----- + +# Issuer: CN=Telia Root CA v2 O=Telia Finland Oyj +# Subject: CN=Telia Root CA v2 O=Telia Finland Oyj +# Label: "Telia Root CA v2" +# Serial: 7288924052977061235122729490515358 +# MD5 Fingerprint: 0e:8f:ac:aa:82:df:85:b1:f4:dc:10:1c:fc:99:d9:48 +# SHA1 Fingerprint: b9:99:cd:d1:73:50:8a:c4:47:05:08:9c:8c:88:fb:be:a0:2b:40:cd +# SHA256 Fingerprint: 24:2b:69:74:2f:cb:1e:5b:2a:bf:98:89:8b:94:57:21:87:54:4e:5b:4d:99:11:78:65:73:62:1f:6a:74:b8:2c +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQx +CzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UE +AwwQVGVsaWEgUm9vdCBDQSB2MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1 +NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZ +MBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ76zBq +AMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9 +vVYiQJ3q9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9 +lRdU2HhE8Qx3FZLgmEKnpNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTOD +n3WhUidhOPFZPY5Q4L15POdslv5e2QJltI5c0BE0312/UqeBAMN/mUWZFdUXyApT +7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW5olWK8jjfN7j/4nlNW4o +6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNrRBH0pUPC +TEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6 +WT0EBXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63R +DolUK5X6wK0dmBR4M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZI +pEYslOqodmJHixBTB0hXbOKSTbauBcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGj +YzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7Wxy+G2CQ5MB0GA1UdDgQWBBRy +rOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ +8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi +0f6X+J8wfBj5tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMM +A8iZGok1GTzTyVR8qPAs5m4HeW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBS +SRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+Cy748fdHif64W1lZYudogsYMVoe+K +TTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygCQMez2P2ccGrGKMOF +6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15h2Er +3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMt +Ty3EHD70sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pT +VmBds9hCG1xLEooc6+t9xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAW +ysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQraVplI/owd8k+BsHMYeB2F326CjYSlKA +rBPuUBQemMc= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH +# Subject: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH +# Label: "D-TRUST BR Root CA 1 2020" +# Serial: 165870826978392376648679885835942448534 +# MD5 Fingerprint: b5:aa:4b:d5:ed:f7:e3:55:2e:8f:72:0a:f3:75:b8:ed +# SHA1 Fingerprint: 1f:5b:98:f0:e3:b5:f7:74:3c:ed:e6:b0:36:7d:32:cd:f4:09:41:67 +# SHA256 Fingerprint: e5:9a:aa:81:60:09:c2:2b:ff:5b:25:ba:d3:7d:f3:06:f0:49:79:7c:1f:81:d8:5a:b0:89:e6:57:bd:8f:00:44 +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQsw +CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS +VVNUIEJSIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5 +NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG +A1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB +BAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7dPYS +zuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0 +QVK5buXuQqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/ +VbNafAkl1bK6CKBrqx9tMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g +PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2JyX3Jvb3Rf +Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 +c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO +PQQDAwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFW +wKrY7RjEsK70PvomAjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHV +dWNbFJWcHwHP2NVypw87 +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH +# Subject: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH +# Label: "D-TRUST EV Root CA 1 2020" +# Serial: 126288379621884218666039612629459926992 +# MD5 Fingerprint: 8c:2d:9d:70:9f:48:99:11:06:11:fb:e9:cb:30:c0:6e +# SHA1 Fingerprint: 61:db:8c:21:59:69:03:90:d8:7c:9c:12:86:54:cf:9d:3d:f4:dd:07 +# SHA256 Fingerprint: 08:17:0d:1a:a3:64:53:90:1a:2f:95:92:45:e3:47:db:0c:8d:37:ab:aa:bc:56:b8:1a:a1:00:dc:95:89:70:db +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQsw +CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS +VVNUIEVWIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5 +NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG +A1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB +BAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8ZRCC +/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rD +wpdhQntJraOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3 +OqQo5FD4pPfsazK2/umLMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g +PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2V2X3Jvb3Rf +Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 +c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO +PQQDAwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CA +y/m0sRtW9XLS/BnRAjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJb +gfM0agPnIjhQW+0ZT0MW +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc. +# Subject: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc. +# Label: "DigiCert TLS ECC P384 Root G5" +# Serial: 13129116028163249804115411775095713523 +# MD5 Fingerprint: d3:71:04:6a:43:1c:db:a6:59:e1:a8:a3:aa:c5:71:ed +# SHA1 Fingerprint: 17:f3:de:5e:9f:0f:19:e9:8e:f6:1f:32:26:6e:20:c4:07:ae:30:ee +# SHA256 Fingerprint: 01:8e:13:f0:77:25:32:cf:80:9b:d1:b1:72:81:86:72:83:fc:48:c6:e1:3b:e9:c6:98:12:85:4a:49:0c:1b:05 +-----BEGIN CERTIFICATE----- +MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp +Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2 +MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ +bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS +7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp +0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS +B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49 +BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ +LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4 +DXZDjC5Ty3zfDBeWUA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc. +# Subject: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc. +# Label: "DigiCert TLS RSA4096 Root G5" +# Serial: 11930366277458970227240571539258396554 +# MD5 Fingerprint: ac:fe:f7:34:96:a9:f2:b3:b4:12:4b:e4:27:41:6f:e1 +# SHA1 Fingerprint: a7:88:49:dc:5d:7c:75:8c:8c:de:39:98:56:b3:aa:d0:b2:a5:71:35 +# SHA256 Fingerprint: 37:1a:00:dc:05:33:b3:72:1a:7e:eb:40:e8:41:9e:70:79:9d:2b:0a:0f:2c:1d:80:69:31:65:f7:ce:c4:ad:75 +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT +HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN +NDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs +IEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS87IE+ +ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG0 +2C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgp +wgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM +pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RD +nU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6Po +sMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDx +Zre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cd +Lvvyz6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvX +KyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNe +XoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPL +tgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfTJc1paPKv +TiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN +AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw +GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7H +PNtQOa27PShNlnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLF +O4uJ+DQtpBflF+aZfTCIITfNMBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQ +REtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/u4cnYiWB39yhL/btp/96j1EuMPik +AdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9GOUrYU9DzLjtxpdRv +/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh47a+ +p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilw +MUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF +qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCK +ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+ +-----END CERTIFICATE----- + +# Issuer: CN=Certainly Root R1 O=Certainly +# Subject: CN=Certainly Root R1 O=Certainly +# Label: "Certainly Root R1" +# Serial: 188833316161142517227353805653483829216 +# MD5 Fingerprint: 07:70:d4:3e:82:87:a0:fa:33:36:13:f4:fa:33:e7:12 +# SHA1 Fingerprint: a0:50:ee:0f:28:71:f4:27:b2:12:6d:6f:50:96:25:ba:cc:86:42:af +# SHA256 Fingerprint: 77:b8:2c:d8:64:4c:43:05:f7:ac:c5:cb:15:6b:45:67:50:04:03:3d:51:c6:0c:62:02:a8:e0:c3:34:67:d3:a0 +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAw +PTELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2Vy +dGFpbmx5IFJvb3QgUjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9 +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0 +YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANA2 +1B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O5MQT +vqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbed +aFySpvXl8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b0 +1C7jcvk2xusVtyWMOvwlDbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5 +r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGIXsXwClTNSaa/ApzSRKft43jvRl5tcdF5 +cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkNKPl6I7ENPT2a/Z2B7yyQ +wHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQAjeZjOVJ +6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA +2CnbrlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyH +Wyf5QBGenDPBt+U1VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMR +eiFPCyEQtkA6qyI6BJyLm4SGcprSp6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB +/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTgqj8ljZ9EXME66C6u +d0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAszHQNTVfSVcOQr +PbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d +8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi +1wrykXprOQ4vMMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrd +rRT90+7iIgXr0PK3aBLXWopBGsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9di +taY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+gjwN/KUD+nsa2UUeYNrEjvn8K8l7 +lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgHJBu6haEaBQmAupVj +yTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7fpYn +Kx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLy +yCwzk5Iwx06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5n +wXARPbv0+Em34yaXOp/SX3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6 +OV+KmalBWQewLK8= +-----END CERTIFICATE----- + +# Issuer: CN=Certainly Root E1 O=Certainly +# Subject: CN=Certainly Root E1 O=Certainly +# Label: "Certainly Root E1" +# Serial: 8168531406727139161245376702891150584 +# MD5 Fingerprint: 0a:9e:ca:cd:3e:52:50:c6:36:f3:4b:a3:ed:a7:53:e9 +# SHA1 Fingerprint: f9:e1:6d:dc:01:89:cf:d5:82:45:63:3e:c5:37:7d:c2:eb:93:6f:2b +# SHA256 Fingerprint: b4:58:5f:22:e4:ac:75:6a:4e:86:12:a1:36:1c:5d:9d:03:1a:93:fd:84:fe:bb:77:8f:a3:06:8b:0f:c4:2d:c2 +-----BEGIN CERTIFICATE----- +MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQsw +CQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlu +bHkgUm9vdCBFMTAeFw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJ +BgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlubHkxGjAYBgNVBAMTEUNlcnRhaW5s +eSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4fxzf7flHh4axpMCK ++IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9YBk2 +QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4 +hevIIgcwCgYIKoZIzj0EAwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozm +ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOG +BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR +-----END CERTIFICATE----- + +# Issuer: CN=E-Tugra Global Root CA RSA v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center +# Subject: CN=E-Tugra Global Root CA RSA v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center +# Label: "E-Tugra Global Root CA RSA v3" +# Serial: 75951268308633135324246244059508261641472512052 +# MD5 Fingerprint: 22:be:10:f6:c2:f8:03:88:73:5f:33:29:47:28:47:a4 +# SHA1 Fingerprint: e9:a8:5d:22:14:52:1c:5b:aa:0a:b4:be:24:6a:23:8a:c9:ba:e2:a9 +# SHA256 Fingerprint: ef:66:b0:b1:0a:3c:db:9f:2e:36:48:c7:6b:d2:af:18:ea:d2:bf:e6:f1:17:65:5e:28:c4:06:0d:a1:a3:f4:c2 +-----BEGIN CERTIFICATE----- +MIIF8zCCA9ugAwIBAgIUDU3FzRYilZYIfrgLfxUGNPt5EDQwDQYJKoZIhvcNAQEL +BQAwgYAxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUt +VHVncmEgRUJHIEEuUy4xHTAbBgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYw +JAYDVQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENBIFJTQSB2MzAeFw0yMDAzMTgw +OTA3MTdaFw00NTAzMTIwOTA3MTdaMIGAMQswCQYDVQQGEwJUUjEPMA0GA1UEBxMG +QW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1 +Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBD +QSBSU0EgdjMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCiZvCJt3J7 +7gnJY9LTQ91ew6aEOErxjYG7FL1H6EAX8z3DeEVypi6Q3po61CBxyryfHUuXCscx +uj7X/iWpKo429NEvx7epXTPcMHD4QGxLsqYxYdE0PD0xesevxKenhOGXpOhL9hd8 +7jwH7eKKV9y2+/hDJVDqJ4GohryPUkqWOmAalrv9c/SF/YP9f4RtNGx/ardLAQO/ +rWm31zLZ9Vdq6YaCPqVmMbMWPcLzJmAy01IesGykNz709a/r4d+ABs8qQedmCeFL +l+d3vSFtKbZnwy1+7dZ5ZdHPOrbRsV5WYVB6Ws5OUDGAA5hH5+QYfERaxqSzO8bG +wzrwbMOLyKSRBfP12baqBqG3q+Sx6iEUXIOk/P+2UNOMEiaZdnDpwA+mdPy70Bt4 +znKS4iicvObpCdg604nmvi533wEKb5b25Y08TVJ2Glbhc34XrD2tbKNSEhhw5oBO +M/J+JjKsBY04pOZ2PJ8QaQ5tndLBeSBrW88zjdGUdjXnXVXHt6woq0bM5zshtQoK +5EpZ3IE1S0SVEgpnpaH/WwAH0sDM+T/8nzPyAPiMbIedBi3x7+PmBvrFZhNb/FAH +nnGGstpvdDDPk1Po3CLW3iAfYY2jLqN4MpBs3KwytQXk9TwzDdbgh3cXTJ2w2Amo +DVf3RIXwyAS+XF1a4xeOVGNpf0l0ZAWMowIDAQABo2MwYTAPBgNVHRMBAf8EBTAD +AQH/MB8GA1UdIwQYMBaAFLK0ruYt9ybVqnUtdkvAG1Mh0EjvMB0GA1UdDgQWBBSy +tK7mLfcm1ap1LXZLwBtTIdBI7zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEL +BQADggIBAImocn+M684uGMQQgC0QDP/7FM0E4BQ8Tpr7nym/Ip5XuYJzEmMmtcyQ +6dIqKe6cLcwsmb5FJ+Sxce3kOJUxQfJ9emN438o2Fi+CiJ+8EUdPdk3ILY7r3y18 +Tjvarvbj2l0Upq7ohUSdBm6O++96SmotKygY/r+QLHUWnw/qln0F7psTpURs+APQ +3SPh/QMSEgj0GDSz4DcLdxEBSL9htLX4GdnLTeqjjO/98Aa1bZL0SmFQhO3sSdPk +vmjmLuMxC1QLGpLWgti2omU8ZgT5Vdps+9u1FGZNlIM7zR6mK7L+d0CGq+ffCsn9 +9t2HVhjYsCxVYJb6CH5SkPVLpi6HfMsg2wY+oF0Dd32iPBMbKaITVaA9FCKvb7jQ +mhty3QUBjYZgv6Rn7rWlDdF/5horYmbDB7rnoEgcOMPpRfunf/ztAmgayncSd6YA +VSgU7NbHEqIbZULpkejLPoeJVF3Zr52XnGnnCv8PWniLYypMfUeUP95L6VPQMPHF +9p5J3zugkaOj/s1YzOrfr28oO6Bpm4/srK4rVJ2bBLFHIK+WEj5jlB0E5y67hscM +moi/dkfv97ALl2bSRM9gUgfh1SxKOidhd8rXj+eHDjD/DLsE4mHDosiXYY60MGo8 +bcIHX0pzLz/5FooBZu+6kcpSV3uu1OYP3Qt6f4ueJiDPO++BcYNZ +-----END CERTIFICATE----- + +# Issuer: CN=E-Tugra Global Root CA ECC v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center +# Subject: CN=E-Tugra Global Root CA ECC v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center +# Label: "E-Tugra Global Root CA ECC v3" +# Serial: 218504919822255052842371958738296604628416471745 +# MD5 Fingerprint: 46:bc:81:bb:f1:b5:1e:f7:4b:96:bc:14:e2:e7:27:64 +# SHA1 Fingerprint: 8a:2f:af:57:53:b1:b0:e6:a1:04:ec:5b:6a:69:71:6d:f6:1c:e2:84 +# SHA256 Fingerprint: 87:3f:46:85:fa:7f:56:36:25:25:2e:6d:36:bc:d7:f1:6f:c2:49:51:f2:64:e4:7e:1b:95:4f:49:08:cd:ca:13 +-----BEGIN CERTIFICATE----- +MIICpTCCAiqgAwIBAgIUJkYZdzHhT28oNt45UYbm1JeIIsEwCgYIKoZIzj0EAwMw +gYAxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVn +cmEgRUJHIEEuUy4xHTAbBgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYD +VQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENBIEVDQyB2MzAeFw0yMDAzMTgwOTQ2 +NThaFw00NTAzMTIwOTQ2NThaMIGAMQswCQYDVQQGEwJUUjEPMA0GA1UEBxMGQW5r +YXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1Z3Jh +IFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBF +Q0MgdjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASOmCm/xxAeJ9urA8woLNheSBkQ +KczLWYHMjLiSF4mDKpL2w6QdTGLVn9agRtwcvHbB40fQWxPa56WzZkjnIZpKT4YK +fWzqTTKACrJ6CZtpS5iB4i7sAnCWH/31Rs7K3IKjYzBhMA8GA1UdEwEB/wQFMAMB +Af8wHwYDVR0jBBgwFoAU/4Ixcj75xGZsrTie0bBRiKWQzPUwHQYDVR0OBBYEFP+C +MXI++cRmbK04ntGwUYilkMz1MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNp +ADBmAjEA5gVYaWHlLcoNy/EZCL3W/VGSGn5jVASQkZo1kTmZ+gepZpO6yGjUij/6 +7W4WAie3AjEA3VoXK3YdZUKWpqxdinlW2Iob35reX8dQj7FbcQwm32pAAOwzkSFx +vmjkI6TZraE3 +-----END CERTIFICATE----- + +# Issuer: CN=Security Communication RootCA3 O=SECOM Trust Systems CO.,LTD. +# Subject: CN=Security Communication RootCA3 O=SECOM Trust Systems CO.,LTD. +# Label: "Security Communication RootCA3" +# Serial: 16247922307909811815 +# MD5 Fingerprint: 1c:9a:16:ff:9e:5c:e0:4d:8a:14:01:f4:35:5d:29:26 +# SHA1 Fingerprint: c3:03:c8:22:74:92:e5:61:a2:9c:5f:79:91:2b:1e:44:13:91:30:3a +# SHA256 Fingerprint: 24:a5:5c:2a:b0:51:44:2d:06:17:76:65:41:23:9a:4a:d0:32:d7:c5:51:75:aa:34:ff:de:2f:bc:4f:5c:52:94 +-----BEGIN CERTIFICATE----- +MIIFfzCCA2egAwIBAgIJAOF8N0D9G/5nMA0GCSqGSIb3DQEBDAUAMF0xCzAJBgNV +BAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScw +JQYDVQQDEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTMwHhcNMTYwNjE2 +MDYxNzE2WhcNMzgwMTE4MDYxNzE2WjBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UEAxMeU2VjdXJpdHkg +Q29tbXVuaWNhdGlvbiBSb290Q0EzMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEA48lySfcw3gl8qUCBWNO0Ot26YQ+TUG5pPDXC7ltzkBtnTCHsXzW7OT4r +CmDvu20rhvtxosis5FaU+cmvsXLUIKx00rgVrVH+hXShuRD+BYD5UpOzQD11EKzA +lrenfna84xtSGc4RHwsENPXY9Wk8d/Nk9A2qhd7gCVAEF5aEt8iKvE1y/By7z/MG +TfmfZPd+pmaGNXHIEYBMwXFAWB6+oHP2/D5Q4eAvJj1+XCO1eXDe+uDRpdYMQXF7 +9+qMHIjH7Iv10S9VlkZ8WjtYO/u62C21Jdp6Ts9EriGmnpjKIG58u4iFW/vAEGK7 +8vknR+/RiTlDxN/e4UG/VHMgly1s2vPUB6PmudhvrvyMGS7TZ2crldtYXLVqAvO4 +g160a75BflcJdURQVc1aEWEhCmHCqYj9E7wtiS/NYeCVvsq1e+F7NGcLH7YMx3we +GVPKp7FKFSBWFHA9K4IsD50VHUeAR/94mQ4xr28+j+2GaR57GIgUssL8gjMunEst ++3A7caoreyYn8xrC3PsXuKHqy6C0rtOUfnrQq8PsOC0RLoi/1D+tEjtCrI8Cbn3M +0V9hvqG8OmpI6iZVIhZdXw3/JzOfGAN0iltSIEdrRU0id4xVJ/CvHozJgyJUt5rQ +T9nO/NkuHJYosQLTA70lUhw0Zk8jq/R3gpYd0VcwCBEF/VfR2ccCAwEAAaNCMEAw +HQYDVR0OBBYEFGQUfPxYchamCik0FW8qy7z8r6irMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDAUAA4ICAQDcAiMI4u8hOscNtybS +YpOnpSNyByCCYN8Y11StaSWSntkUz5m5UoHPrmyKO1o5yGwBQ8IibQLwYs1OY0PA +FNr0Y/Dq9HHuTofjcan0yVflLl8cebsjqodEV+m9NU1Bu0soo5iyG9kLFwfl9+qd +9XbXv8S2gVj/yP9kaWJ5rW4OH3/uHWnlt3Jxs/6lATWUVCvAUm2PVcTJ0rjLyjQI +UYWg9by0F1jqClx6vWPGOi//lkkZhOpn2ASxYfQAW0q3nHE3GYV5v4GwxxMOdnE+ +OoAGrgYWp421wsTL/0ClXI2lyTrtcoHKXJg80jQDdwj98ClZXSEIx2C/pHF7uNke +gr4Jr2VvKKu/S7XuPghHJ6APbw+LP6yVGPO5DtxnVW5inkYO0QR4ynKudtml+LLf +iAlhi+8kTtFZP1rUPcmTPCtk9YENFpb3ksP+MW/oKjJ0DvRMmEoYDjBU1cXrvMUV +nuiZIesnKwkK2/HmcBhWuwzkvvnoEKQTkrgc4NtnHVMDpCKn3F2SEDzq//wbEBrD +2NCcnWXL0CsnMQMeNuE9dnUM/0Umud1RvCPHX9jYhxBAEg09ODfnRDwYwFMJZI// +1ZqmfHAuc1Uh6N//g7kdPjIe1qZ9LPFm6Vwdp6POXiUyK+OVrCoHzrQoeIY8Laad +TdJ0MN1kURXbg4NR16/9M51NZg== +-----END CERTIFICATE----- + +# Issuer: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD. +# Subject: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD. +# Label: "Security Communication ECC RootCA1" +# Serial: 15446673492073852651 +# MD5 Fingerprint: 7e:43:b0:92:68:ec:05:43:4c:98:ab:5d:35:2e:7e:86 +# SHA1 Fingerprint: b8:0e:26:a9:bf:d2:b2:3b:c0:ef:46:c9:ba:c7:bb:f6:1d:0d:41:41 +# SHA256 Fingerprint: e7:4f:bd:a5:5b:d5:64:c4:73:a3:6b:44:1a:a7:99:c8:a6:8e:07:74:40:e8:28:8b:9f:a1:e5:0e:4b:ba:ca:11 +-----BEGIN CERTIFICATE----- +MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYT +AkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYD +VQQDEyJTZWN1cml0eSBDb21tdW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYx +NjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTELMAkGA1UEBhMCSlAxJTAjBgNVBAoT +HFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNVBAMTIlNlY3VyaXR5 +IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+Cnnfdl +dB9sELLo5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpK +ULGjQjBAMB0GA1UdDgQWBBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu +9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0O +be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k= +-----END CERTIFICATE----- + +# Issuer: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY +# Subject: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY +# Label: "BJCA Global Root CA1" +# Serial: 113562791157148395269083148143378328608 +# MD5 Fingerprint: 42:32:99:76:43:33:36:24:35:07:82:9b:28:f9:d0:90 +# SHA1 Fingerprint: d5:ec:8d:7b:4c:ba:79:f4:e7:e8:cb:9d:6b:ae:77:83:10:03:21:6a +# SHA256 Fingerprint: f3:89:6f:88:fe:7c:0a:88:27:66:a7:fa:6a:d2:74:9f:b5:7a:7f:3e:98:fb:76:9c:1f:a7:b0:9c:2c:44:d5:ae +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIQVW9l47TZkGobCdFsPsBsIDANBgkqhkiG9w0BAQsFADBU +MQswCQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRI +T1JJVFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0ExMB4XDTE5MTIxOTAz +MTYxN1oXDTQ0MTIxMjAzMTYxN1owVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJF +SUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2Jh +bCBSb290IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPFmCL3Z +xRVhy4QEQaVpN3cdwbB7+sN3SJATcmTRuHyQNZ0YeYjjlwE8R4HyDqKYDZ4/N+AZ +spDyRhySsTphzvq3Rp4Dhtczbu33RYx2N95ulpH3134rhxfVizXuhJFyV9xgw8O5 +58dnJCNPYwpj9mZ9S1WnP3hkSWkSl+BMDdMJoDIwOvqfwPKcxRIqLhy1BDPapDgR +at7GGPZHOiJBhyL8xIkoVNiMpTAK+BcWyqw3/XmnkRd4OJmtWO2y3syJfQOcs4ll +5+M7sSKGjwZteAf9kRJ/sGsciQ35uMt0WwfCyPQ10WRjeulumijWML3mG90Vr4Tq +nMfK9Q7q8l0ph49pczm+LiRvRSGsxdRpJQaDrXpIhRMsDQa4bHlW/KNnMoH1V6XK +V0Jp6VwkYe/iMBhORJhVb3rCk9gZtt58R4oRTklH2yiUAguUSiz5EtBP6DF+bHq/ +pj+bOT0CFqMYs2esWz8sgytnOYFcuX6U1WTdno9uruh8W7TXakdI136z1C2OVnZO +z2nxbkRs1CTqjSShGL+9V/6pmTW12xB3uD1IutbB5/EjPtffhZ0nPNRAvQoMvfXn +jSXWgXSHRtQpdaJCbPdzied9v3pKH9MiyRVVz99vfFXQpIsHETdfg6YmV6YBW37+ +WGgHqel62bno/1Afq8K0wM7o6v0PvY1NuLxxAgMBAAGjQjBAMB0GA1UdDgQWBBTF +7+3M2I0hxkjk49cULqcWk+WYATAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAUoKsITQfI/Ki2Pm4rzc2IInRNwPWaZ+4 +YRC6ojGYWUfo0Q0lHhVBDOAqVdVXUsv45Mdpox1NcQJeXyFFYEhcCY5JEMEE3Kli +awLwQ8hOnThJdMkycFRtwUf8jrQ2ntScvd0g1lPJGKm1Vrl2i5VnZu69mP6u775u ++2D2/VnGKhs/I0qUJDAnyIm860Qkmss9vk/Ves6OF8tiwdneHg56/0OGNFK8YT88 +X7vZdrRTvJez/opMEi4r89fO4aL/3Xtw+zuhTaRjAv04l5U/BXCga99igUOLtFkN +SoxUnMW7gZ/NfaXvCyUeOiDbHPwfmGcCCtRzRBPbUYQaVQNW4AB+dAb/OMRyHdOo +P2gxXdMJxy6MW2Pg6Nwe0uxhHvLe5e/2mXZgLR6UcnHGCyoyx5JO1UbXHfmpGQrI ++pXObSOYqgs4rZpWDW+N8TEAiMEXnM0ZNjX+VVOg4DwzX5Ze4jLp3zO7Bkqp2IRz +znfSxqxx4VyjHQy7Ct9f4qNx2No3WqB4K/TUfet27fJhcKVlmtOJNBir+3I+17Q9 +eVzYH6Eze9mCUAyTF6ps3MKCuwJXNq+YJyo5UOGwifUll35HaBC07HPKs5fRJNz2 +YqAo07WjuGS3iGJCz51TzZm+ZGiPTx4SSPfSKcOYKMryMguTjClPPGAyzQWWYezy +r/6zcCwupvI= +-----END CERTIFICATE----- + +# Issuer: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY +# Subject: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY +# Label: "BJCA Global Root CA2" +# Serial: 58605626836079930195615843123109055211 +# MD5 Fingerprint: 5e:0a:f6:47:5f:a6:14:e8:11:01:95:3f:4d:01:eb:3c +# SHA1 Fingerprint: f4:27:86:eb:6e:b8:6d:88:31:67:02:fb:ba:66:a4:53:00:aa:7a:a6 +# SHA256 Fingerprint: 57:4d:f6:93:1e:27:80:39:66:7b:72:0a:fd:c1:60:0f:c2:7e:b6:6d:d3:09:29:79:fb:73:85:64:87:21:28:82 +-----BEGIN CERTIFICATE----- +MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQsw +CQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJ +VFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgy +MVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJ +TkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2JhbCBS +b290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jlSR9B +IgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK+ ++kpRuDCK/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJK +sVF/BvDRgh9Obl+rg/xI1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA +94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8gUXOQwKhbYdDFUDn9hf7B +43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w== +-----END CERTIFICATE----- diff --git a/virt/lib/python3.9/site-packages/pip/_vendor/chardet/gb2312freq 3.py b/virt/lib/python3.9/site-packages/pip/_vendor/chardet/gb2312freq 3.py new file mode 100644 index 00000000..3e0b5080 --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_vendor/chardet/gb2312freq 3.py @@ -0,0 +1,284 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# GB2312 most frequently used character table +# +# Char to FreqOrder table , from hz6763 + +# 512 --> 0.79 -- 0.79 +# 1024 --> 0.92 -- 0.13 +# 2048 --> 0.98 -- 0.06 +# 6768 --> 1.00 -- 0.02 +# +# Ideal Distribution Ratio = 0.79135/(1-0.79135) = 3.79 +# Random Distribution Ration = 512 / (3755 - 512) = 0.157 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher that RDR + +GB2312_TYPICAL_DISTRIBUTION_RATIO = 0.9 + +GB2312_TABLE_SIZE = 3760 + +# fmt: off +GB2312_CHAR_TO_FREQ_ORDER = ( +1671, 749,1443,2364,3924,3807,2330,3921,1704,3463,2691,1511,1515, 572,3191,2205, +2361, 224,2558, 479,1711, 963,3162, 440,4060,1905,2966,2947,3580,2647,3961,3842, +2204, 869,4207, 970,2678,5626,2944,2956,1479,4048, 514,3595, 588,1346,2820,3409, + 249,4088,1746,1873,2047,1774, 581,1813, 358,1174,3590,1014,1561,4844,2245, 670, +1636,3112, 889,1286, 953, 556,2327,3060,1290,3141, 613, 185,3477,1367, 850,3820, +1715,2428,2642,2303,2732,3041,2562,2648,3566,3946,1349, 388,3098,2091,1360,3585, + 152,1687,1539, 738,1559, 59,1232,2925,2267,1388,1249,1741,1679,2960, 151,1566, +1125,1352,4271, 924,4296, 385,3166,4459, 310,1245,2850, 70,3285,2729,3534,3575, +2398,3298,3466,1960,2265, 217,3647, 864,1909,2084,4401,2773,1010,3269,5152, 853, +3051,3121,1244,4251,1895, 364,1499,1540,2313,1180,3655,2268, 562, 715,2417,3061, + 544, 336,3768,2380,1752,4075, 950, 280,2425,4382, 183,2759,3272, 333,4297,2155, +1688,2356,1444,1039,4540, 736,1177,3349,2443,2368,2144,2225, 565, 196,1482,3406, + 927,1335,4147, 692, 878,1311,1653,3911,3622,1378,4200,1840,2969,3149,2126,1816, +2534,1546,2393,2760, 737,2494, 13, 447, 245,2747, 38,2765,2129,2589,1079, 606, + 360, 471,3755,2890, 404, 848, 699,1785,1236, 370,2221,1023,3746,2074,2026,2023, +2388,1581,2119, 812,1141,3091,2536,1519, 804,2053, 406,1596,1090, 784, 548,4414, +1806,2264,2936,1100, 343,4114,5096, 622,3358, 743,3668,1510,1626,5020,3567,2513, +3195,4115,5627,2489,2991, 24,2065,2697,1087,2719, 48,1634, 315, 68, 985,2052, + 198,2239,1347,1107,1439, 597,2366,2172, 871,3307, 919,2487,2790,1867, 236,2570, +1413,3794, 906,3365,3381,1701,1982,1818,1524,2924,1205, 616,2586,2072,2004, 575, + 253,3099, 32,1365,1182, 197,1714,2454,1201, 554,3388,3224,2748, 756,2587, 250, +2567,1507,1517,3529,1922,2761,2337,3416,1961,1677,2452,2238,3153, 615, 911,1506, +1474,2495,1265,1906,2749,3756,3280,2161, 898,2714,1759,3450,2243,2444, 563, 26, +3286,2266,3769,3344,2707,3677, 611,1402, 531,1028,2871,4548,1375, 261,2948, 835, +1190,4134, 353, 840,2684,1900,3082,1435,2109,1207,1674, 329,1872,2781,4055,2686, +2104, 608,3318,2423,2957,2768,1108,3739,3512,3271,3985,2203,1771,3520,1418,2054, +1681,1153, 225,1627,2929, 162,2050,2511,3687,1954, 124,1859,2431,1684,3032,2894, + 585,4805,3969,2869,2704,2088,2032,2095,3656,2635,4362,2209, 256, 518,2042,2105, +3777,3657, 643,2298,1148,1779, 190, 989,3544, 414, 11,2135,2063,2979,1471, 403, +3678, 126, 770,1563, 671,2499,3216,2877, 600,1179, 307,2805,4937,1268,1297,2694, + 252,4032,1448,1494,1331,1394, 127,2256, 222,1647,1035,1481,3056,1915,1048, 873, +3651, 210, 33,1608,2516, 200,1520, 415, 102, 0,3389,1287, 817, 91,3299,2940, + 836,1814, 549,2197,1396,1669,2987,3582,2297,2848,4528,1070, 687, 20,1819, 121, +1552,1364,1461,1968,2617,3540,2824,2083, 177, 948,4938,2291, 110,4549,2066, 648, +3359,1755,2110,2114,4642,4845,1693,3937,3308,1257,1869,2123, 208,1804,3159,2992, +2531,2549,3361,2418,1350,2347,2800,2568,1291,2036,2680, 72, 842,1990, 212,1233, +1154,1586, 75,2027,3410,4900,1823,1337,2710,2676, 728,2810,1522,3026,4995, 157, + 755,1050,4022, 710, 785,1936,2194,2085,1406,2777,2400, 150,1250,4049,1206, 807, +1910, 534, 529,3309,1721,1660, 274, 39,2827, 661,2670,1578, 925,3248,3815,1094, +4278,4901,4252, 41,1150,3747,2572,2227,4501,3658,4902,3813,3357,3617,2884,2258, + 887, 538,4187,3199,1294,2439,3042,2329,2343,2497,1255, 107, 543,1527, 521,3478, +3568, 194,5062, 15, 961,3870,1241,1192,2664, 66,5215,3260,2111,1295,1127,2152, +3805,4135, 901,1164,1976, 398,1278, 530,1460, 748, 904,1054,1966,1426, 53,2909, + 509, 523,2279,1534, 536,1019, 239,1685, 460,2353, 673,1065,2401,3600,4298,2272, +1272,2363, 284,1753,3679,4064,1695, 81, 815,2677,2757,2731,1386, 859, 500,4221, +2190,2566, 757,1006,2519,2068,1166,1455, 337,2654,3203,1863,1682,1914,3025,1252, +1409,1366, 847, 714,2834,2038,3209, 964,2970,1901, 885,2553,1078,1756,3049, 301, +1572,3326, 688,2130,1996,2429,1805,1648,2930,3421,2750,3652,3088, 262,1158,1254, + 389,1641,1812, 526,1719, 923,2073,1073,1902, 468, 489,4625,1140, 857,2375,3070, +3319,2863, 380, 116,1328,2693,1161,2244, 273,1212,1884,2769,3011,1775,1142, 461, +3066,1200,2147,2212, 790, 702,2695,4222,1601,1058, 434,2338,5153,3640, 67,2360, +4099,2502, 618,3472,1329, 416,1132, 830,2782,1807,2653,3211,3510,1662, 192,2124, + 296,3979,1739,1611,3684, 23, 118, 324, 446,1239,1225, 293,2520,3814,3795,2535, +3116, 17,1074, 467,2692,2201, 387,2922, 45,1326,3055,1645,3659,2817, 958, 243, +1903,2320,1339,2825,1784,3289, 356, 576, 865,2315,2381,3377,3916,1088,3122,1713, +1655, 935, 628,4689,1034,1327, 441, 800, 720, 894,1979,2183,1528,5289,2702,1071, +4046,3572,2399,1571,3281, 79, 761,1103, 327, 134, 758,1899,1371,1615, 879, 442, + 215,2605,2579, 173,2048,2485,1057,2975,3317,1097,2253,3801,4263,1403,1650,2946, + 814,4968,3487,1548,2644,1567,1285, 2, 295,2636, 97, 946,3576, 832, 141,4257, +3273, 760,3821,3521,3156,2607, 949,1024,1733,1516,1803,1920,2125,2283,2665,3180, +1501,2064,3560,2171,1592, 803,3518,1416, 732,3897,4258,1363,1362,2458, 119,1427, + 602,1525,2608,1605,1639,3175, 694,3064, 10, 465, 76,2000,4846,4208, 444,3781, +1619,3353,2206,1273,3796, 740,2483, 320,1723,2377,3660,2619,1359,1137,1762,1724, +2345,2842,1850,1862, 912, 821,1866, 612,2625,1735,2573,3369,1093, 844, 89, 937, + 930,1424,3564,2413,2972,1004,3046,3019,2011, 711,3171,1452,4178, 428, 801,1943, + 432, 445,2811, 206,4136,1472, 730, 349, 73, 397,2802,2547, 998,1637,1167, 789, + 396,3217, 154,1218, 716,1120,1780,2819,4826,1931,3334,3762,2139,1215,2627, 552, +3664,3628,3232,1405,2383,3111,1356,2652,3577,3320,3101,1703, 640,1045,1370,1246, +4996, 371,1575,2436,1621,2210, 984,4033,1734,2638, 16,4529, 663,2755,3255,1451, +3917,2257,1253,1955,2234,1263,2951, 214,1229, 617, 485, 359,1831,1969, 473,2310, + 750,2058, 165, 80,2864,2419, 361,4344,2416,2479,1134, 796,3726,1266,2943, 860, +2715, 938, 390,2734,1313,1384, 248, 202, 877,1064,2854, 522,3907, 279,1602, 297, +2357, 395,3740, 137,2075, 944,4089,2584,1267,3802, 62,1533,2285, 178, 176, 780, +2440, 201,3707, 590, 478,1560,4354,2117,1075, 30, 74,4643,4004,1635,1441,2745, + 776,2596, 238,1077,1692,1912,2844, 605, 499,1742,3947, 241,3053, 980,1749, 936, +2640,4511,2582, 515,1543,2162,5322,2892,2993, 890,2148,1924, 665,1827,3581,1032, + 968,3163, 339,1044,1896, 270, 583,1791,1720,4367,1194,3488,3669, 43,2523,1657, + 163,2167, 290,1209,1622,3378, 550, 634,2508,2510, 695,2634,2384,2512,1476,1414, + 220,1469,2341,2138,2852,3183,2900,4939,2865,3502,1211,3680, 854,3227,1299,2976, +3172, 186,2998,1459, 443,1067,3251,1495, 321,1932,3054, 909, 753,1410,1828, 436, +2441,1119,1587,3164,2186,1258, 227, 231,1425,1890,3200,3942, 247, 959, 725,5254, +2741, 577,2158,2079, 929, 120, 174, 838,2813, 591,1115, 417,2024, 40,3240,1536, +1037, 291,4151,2354, 632,1298,2406,2500,3535,1825,1846,3451, 205,1171, 345,4238, + 18,1163, 811, 685,2208,1217, 425,1312,1508,1175,4308,2552,1033, 587,1381,3059, +2984,3482, 340,1316,4023,3972, 792,3176, 519, 777,4690, 918, 933,4130,2981,3741, + 90,3360,2911,2200,5184,4550, 609,3079,2030, 272,3379,2736, 363,3881,1130,1447, + 286, 779, 357,1169,3350,3137,1630,1220,2687,2391, 747,1277,3688,2618,2682,2601, +1156,3196,5290,4034,3102,1689,3596,3128, 874, 219,2783, 798, 508,1843,2461, 269, +1658,1776,1392,1913,2983,3287,2866,2159,2372, 829,4076, 46,4253,2873,1889,1894, + 915,1834,1631,2181,2318, 298, 664,2818,3555,2735, 954,3228,3117, 527,3511,2173, + 681,2712,3033,2247,2346,3467,1652, 155,2164,3382, 113,1994, 450, 899, 494, 994, +1237,2958,1875,2336,1926,3727, 545,1577,1550, 633,3473, 204,1305,3072,2410,1956, +2471, 707,2134, 841,2195,2196,2663,3843,1026,4940, 990,3252,4997, 368,1092, 437, +3212,3258,1933,1829, 675,2977,2893, 412, 943,3723,4644,3294,3283,2230,2373,5154, +2389,2241,2661,2323,1404,2524, 593, 787, 677,3008,1275,2059, 438,2709,2609,2240, +2269,2246,1446, 36,1568,1373,3892,1574,2301,1456,3962, 693,2276,5216,2035,1143, +2720,1919,1797,1811,2763,4137,2597,1830,1699,1488,1198,2090, 424,1694, 312,3634, +3390,4179,3335,2252,1214, 561,1059,3243,2295,2561, 975,5155,2321,2751,3772, 472, +1537,3282,3398,1047,2077,2348,2878,1323,3340,3076, 690,2906, 51, 369, 170,3541, +1060,2187,2688,3670,2541,1083,1683, 928,3918, 459, 109,4427, 599,3744,4286, 143, +2101,2730,2490, 82,1588,3036,2121, 281,1860, 477,4035,1238,2812,3020,2716,3312, +1530,2188,2055,1317, 843, 636,1808,1173,3495, 649, 181,1002, 147,3641,1159,2414, +3750,2289,2795, 813,3123,2610,1136,4368, 5,3391,4541,2174, 420, 429,1728, 754, +1228,2115,2219, 347,2223,2733, 735,1518,3003,2355,3134,1764,3948,3329,1888,2424, +1001,1234,1972,3321,3363,1672,1021,1450,1584, 226, 765, 655,2526,3404,3244,2302, +3665, 731, 594,2184, 319,1576, 621, 658,2656,4299,2099,3864,1279,2071,2598,2739, + 795,3086,3699,3908,1707,2352,2402,1382,3136,2475,1465,4847,3496,3865,1085,3004, +2591,1084, 213,2287,1963,3565,2250, 822, 793,4574,3187,1772,1789,3050, 595,1484, +1959,2770,1080,2650, 456, 422,2996, 940,3322,4328,4345,3092,2742, 965,2784, 739, +4124, 952,1358,2498,2949,2565, 332,2698,2378, 660,2260,2473,4194,3856,2919, 535, +1260,2651,1208,1428,1300,1949,1303,2942, 433,2455,2450,1251,1946, 614,1269, 641, +1306,1810,2737,3078,2912, 564,2365,1419,1415,1497,4460,2367,2185,1379,3005,1307, +3218,2175,1897,3063, 682,1157,4040,4005,1712,1160,1941,1399, 394, 402,2952,1573, +1151,2986,2404, 862, 299,2033,1489,3006, 346, 171,2886,3401,1726,2932, 168,2533, + 47,2507,1030,3735,1145,3370,1395,1318,1579,3609,4560,2857,4116,1457,2529,1965, + 504,1036,2690,2988,2405, 745,5871, 849,2397,2056,3081, 863,2359,3857,2096, 99, +1397,1769,2300,4428,1643,3455,1978,1757,3718,1440, 35,4879,3742,1296,4228,2280, + 160,5063,1599,2013, 166, 520,3479,1646,3345,3012, 490,1937,1545,1264,2182,2505, +1096,1188,1369,1436,2421,1667,2792,2460,1270,2122, 727,3167,2143, 806,1706,1012, +1800,3037, 960,2218,1882, 805, 139,2456,1139,1521, 851,1052,3093,3089, 342,2039, + 744,5097,1468,1502,1585,2087, 223, 939, 326,2140,2577, 892,2481,1623,4077, 982, +3708, 135,2131, 87,2503,3114,2326,1106, 876,1616, 547,2997,2831,2093,3441,4530, +4314, 9,3256,4229,4148, 659,1462,1986,1710,2046,2913,2231,4090,4880,5255,3392, +3274,1368,3689,4645,1477, 705,3384,3635,1068,1529,2941,1458,3782,1509, 100,1656, +2548, 718,2339, 408,1590,2780,3548,1838,4117,3719,1345,3530, 717,3442,2778,3220, +2898,1892,4590,3614,3371,2043,1998,1224,3483, 891, 635, 584,2559,3355, 733,1766, +1729,1172,3789,1891,2307, 781,2982,2271,1957,1580,5773,2633,2005,4195,3097,1535, +3213,1189,1934,5693,3262, 586,3118,1324,1598, 517,1564,2217,1868,1893,4445,3728, +2703,3139,1526,1787,1992,3882,2875,1549,1199,1056,2224,1904,2711,5098,4287, 338, +1993,3129,3489,2689,1809,2815,1997, 957,1855,3898,2550,3275,3057,1105,1319, 627, +1505,1911,1883,3526, 698,3629,3456,1833,1431, 746, 77,1261,2017,2296,1977,1885, + 125,1334,1600, 525,1798,1109,2222,1470,1945, 559,2236,1186,3443,2476,1929,1411, +2411,3135,1777,3372,2621,1841,1613,3229, 668,1430,1839,2643,2916, 195,1989,2671, +2358,1387, 629,3205,2293,5256,4439, 123,1310, 888,1879,4300,3021,3605,1003,1162, +3192,2910,2010, 140,2395,2859, 55,1082,2012,2901, 662, 419,2081,1438, 680,2774, +4654,3912,1620,1731,1625,5035,4065,2328, 512,1344, 802,5443,2163,2311,2537, 524, +3399, 98,1155,2103,1918,2606,3925,2816,1393,2465,1504,3773,2177,3963,1478,4346, + 180,1113,4655,3461,2028,1698, 833,2696,1235,1322,1594,4408,3623,3013,3225,2040, +3022, 541,2881, 607,3632,2029,1665,1219, 639,1385,1686,1099,2803,3231,1938,3188, +2858, 427, 676,2772,1168,2025, 454,3253,2486,3556, 230,1950, 580, 791,1991,1280, +1086,1974,2034, 630, 257,3338,2788,4903,1017, 86,4790, 966,2789,1995,1696,1131, + 259,3095,4188,1308, 179,1463,5257, 289,4107,1248, 42,3413,1725,2288, 896,1947, + 774,4474,4254, 604,3430,4264, 392,2514,2588, 452, 237,1408,3018, 988,4531,1970, +3034,3310, 540,2370,1562,1288,2990, 502,4765,1147, 4,1853,2708, 207, 294,2814, +4078,2902,2509, 684, 34,3105,3532,2551, 644, 709,2801,2344, 573,1727,3573,3557, +2021,1081,3100,4315,2100,3681, 199,2263,1837,2385, 146,3484,1195,2776,3949, 997, +1939,3973,1008,1091,1202,1962,1847,1149,4209,5444,1076, 493, 117,5400,2521, 972, +1490,2934,1796,4542,2374,1512,2933,2657, 413,2888,1135,2762,2314,2156,1355,2369, + 766,2007,2527,2170,3124,2491,2593,2632,4757,2437, 234,3125,3591,1898,1750,1376, +1942,3468,3138, 570,2127,2145,3276,4131, 962, 132,1445,4196, 19, 941,3624,3480, +3366,1973,1374,4461,3431,2629, 283,2415,2275, 808,2887,3620,2112,2563,1353,3610, + 955,1089,3103,1053, 96, 88,4097, 823,3808,1583, 399, 292,4091,3313, 421,1128, + 642,4006, 903,2539,1877,2082, 596, 29,4066,1790, 722,2157, 130, 995,1569, 769, +1485, 464, 513,2213, 288,1923,1101,2453,4316, 133, 486,2445, 50, 625, 487,2207, + 57, 423, 481,2962, 159,3729,1558, 491, 303, 482, 501, 240,2837, 112,3648,2392, +1783, 362, 8,3433,3422, 610,2793,3277,1390,1284,1654, 21,3823, 734, 367, 623, + 193, 287, 374,1009,1483, 816, 476, 313,2255,2340,1262,2150,2899,1146,2581, 782, +2116,1659,2018,1880, 255,3586,3314,1110,2867,2137,2564, 986,2767,5185,2006, 650, + 158, 926, 762, 881,3157,2717,2362,3587, 306,3690,3245,1542,3077,2427,1691,2478, +2118,2985,3490,2438, 539,2305, 983, 129,1754, 355,4201,2386, 827,2923, 104,1773, +2838,2771, 411,2905,3919, 376, 767, 122,1114, 828,2422,1817,3506, 266,3460,1007, +1609,4998, 945,2612,4429,2274, 726,1247,1964,2914,2199,2070,4002,4108, 657,3323, +1422, 579, 455,2764,4737,1222,2895,1670, 824,1223,1487,2525, 558, 861,3080, 598, +2659,2515,1967, 752,2583,2376,2214,4180, 977, 704,2464,4999,2622,4109,1210,2961, + 819,1541, 142,2284, 44, 418, 457,1126,3730,4347,4626,1644,1876,3671,1864, 302, +1063,5694, 624, 723,1984,3745,1314,1676,2488,1610,1449,3558,3569,2166,2098, 409, +1011,2325,3704,2306, 818,1732,1383,1824,1844,3757, 999,2705,3497,1216,1423,2683, +2426,2954,2501,2726,2229,1475,2554,5064,1971,1794,1666,2014,1343, 783, 724, 191, +2434,1354,2220,5065,1763,2752,2472,4152, 131, 175,2885,3434, 92,1466,4920,2616, +3871,3872,3866, 128,1551,1632, 669,1854,3682,4691,4125,1230, 188,2973,3290,1302, +1213, 560,3266, 917, 763,3909,3249,1760, 868,1958, 764,1782,2097, 145,2277,3774, +4462, 64,1491,3062, 971,2132,3606,2442, 221,1226,1617, 218, 323,1185,3207,3147, + 571, 619,1473,1005,1744,2281, 449,1887,2396,3685, 275, 375,3816,1743,3844,3731, + 845,1983,2350,4210,1377, 773, 967,3499,3052,3743,2725,4007,1697,1022,3943,1464, +3264,2855,2722,1952,1029,2839,2467, 84,4383,2215, 820,1391,2015,2448,3672, 377, +1948,2168, 797,2545,3536,2578,2645, 94,2874,1678, 405,1259,3071, 771, 546,1315, + 470,1243,3083, 895,2468, 981, 969,2037, 846,4181, 653,1276,2928, 14,2594, 557, +3007,2474, 156, 902,1338,1740,2574, 537,2518, 973,2282,2216,2433,1928, 138,2903, +1293,2631,1612, 646,3457, 839,2935, 111, 496,2191,2847, 589,3186, 149,3994,2060, +4031,2641,4067,3145,1870, 37,3597,2136,1025,2051,3009,3383,3549,1121,1016,3261, +1301, 251,2446,2599,2153, 872,3246, 637, 334,3705, 831, 884, 921,3065,3140,4092, +2198,1944, 246,2964, 108,2045,1152,1921,2308,1031, 203,3173,4170,1907,3890, 810, +1401,2003,1690, 506, 647,1242,2828,1761,1649,3208,2249,1589,3709,2931,5156,1708, + 498, 666,2613, 834,3817,1231, 184,2851,1124, 883,3197,2261,3710,1765,1553,2658, +1178,2639,2351, 93,1193, 942,2538,2141,4402, 235,1821, 870,1591,2192,1709,1871, +3341,1618,4126,2595,2334, 603, 651, 69, 701, 268,2662,3411,2555,1380,1606, 503, + 448, 254,2371,2646, 574,1187,2309,1770, 322,2235,1292,1801, 305, 566,1133, 229, +2067,2057, 706, 167, 483,2002,2672,3295,1820,3561,3067, 316, 378,2746,3452,1112, + 136,1981, 507,1651,2917,1117, 285,4591, 182,2580,3522,1304, 335,3303,1835,2504, +1795,1792,2248, 674,1018,2106,2449,1857,2292,2845, 976,3047,1781,2600,2727,1389, +1281, 52,3152, 153, 265,3950, 672,3485,3951,4463, 430,1183, 365, 278,2169, 27, +1407,1336,2304, 209,1340,1730,2202,1852,2403,2883, 979,1737,1062, 631,2829,2542, +3876,2592, 825,2086,2226,3048,3625, 352,1417,3724, 542, 991, 431,1351,3938,1861, +2294, 826,1361,2927,3142,3503,1738, 463,2462,2723, 582,1916,1595,2808, 400,3845, +3891,2868,3621,2254, 58,2492,1123, 910,2160,2614,1372,1603,1196,1072,3385,1700, +3267,1980, 696, 480,2430, 920, 799,1570,2920,1951,2041,4047,2540,1321,4223,2469, +3562,2228,1271,2602, 401,2833,3351,2575,5157, 907,2312,1256, 410, 263,3507,1582, + 996, 678,1849,2316,1480, 908,3545,2237, 703,2322, 667,1826,2849,1531,2604,2999, +2407,3146,2151,2630,1786,3711, 469,3542, 497,3899,2409, 858, 837,4446,3393,1274, + 786, 620,1845,2001,3311, 484, 308,3367,1204,1815,3691,2332,1532,2557,1842,2020, +2724,1927,2333,4440, 567, 22,1673,2728,4475,1987,1858,1144,1597, 101,1832,3601, + 12, 974,3783,4391, 951,1412, 1,3720, 453,4608,4041, 528,1041,1027,3230,2628, +1129, 875,1051,3291,1203,2262,1069,2860,2799,2149,2615,3278, 144,1758,3040, 31, + 475,1680, 366,2685,3184, 311,1642,4008,2466,5036,1593,1493,2809, 216,1420,1668, + 233, 304,2128,3284, 232,1429,1768,1040,2008,3407,2740,2967,2543, 242,2133, 778, +1565,2022,2620, 505,2189,2756,1098,2273, 372,1614, 708, 553,2846,2094,2278, 169, +3626,2835,4161, 228,2674,3165, 809,1454,1309, 466,1705,1095, 900,3423, 880,2667, +3751,5258,2317,3109,2571,4317,2766,1503,1342, 866,4447,1118, 63,2076, 314,1881, +1348,1061, 172, 978,3515,1747, 532, 511,3970, 6, 601, 905,2699,3300,1751, 276, +1467,3725,2668, 65,4239,2544,2779,2556,1604, 578,2451,1802, 992,2331,2624,1320, +3446, 713,1513,1013, 103,2786,2447,1661, 886,1702, 916, 654,3574,2031,1556, 751, +2178,2821,2179,1498,1538,2176, 271, 914,2251,2080,1325, 638,1953,2937,3877,2432, +2754, 95,3265,1716, 260,1227,4083, 775, 106,1357,3254, 426,1607, 555,2480, 772, +1985, 244,2546, 474, 495,1046,2611,1851,2061, 71,2089,1675,2590, 742,3758,2843, +3222,1433, 267,2180,2576,2826,2233,2092,3913,2435, 956,1745,3075, 856,2113,1116, + 451, 3,1988,2896,1398, 993,2463,1878,2049,1341,2718,2721,2870,2108, 712,2904, +4363,2753,2324, 277,2872,2349,2649, 384, 987, 435, 691,3000, 922, 164,3939, 652, +1500,1184,4153,2482,3373,2165,4848,2335,3775,3508,3154,2806,2830,1554,2102,1664, +2530,1434,2408, 893,1547,2623,3447,2832,2242,2532,3169,2856,3223,2078, 49,3770, +3469, 462, 318, 656,2259,3250,3069, 679,1629,2758, 344,1138,1104,3120,1836,1283, +3115,2154,1437,4448, 934, 759,1999, 794,2862,1038, 533,2560,1722,2342, 855,2626, +1197,1663,4476,3127, 85,4240,2528, 25,1111,1181,3673, 407,3470,4561,2679,2713, + 768,1925,2841,3986,1544,1165, 932, 373,1240,2146,1930,2673, 721,4766, 354,4333, + 391,2963, 187, 61,3364,1442,1102, 330,1940,1767, 341,3809,4118, 393,2496,2062, +2211, 105, 331, 300, 439, 913,1332, 626, 379,3304,1557, 328, 689,3952, 309,1555, + 931, 317,2517,3027, 325, 569, 686,2107,3084, 60,1042,1333,2794, 264,3177,4014, +1628, 258,3712, 7,4464,1176,1043,1778, 683, 114,1975, 78,1492, 383,1886, 510, + 386, 645,5291,2891,2069,3305,4138,3867,2939,2603,2493,1935,1066,1848,3588,1015, +1282,1289,4609, 697,1453,3044,2666,3611,1856,2412, 54, 719,1330, 568,3778,2459, +1748, 788, 492, 551,1191,1000, 488,3394,3763, 282,1799, 348,2016,1523,3155,2390, +1049, 382,2019,1788,1170, 729,2968,3523, 897,3926,2785,2938,3292, 350,2319,3238, +1718,1717,2655,3453,3143,4465, 161,2889,2980,2009,1421, 56,1908,1640,2387,2232, +1917,1874,2477,4921, 148, 83,3438, 592,4245,2882,1822,1055, 741, 115,1496,1624, + 381,1638,4592,1020, 516,3214, 458, 947,4575,1432, 211,1514,2926,1865,2142, 189, + 852,1221,1400,1486, 882,2299,4036, 351, 28,1122, 700,6479,6480,6481,6482,6483, #last 512 +) +# fmt: on diff --git a/virt/lib/python3.9/site-packages/pip/_vendor/chardet/johabfreq 3.py b/virt/lib/python3.9/site-packages/pip/_vendor/chardet/johabfreq 3.py new file mode 100644 index 00000000..3fee2c32 --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_vendor/chardet/johabfreq 3.py @@ -0,0 +1,2382 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# The frequency data itself is the same as euc-kr. +# This is just a mapping table to euc-kr. + +JOHAB_TO_EUCKR_ORDER_TABLE = { + 0x8861: 0, + 0x8862: 1, + 0x8865: 2, + 0x8868: 3, + 0x8869: 4, + 0x886A: 5, + 0x886B: 6, + 0x8871: 7, + 0x8873: 8, + 0x8874: 9, + 0x8875: 10, + 0x8876: 11, + 0x8877: 12, + 0x8878: 13, + 0x8879: 14, + 0x887B: 15, + 0x887C: 16, + 0x887D: 17, + 0x8881: 18, + 0x8882: 19, + 0x8885: 20, + 0x8889: 21, + 0x8891: 22, + 0x8893: 23, + 0x8895: 24, + 0x8896: 25, + 0x8897: 26, + 0x88A1: 27, + 0x88A2: 28, + 0x88A5: 29, + 0x88A9: 30, + 0x88B5: 31, + 0x88B7: 32, + 0x88C1: 33, + 0x88C5: 34, + 0x88C9: 35, + 0x88E1: 36, + 0x88E2: 37, + 0x88E5: 38, + 0x88E8: 39, + 0x88E9: 40, + 0x88EB: 41, + 0x88F1: 42, + 0x88F3: 43, + 0x88F5: 44, + 0x88F6: 45, + 0x88F7: 46, + 0x88F8: 47, + 0x88FB: 48, + 0x88FC: 49, + 0x88FD: 50, + 0x8941: 51, + 0x8945: 52, + 0x8949: 53, + 0x8951: 54, + 0x8953: 55, + 0x8955: 56, + 0x8956: 57, + 0x8957: 58, + 0x8961: 59, + 0x8962: 60, + 0x8963: 61, + 0x8965: 62, + 0x8968: 63, + 0x8969: 64, + 0x8971: 65, + 0x8973: 66, + 0x8975: 67, + 0x8976: 68, + 0x8977: 69, + 0x897B: 70, + 0x8981: 71, + 0x8985: 72, + 0x8989: 73, + 0x8993: 74, + 0x8995: 75, + 0x89A1: 76, + 0x89A2: 77, + 0x89A5: 78, + 0x89A8: 79, + 0x89A9: 80, + 0x89AB: 81, + 0x89AD: 82, + 0x89B0: 83, + 0x89B1: 84, + 0x89B3: 85, + 0x89B5: 86, + 0x89B7: 87, + 0x89B8: 88, + 0x89C1: 89, + 0x89C2: 90, + 0x89C5: 91, + 0x89C9: 92, + 0x89CB: 93, + 0x89D1: 94, + 0x89D3: 95, + 0x89D5: 96, + 0x89D7: 97, + 0x89E1: 98, + 0x89E5: 99, + 0x89E9: 100, + 0x89F3: 101, + 0x89F6: 102, + 0x89F7: 103, + 0x8A41: 104, + 0x8A42: 105, + 0x8A45: 106, + 0x8A49: 107, + 0x8A51: 108, + 0x8A53: 109, + 0x8A55: 110, + 0x8A57: 111, + 0x8A61: 112, + 0x8A65: 113, + 0x8A69: 114, + 0x8A73: 115, + 0x8A75: 116, + 0x8A81: 117, + 0x8A82: 118, + 0x8A85: 119, + 0x8A88: 120, + 0x8A89: 121, + 0x8A8A: 122, + 0x8A8B: 123, + 0x8A90: 124, + 0x8A91: 125, + 0x8A93: 126, + 0x8A95: 127, + 0x8A97: 128, + 0x8A98: 129, + 0x8AA1: 130, + 0x8AA2: 131, + 0x8AA5: 132, + 0x8AA9: 133, + 0x8AB6: 134, + 0x8AB7: 135, + 0x8AC1: 136, + 0x8AD5: 137, + 0x8AE1: 138, + 0x8AE2: 139, + 0x8AE5: 140, + 0x8AE9: 141, + 0x8AF1: 142, + 0x8AF3: 143, + 0x8AF5: 144, + 0x8B41: 145, + 0x8B45: 146, + 0x8B49: 147, + 0x8B61: 148, + 0x8B62: 149, + 0x8B65: 150, + 0x8B68: 151, + 0x8B69: 152, + 0x8B6A: 153, + 0x8B71: 154, + 0x8B73: 155, + 0x8B75: 156, + 0x8B77: 157, + 0x8B81: 158, + 0x8BA1: 159, + 0x8BA2: 160, + 0x8BA5: 161, + 0x8BA8: 162, + 0x8BA9: 163, + 0x8BAB: 164, + 0x8BB1: 165, + 0x8BB3: 166, + 0x8BB5: 167, + 0x8BB7: 168, + 0x8BB8: 169, + 0x8BBC: 170, + 0x8C61: 171, + 0x8C62: 172, + 0x8C63: 173, + 0x8C65: 174, + 0x8C69: 175, + 0x8C6B: 176, + 0x8C71: 177, + 0x8C73: 178, + 0x8C75: 179, + 0x8C76: 180, + 0x8C77: 181, + 0x8C7B: 182, + 0x8C81: 183, + 0x8C82: 184, + 0x8C85: 185, + 0x8C89: 186, + 0x8C91: 187, + 0x8C93: 188, + 0x8C95: 189, + 0x8C96: 190, + 0x8C97: 191, + 0x8CA1: 192, + 0x8CA2: 193, + 0x8CA9: 194, + 0x8CE1: 195, + 0x8CE2: 196, + 0x8CE3: 197, + 0x8CE5: 198, + 0x8CE9: 199, + 0x8CF1: 200, + 0x8CF3: 201, + 0x8CF5: 202, + 0x8CF6: 203, + 0x8CF7: 204, + 0x8D41: 205, + 0x8D42: 206, + 0x8D45: 207, + 0x8D51: 208, + 0x8D55: 209, + 0x8D57: 210, + 0x8D61: 211, + 0x8D65: 212, + 0x8D69: 213, + 0x8D75: 214, + 0x8D76: 215, + 0x8D7B: 216, + 0x8D81: 217, + 0x8DA1: 218, + 0x8DA2: 219, + 0x8DA5: 220, + 0x8DA7: 221, + 0x8DA9: 222, + 0x8DB1: 223, + 0x8DB3: 224, + 0x8DB5: 225, + 0x8DB7: 226, + 0x8DB8: 227, + 0x8DB9: 228, + 0x8DC1: 229, + 0x8DC2: 230, + 0x8DC9: 231, + 0x8DD6: 232, + 0x8DD7: 233, + 0x8DE1: 234, + 0x8DE2: 235, + 0x8DF7: 236, + 0x8E41: 237, + 0x8E45: 238, + 0x8E49: 239, + 0x8E51: 240, + 0x8E53: 241, + 0x8E57: 242, + 0x8E61: 243, + 0x8E81: 244, + 0x8E82: 245, + 0x8E85: 246, + 0x8E89: 247, + 0x8E90: 248, + 0x8E91: 249, + 0x8E93: 250, + 0x8E95: 251, + 0x8E97: 252, + 0x8E98: 253, + 0x8EA1: 254, + 0x8EA9: 255, + 0x8EB6: 256, + 0x8EB7: 257, + 0x8EC1: 258, + 0x8EC2: 259, + 0x8EC5: 260, + 0x8EC9: 261, + 0x8ED1: 262, + 0x8ED3: 263, + 0x8ED6: 264, + 0x8EE1: 265, + 0x8EE5: 266, + 0x8EE9: 267, + 0x8EF1: 268, + 0x8EF3: 269, + 0x8F41: 270, + 0x8F61: 271, + 0x8F62: 272, + 0x8F65: 273, + 0x8F67: 274, + 0x8F69: 275, + 0x8F6B: 276, + 0x8F70: 277, + 0x8F71: 278, + 0x8F73: 279, + 0x8F75: 280, + 0x8F77: 281, + 0x8F7B: 282, + 0x8FA1: 283, + 0x8FA2: 284, + 0x8FA5: 285, + 0x8FA9: 286, + 0x8FB1: 287, + 0x8FB3: 288, + 0x8FB5: 289, + 0x8FB7: 290, + 0x9061: 291, + 0x9062: 292, + 0x9063: 293, + 0x9065: 294, + 0x9068: 295, + 0x9069: 296, + 0x906A: 297, + 0x906B: 298, + 0x9071: 299, + 0x9073: 300, + 0x9075: 301, + 0x9076: 302, + 0x9077: 303, + 0x9078: 304, + 0x9079: 305, + 0x907B: 306, + 0x907D: 307, + 0x9081: 308, + 0x9082: 309, + 0x9085: 310, + 0x9089: 311, + 0x9091: 312, + 0x9093: 313, + 0x9095: 314, + 0x9096: 315, + 0x9097: 316, + 0x90A1: 317, + 0x90A2: 318, + 0x90A5: 319, + 0x90A9: 320, + 0x90B1: 321, + 0x90B7: 322, + 0x90E1: 323, + 0x90E2: 324, + 0x90E4: 325, + 0x90E5: 326, + 0x90E9: 327, + 0x90EB: 328, + 0x90EC: 329, + 0x90F1: 330, + 0x90F3: 331, + 0x90F5: 332, + 0x90F6: 333, + 0x90F7: 334, + 0x90FD: 335, + 0x9141: 336, + 0x9142: 337, + 0x9145: 338, + 0x9149: 339, + 0x9151: 340, + 0x9153: 341, + 0x9155: 342, + 0x9156: 343, + 0x9157: 344, + 0x9161: 345, + 0x9162: 346, + 0x9165: 347, + 0x9169: 348, + 0x9171: 349, + 0x9173: 350, + 0x9176: 351, + 0x9177: 352, + 0x917A: 353, + 0x9181: 354, + 0x9185: 355, + 0x91A1: 356, + 0x91A2: 357, + 0x91A5: 358, + 0x91A9: 359, + 0x91AB: 360, + 0x91B1: 361, + 0x91B3: 362, + 0x91B5: 363, + 0x91B7: 364, + 0x91BC: 365, + 0x91BD: 366, + 0x91C1: 367, + 0x91C5: 368, + 0x91C9: 369, + 0x91D6: 370, + 0x9241: 371, + 0x9245: 372, + 0x9249: 373, + 0x9251: 374, + 0x9253: 375, + 0x9255: 376, + 0x9261: 377, + 0x9262: 378, + 0x9265: 379, + 0x9269: 380, + 0x9273: 381, + 0x9275: 382, + 0x9277: 383, + 0x9281: 384, + 0x9282: 385, + 0x9285: 386, + 0x9288: 387, + 0x9289: 388, + 0x9291: 389, + 0x9293: 390, + 0x9295: 391, + 0x9297: 392, + 0x92A1: 393, + 0x92B6: 394, + 0x92C1: 395, + 0x92E1: 396, + 0x92E5: 397, + 0x92E9: 398, + 0x92F1: 399, + 0x92F3: 400, + 0x9341: 401, + 0x9342: 402, + 0x9349: 403, + 0x9351: 404, + 0x9353: 405, + 0x9357: 406, + 0x9361: 407, + 0x9362: 408, + 0x9365: 409, + 0x9369: 410, + 0x936A: 411, + 0x936B: 412, + 0x9371: 413, + 0x9373: 414, + 0x9375: 415, + 0x9377: 416, + 0x9378: 417, + 0x937C: 418, + 0x9381: 419, + 0x9385: 420, + 0x9389: 421, + 0x93A1: 422, + 0x93A2: 423, + 0x93A5: 424, + 0x93A9: 425, + 0x93AB: 426, + 0x93B1: 427, + 0x93B3: 428, + 0x93B5: 429, + 0x93B7: 430, + 0x93BC: 431, + 0x9461: 432, + 0x9462: 433, + 0x9463: 434, + 0x9465: 435, + 0x9468: 436, + 0x9469: 437, + 0x946A: 438, + 0x946B: 439, + 0x946C: 440, + 0x9470: 441, + 0x9471: 442, + 0x9473: 443, + 0x9475: 444, + 0x9476: 445, + 0x9477: 446, + 0x9478: 447, + 0x9479: 448, + 0x947D: 449, + 0x9481: 450, + 0x9482: 451, + 0x9485: 452, + 0x9489: 453, + 0x9491: 454, + 0x9493: 455, + 0x9495: 456, + 0x9496: 457, + 0x9497: 458, + 0x94A1: 459, + 0x94E1: 460, + 0x94E2: 461, + 0x94E3: 462, + 0x94E5: 463, + 0x94E8: 464, + 0x94E9: 465, + 0x94EB: 466, + 0x94EC: 467, + 0x94F1: 468, + 0x94F3: 469, + 0x94F5: 470, + 0x94F7: 471, + 0x94F9: 472, + 0x94FC: 473, + 0x9541: 474, + 0x9542: 475, + 0x9545: 476, + 0x9549: 477, + 0x9551: 478, + 0x9553: 479, + 0x9555: 480, + 0x9556: 481, + 0x9557: 482, + 0x9561: 483, + 0x9565: 484, + 0x9569: 485, + 0x9576: 486, + 0x9577: 487, + 0x9581: 488, + 0x9585: 489, + 0x95A1: 490, + 0x95A2: 491, + 0x95A5: 492, + 0x95A8: 493, + 0x95A9: 494, + 0x95AB: 495, + 0x95AD: 496, + 0x95B1: 497, + 0x95B3: 498, + 0x95B5: 499, + 0x95B7: 500, + 0x95B9: 501, + 0x95BB: 502, + 0x95C1: 503, + 0x95C5: 504, + 0x95C9: 505, + 0x95E1: 506, + 0x95F6: 507, + 0x9641: 508, + 0x9645: 509, + 0x9649: 510, + 0x9651: 511, + 0x9653: 512, + 0x9655: 513, + 0x9661: 514, + 0x9681: 515, + 0x9682: 516, + 0x9685: 517, + 0x9689: 518, + 0x9691: 519, + 0x9693: 520, + 0x9695: 521, + 0x9697: 522, + 0x96A1: 523, + 0x96B6: 524, + 0x96C1: 525, + 0x96D7: 526, + 0x96E1: 527, + 0x96E5: 528, + 0x96E9: 529, + 0x96F3: 530, + 0x96F5: 531, + 0x96F7: 532, + 0x9741: 533, + 0x9745: 534, + 0x9749: 535, + 0x9751: 536, + 0x9757: 537, + 0x9761: 538, + 0x9762: 539, + 0x9765: 540, + 0x9768: 541, + 0x9769: 542, + 0x976B: 543, + 0x9771: 544, + 0x9773: 545, + 0x9775: 546, + 0x9777: 547, + 0x9781: 548, + 0x97A1: 549, + 0x97A2: 550, + 0x97A5: 551, + 0x97A8: 552, + 0x97A9: 553, + 0x97B1: 554, + 0x97B3: 555, + 0x97B5: 556, + 0x97B6: 557, + 0x97B7: 558, + 0x97B8: 559, + 0x9861: 560, + 0x9862: 561, + 0x9865: 562, + 0x9869: 563, + 0x9871: 564, + 0x9873: 565, + 0x9875: 566, + 0x9876: 567, + 0x9877: 568, + 0x987D: 569, + 0x9881: 570, + 0x9882: 571, + 0x9885: 572, + 0x9889: 573, + 0x9891: 574, + 0x9893: 575, + 0x9895: 576, + 0x9896: 577, + 0x9897: 578, + 0x98E1: 579, + 0x98E2: 580, + 0x98E5: 581, + 0x98E9: 582, + 0x98EB: 583, + 0x98EC: 584, + 0x98F1: 585, + 0x98F3: 586, + 0x98F5: 587, + 0x98F6: 588, + 0x98F7: 589, + 0x98FD: 590, + 0x9941: 591, + 0x9942: 592, + 0x9945: 593, + 0x9949: 594, + 0x9951: 595, + 0x9953: 596, + 0x9955: 597, + 0x9956: 598, + 0x9957: 599, + 0x9961: 600, + 0x9976: 601, + 0x99A1: 602, + 0x99A2: 603, + 0x99A5: 604, + 0x99A9: 605, + 0x99B7: 606, + 0x99C1: 607, + 0x99C9: 608, + 0x99E1: 609, + 0x9A41: 610, + 0x9A45: 611, + 0x9A81: 612, + 0x9A82: 613, + 0x9A85: 614, + 0x9A89: 615, + 0x9A90: 616, + 0x9A91: 617, + 0x9A97: 618, + 0x9AC1: 619, + 0x9AE1: 620, + 0x9AE5: 621, + 0x9AE9: 622, + 0x9AF1: 623, + 0x9AF3: 624, + 0x9AF7: 625, + 0x9B61: 626, + 0x9B62: 627, + 0x9B65: 628, + 0x9B68: 629, + 0x9B69: 630, + 0x9B71: 631, + 0x9B73: 632, + 0x9B75: 633, + 0x9B81: 634, + 0x9B85: 635, + 0x9B89: 636, + 0x9B91: 637, + 0x9B93: 638, + 0x9BA1: 639, + 0x9BA5: 640, + 0x9BA9: 641, + 0x9BB1: 642, + 0x9BB3: 643, + 0x9BB5: 644, + 0x9BB7: 645, + 0x9C61: 646, + 0x9C62: 647, + 0x9C65: 648, + 0x9C69: 649, + 0x9C71: 650, + 0x9C73: 651, + 0x9C75: 652, + 0x9C76: 653, + 0x9C77: 654, + 0x9C78: 655, + 0x9C7C: 656, + 0x9C7D: 657, + 0x9C81: 658, + 0x9C82: 659, + 0x9C85: 660, + 0x9C89: 661, + 0x9C91: 662, + 0x9C93: 663, + 0x9C95: 664, + 0x9C96: 665, + 0x9C97: 666, + 0x9CA1: 667, + 0x9CA2: 668, + 0x9CA5: 669, + 0x9CB5: 670, + 0x9CB7: 671, + 0x9CE1: 672, + 0x9CE2: 673, + 0x9CE5: 674, + 0x9CE9: 675, + 0x9CF1: 676, + 0x9CF3: 677, + 0x9CF5: 678, + 0x9CF6: 679, + 0x9CF7: 680, + 0x9CFD: 681, + 0x9D41: 682, + 0x9D42: 683, + 0x9D45: 684, + 0x9D49: 685, + 0x9D51: 686, + 0x9D53: 687, + 0x9D55: 688, + 0x9D57: 689, + 0x9D61: 690, + 0x9D62: 691, + 0x9D65: 692, + 0x9D69: 693, + 0x9D71: 694, + 0x9D73: 695, + 0x9D75: 696, + 0x9D76: 697, + 0x9D77: 698, + 0x9D81: 699, + 0x9D85: 700, + 0x9D93: 701, + 0x9D95: 702, + 0x9DA1: 703, + 0x9DA2: 704, + 0x9DA5: 705, + 0x9DA9: 706, + 0x9DB1: 707, + 0x9DB3: 708, + 0x9DB5: 709, + 0x9DB7: 710, + 0x9DC1: 711, + 0x9DC5: 712, + 0x9DD7: 713, + 0x9DF6: 714, + 0x9E41: 715, + 0x9E45: 716, + 0x9E49: 717, + 0x9E51: 718, + 0x9E53: 719, + 0x9E55: 720, + 0x9E57: 721, + 0x9E61: 722, + 0x9E65: 723, + 0x9E69: 724, + 0x9E73: 725, + 0x9E75: 726, + 0x9E77: 727, + 0x9E81: 728, + 0x9E82: 729, + 0x9E85: 730, + 0x9E89: 731, + 0x9E91: 732, + 0x9E93: 733, + 0x9E95: 734, + 0x9E97: 735, + 0x9EA1: 736, + 0x9EB6: 737, + 0x9EC1: 738, + 0x9EE1: 739, + 0x9EE2: 740, + 0x9EE5: 741, + 0x9EE9: 742, + 0x9EF1: 743, + 0x9EF5: 744, + 0x9EF7: 745, + 0x9F41: 746, + 0x9F42: 747, + 0x9F45: 748, + 0x9F49: 749, + 0x9F51: 750, + 0x9F53: 751, + 0x9F55: 752, + 0x9F57: 753, + 0x9F61: 754, + 0x9F62: 755, + 0x9F65: 756, + 0x9F69: 757, + 0x9F71: 758, + 0x9F73: 759, + 0x9F75: 760, + 0x9F77: 761, + 0x9F78: 762, + 0x9F7B: 763, + 0x9F7C: 764, + 0x9FA1: 765, + 0x9FA2: 766, + 0x9FA5: 767, + 0x9FA9: 768, + 0x9FB1: 769, + 0x9FB3: 770, + 0x9FB5: 771, + 0x9FB7: 772, + 0xA061: 773, + 0xA062: 774, + 0xA065: 775, + 0xA067: 776, + 0xA068: 777, + 0xA069: 778, + 0xA06A: 779, + 0xA06B: 780, + 0xA071: 781, + 0xA073: 782, + 0xA075: 783, + 0xA077: 784, + 0xA078: 785, + 0xA07B: 786, + 0xA07D: 787, + 0xA081: 788, + 0xA082: 789, + 0xA085: 790, + 0xA089: 791, + 0xA091: 792, + 0xA093: 793, + 0xA095: 794, + 0xA096: 795, + 0xA097: 796, + 0xA098: 797, + 0xA0A1: 798, + 0xA0A2: 799, + 0xA0A9: 800, + 0xA0B7: 801, + 0xA0E1: 802, + 0xA0E2: 803, + 0xA0E5: 804, + 0xA0E9: 805, + 0xA0EB: 806, + 0xA0F1: 807, + 0xA0F3: 808, + 0xA0F5: 809, + 0xA0F7: 810, + 0xA0F8: 811, + 0xA0FD: 812, + 0xA141: 813, + 0xA142: 814, + 0xA145: 815, + 0xA149: 816, + 0xA151: 817, + 0xA153: 818, + 0xA155: 819, + 0xA156: 820, + 0xA157: 821, + 0xA161: 822, + 0xA162: 823, + 0xA165: 824, + 0xA169: 825, + 0xA175: 826, + 0xA176: 827, + 0xA177: 828, + 0xA179: 829, + 0xA181: 830, + 0xA1A1: 831, + 0xA1A2: 832, + 0xA1A4: 833, + 0xA1A5: 834, + 0xA1A9: 835, + 0xA1AB: 836, + 0xA1B1: 837, + 0xA1B3: 838, + 0xA1B5: 839, + 0xA1B7: 840, + 0xA1C1: 841, + 0xA1C5: 842, + 0xA1D6: 843, + 0xA1D7: 844, + 0xA241: 845, + 0xA245: 846, + 0xA249: 847, + 0xA253: 848, + 0xA255: 849, + 0xA257: 850, + 0xA261: 851, + 0xA265: 852, + 0xA269: 853, + 0xA273: 854, + 0xA275: 855, + 0xA281: 856, + 0xA282: 857, + 0xA283: 858, + 0xA285: 859, + 0xA288: 860, + 0xA289: 861, + 0xA28A: 862, + 0xA28B: 863, + 0xA291: 864, + 0xA293: 865, + 0xA295: 866, + 0xA297: 867, + 0xA29B: 868, + 0xA29D: 869, + 0xA2A1: 870, + 0xA2A5: 871, + 0xA2A9: 872, + 0xA2B3: 873, + 0xA2B5: 874, + 0xA2C1: 875, + 0xA2E1: 876, + 0xA2E5: 877, + 0xA2E9: 878, + 0xA341: 879, + 0xA345: 880, + 0xA349: 881, + 0xA351: 882, + 0xA355: 883, + 0xA361: 884, + 0xA365: 885, + 0xA369: 886, + 0xA371: 887, + 0xA375: 888, + 0xA3A1: 889, + 0xA3A2: 890, + 0xA3A5: 891, + 0xA3A8: 892, + 0xA3A9: 893, + 0xA3AB: 894, + 0xA3B1: 895, + 0xA3B3: 896, + 0xA3B5: 897, + 0xA3B6: 898, + 0xA3B7: 899, + 0xA3B9: 900, + 0xA3BB: 901, + 0xA461: 902, + 0xA462: 903, + 0xA463: 904, + 0xA464: 905, + 0xA465: 906, + 0xA468: 907, + 0xA469: 908, + 0xA46A: 909, + 0xA46B: 910, + 0xA46C: 911, + 0xA471: 912, + 0xA473: 913, + 0xA475: 914, + 0xA477: 915, + 0xA47B: 916, + 0xA481: 917, + 0xA482: 918, + 0xA485: 919, + 0xA489: 920, + 0xA491: 921, + 0xA493: 922, + 0xA495: 923, + 0xA496: 924, + 0xA497: 925, + 0xA49B: 926, + 0xA4A1: 927, + 0xA4A2: 928, + 0xA4A5: 929, + 0xA4B3: 930, + 0xA4E1: 931, + 0xA4E2: 932, + 0xA4E5: 933, + 0xA4E8: 934, + 0xA4E9: 935, + 0xA4EB: 936, + 0xA4F1: 937, + 0xA4F3: 938, + 0xA4F5: 939, + 0xA4F7: 940, + 0xA4F8: 941, + 0xA541: 942, + 0xA542: 943, + 0xA545: 944, + 0xA548: 945, + 0xA549: 946, + 0xA551: 947, + 0xA553: 948, + 0xA555: 949, + 0xA556: 950, + 0xA557: 951, + 0xA561: 952, + 0xA562: 953, + 0xA565: 954, + 0xA569: 955, + 0xA573: 956, + 0xA575: 957, + 0xA576: 958, + 0xA577: 959, + 0xA57B: 960, + 0xA581: 961, + 0xA585: 962, + 0xA5A1: 963, + 0xA5A2: 964, + 0xA5A3: 965, + 0xA5A5: 966, + 0xA5A9: 967, + 0xA5B1: 968, + 0xA5B3: 969, + 0xA5B5: 970, + 0xA5B7: 971, + 0xA5C1: 972, + 0xA5C5: 973, + 0xA5D6: 974, + 0xA5E1: 975, + 0xA5F6: 976, + 0xA641: 977, + 0xA642: 978, + 0xA645: 979, + 0xA649: 980, + 0xA651: 981, + 0xA653: 982, + 0xA661: 983, + 0xA665: 984, + 0xA681: 985, + 0xA682: 986, + 0xA685: 987, + 0xA688: 988, + 0xA689: 989, + 0xA68A: 990, + 0xA68B: 991, + 0xA691: 992, + 0xA693: 993, + 0xA695: 994, + 0xA697: 995, + 0xA69B: 996, + 0xA69C: 997, + 0xA6A1: 998, + 0xA6A9: 999, + 0xA6B6: 1000, + 0xA6C1: 1001, + 0xA6E1: 1002, + 0xA6E2: 1003, + 0xA6E5: 1004, + 0xA6E9: 1005, + 0xA6F7: 1006, + 0xA741: 1007, + 0xA745: 1008, + 0xA749: 1009, + 0xA751: 1010, + 0xA755: 1011, + 0xA757: 1012, + 0xA761: 1013, + 0xA762: 1014, + 0xA765: 1015, + 0xA769: 1016, + 0xA771: 1017, + 0xA773: 1018, + 0xA775: 1019, + 0xA7A1: 1020, + 0xA7A2: 1021, + 0xA7A5: 1022, + 0xA7A9: 1023, + 0xA7AB: 1024, + 0xA7B1: 1025, + 0xA7B3: 1026, + 0xA7B5: 1027, + 0xA7B7: 1028, + 0xA7B8: 1029, + 0xA7B9: 1030, + 0xA861: 1031, + 0xA862: 1032, + 0xA865: 1033, + 0xA869: 1034, + 0xA86B: 1035, + 0xA871: 1036, + 0xA873: 1037, + 0xA875: 1038, + 0xA876: 1039, + 0xA877: 1040, + 0xA87D: 1041, + 0xA881: 1042, + 0xA882: 1043, + 0xA885: 1044, + 0xA889: 1045, + 0xA891: 1046, + 0xA893: 1047, + 0xA895: 1048, + 0xA896: 1049, + 0xA897: 1050, + 0xA8A1: 1051, + 0xA8A2: 1052, + 0xA8B1: 1053, + 0xA8E1: 1054, + 0xA8E2: 1055, + 0xA8E5: 1056, + 0xA8E8: 1057, + 0xA8E9: 1058, + 0xA8F1: 1059, + 0xA8F5: 1060, + 0xA8F6: 1061, + 0xA8F7: 1062, + 0xA941: 1063, + 0xA957: 1064, + 0xA961: 1065, + 0xA962: 1066, + 0xA971: 1067, + 0xA973: 1068, + 0xA975: 1069, + 0xA976: 1070, + 0xA977: 1071, + 0xA9A1: 1072, + 0xA9A2: 1073, + 0xA9A5: 1074, + 0xA9A9: 1075, + 0xA9B1: 1076, + 0xA9B3: 1077, + 0xA9B7: 1078, + 0xAA41: 1079, + 0xAA61: 1080, + 0xAA77: 1081, + 0xAA81: 1082, + 0xAA82: 1083, + 0xAA85: 1084, + 0xAA89: 1085, + 0xAA91: 1086, + 0xAA95: 1087, + 0xAA97: 1088, + 0xAB41: 1089, + 0xAB57: 1090, + 0xAB61: 1091, + 0xAB65: 1092, + 0xAB69: 1093, + 0xAB71: 1094, + 0xAB73: 1095, + 0xABA1: 1096, + 0xABA2: 1097, + 0xABA5: 1098, + 0xABA9: 1099, + 0xABB1: 1100, + 0xABB3: 1101, + 0xABB5: 1102, + 0xABB7: 1103, + 0xAC61: 1104, + 0xAC62: 1105, + 0xAC64: 1106, + 0xAC65: 1107, + 0xAC68: 1108, + 0xAC69: 1109, + 0xAC6A: 1110, + 0xAC6B: 1111, + 0xAC71: 1112, + 0xAC73: 1113, + 0xAC75: 1114, + 0xAC76: 1115, + 0xAC77: 1116, + 0xAC7B: 1117, + 0xAC81: 1118, + 0xAC82: 1119, + 0xAC85: 1120, + 0xAC89: 1121, + 0xAC91: 1122, + 0xAC93: 1123, + 0xAC95: 1124, + 0xAC96: 1125, + 0xAC97: 1126, + 0xACA1: 1127, + 0xACA2: 1128, + 0xACA5: 1129, + 0xACA9: 1130, + 0xACB1: 1131, + 0xACB3: 1132, + 0xACB5: 1133, + 0xACB7: 1134, + 0xACC1: 1135, + 0xACC5: 1136, + 0xACC9: 1137, + 0xACD1: 1138, + 0xACD7: 1139, + 0xACE1: 1140, + 0xACE2: 1141, + 0xACE3: 1142, + 0xACE4: 1143, + 0xACE5: 1144, + 0xACE8: 1145, + 0xACE9: 1146, + 0xACEB: 1147, + 0xACEC: 1148, + 0xACF1: 1149, + 0xACF3: 1150, + 0xACF5: 1151, + 0xACF6: 1152, + 0xACF7: 1153, + 0xACFC: 1154, + 0xAD41: 1155, + 0xAD42: 1156, + 0xAD45: 1157, + 0xAD49: 1158, + 0xAD51: 1159, + 0xAD53: 1160, + 0xAD55: 1161, + 0xAD56: 1162, + 0xAD57: 1163, + 0xAD61: 1164, + 0xAD62: 1165, + 0xAD65: 1166, + 0xAD69: 1167, + 0xAD71: 1168, + 0xAD73: 1169, + 0xAD75: 1170, + 0xAD76: 1171, + 0xAD77: 1172, + 0xAD81: 1173, + 0xAD85: 1174, + 0xAD89: 1175, + 0xAD97: 1176, + 0xADA1: 1177, + 0xADA2: 1178, + 0xADA3: 1179, + 0xADA5: 1180, + 0xADA9: 1181, + 0xADAB: 1182, + 0xADB1: 1183, + 0xADB3: 1184, + 0xADB5: 1185, + 0xADB7: 1186, + 0xADBB: 1187, + 0xADC1: 1188, + 0xADC2: 1189, + 0xADC5: 1190, + 0xADC9: 1191, + 0xADD7: 1192, + 0xADE1: 1193, + 0xADE5: 1194, + 0xADE9: 1195, + 0xADF1: 1196, + 0xADF5: 1197, + 0xADF6: 1198, + 0xAE41: 1199, + 0xAE45: 1200, + 0xAE49: 1201, + 0xAE51: 1202, + 0xAE53: 1203, + 0xAE55: 1204, + 0xAE61: 1205, + 0xAE62: 1206, + 0xAE65: 1207, + 0xAE69: 1208, + 0xAE71: 1209, + 0xAE73: 1210, + 0xAE75: 1211, + 0xAE77: 1212, + 0xAE81: 1213, + 0xAE82: 1214, + 0xAE85: 1215, + 0xAE88: 1216, + 0xAE89: 1217, + 0xAE91: 1218, + 0xAE93: 1219, + 0xAE95: 1220, + 0xAE97: 1221, + 0xAE99: 1222, + 0xAE9B: 1223, + 0xAE9C: 1224, + 0xAEA1: 1225, + 0xAEB6: 1226, + 0xAEC1: 1227, + 0xAEC2: 1228, + 0xAEC5: 1229, + 0xAEC9: 1230, + 0xAED1: 1231, + 0xAED7: 1232, + 0xAEE1: 1233, + 0xAEE2: 1234, + 0xAEE5: 1235, + 0xAEE9: 1236, + 0xAEF1: 1237, + 0xAEF3: 1238, + 0xAEF5: 1239, + 0xAEF7: 1240, + 0xAF41: 1241, + 0xAF42: 1242, + 0xAF49: 1243, + 0xAF51: 1244, + 0xAF55: 1245, + 0xAF57: 1246, + 0xAF61: 1247, + 0xAF62: 1248, + 0xAF65: 1249, + 0xAF69: 1250, + 0xAF6A: 1251, + 0xAF71: 1252, + 0xAF73: 1253, + 0xAF75: 1254, + 0xAF77: 1255, + 0xAFA1: 1256, + 0xAFA2: 1257, + 0xAFA5: 1258, + 0xAFA8: 1259, + 0xAFA9: 1260, + 0xAFB0: 1261, + 0xAFB1: 1262, + 0xAFB3: 1263, + 0xAFB5: 1264, + 0xAFB7: 1265, + 0xAFBC: 1266, + 0xB061: 1267, + 0xB062: 1268, + 0xB064: 1269, + 0xB065: 1270, + 0xB069: 1271, + 0xB071: 1272, + 0xB073: 1273, + 0xB076: 1274, + 0xB077: 1275, + 0xB07D: 1276, + 0xB081: 1277, + 0xB082: 1278, + 0xB085: 1279, + 0xB089: 1280, + 0xB091: 1281, + 0xB093: 1282, + 0xB096: 1283, + 0xB097: 1284, + 0xB0B7: 1285, + 0xB0E1: 1286, + 0xB0E2: 1287, + 0xB0E5: 1288, + 0xB0E9: 1289, + 0xB0EB: 1290, + 0xB0F1: 1291, + 0xB0F3: 1292, + 0xB0F6: 1293, + 0xB0F7: 1294, + 0xB141: 1295, + 0xB145: 1296, + 0xB149: 1297, + 0xB185: 1298, + 0xB1A1: 1299, + 0xB1A2: 1300, + 0xB1A5: 1301, + 0xB1A8: 1302, + 0xB1A9: 1303, + 0xB1AB: 1304, + 0xB1B1: 1305, + 0xB1B3: 1306, + 0xB1B7: 1307, + 0xB1C1: 1308, + 0xB1C2: 1309, + 0xB1C5: 1310, + 0xB1D6: 1311, + 0xB1E1: 1312, + 0xB1F6: 1313, + 0xB241: 1314, + 0xB245: 1315, + 0xB249: 1316, + 0xB251: 1317, + 0xB253: 1318, + 0xB261: 1319, + 0xB281: 1320, + 0xB282: 1321, + 0xB285: 1322, + 0xB289: 1323, + 0xB291: 1324, + 0xB293: 1325, + 0xB297: 1326, + 0xB2A1: 1327, + 0xB2B6: 1328, + 0xB2C1: 1329, + 0xB2E1: 1330, + 0xB2E5: 1331, + 0xB357: 1332, + 0xB361: 1333, + 0xB362: 1334, + 0xB365: 1335, + 0xB369: 1336, + 0xB36B: 1337, + 0xB370: 1338, + 0xB371: 1339, + 0xB373: 1340, + 0xB381: 1341, + 0xB385: 1342, + 0xB389: 1343, + 0xB391: 1344, + 0xB3A1: 1345, + 0xB3A2: 1346, + 0xB3A5: 1347, + 0xB3A9: 1348, + 0xB3B1: 1349, + 0xB3B3: 1350, + 0xB3B5: 1351, + 0xB3B7: 1352, + 0xB461: 1353, + 0xB462: 1354, + 0xB465: 1355, + 0xB466: 1356, + 0xB467: 1357, + 0xB469: 1358, + 0xB46A: 1359, + 0xB46B: 1360, + 0xB470: 1361, + 0xB471: 1362, + 0xB473: 1363, + 0xB475: 1364, + 0xB476: 1365, + 0xB477: 1366, + 0xB47B: 1367, + 0xB47C: 1368, + 0xB481: 1369, + 0xB482: 1370, + 0xB485: 1371, + 0xB489: 1372, + 0xB491: 1373, + 0xB493: 1374, + 0xB495: 1375, + 0xB496: 1376, + 0xB497: 1377, + 0xB4A1: 1378, + 0xB4A2: 1379, + 0xB4A5: 1380, + 0xB4A9: 1381, + 0xB4AC: 1382, + 0xB4B1: 1383, + 0xB4B3: 1384, + 0xB4B5: 1385, + 0xB4B7: 1386, + 0xB4BB: 1387, + 0xB4BD: 1388, + 0xB4C1: 1389, + 0xB4C5: 1390, + 0xB4C9: 1391, + 0xB4D3: 1392, + 0xB4E1: 1393, + 0xB4E2: 1394, + 0xB4E5: 1395, + 0xB4E6: 1396, + 0xB4E8: 1397, + 0xB4E9: 1398, + 0xB4EA: 1399, + 0xB4EB: 1400, + 0xB4F1: 1401, + 0xB4F3: 1402, + 0xB4F4: 1403, + 0xB4F5: 1404, + 0xB4F6: 1405, + 0xB4F7: 1406, + 0xB4F8: 1407, + 0xB4FA: 1408, + 0xB4FC: 1409, + 0xB541: 1410, + 0xB542: 1411, + 0xB545: 1412, + 0xB549: 1413, + 0xB551: 1414, + 0xB553: 1415, + 0xB555: 1416, + 0xB557: 1417, + 0xB561: 1418, + 0xB562: 1419, + 0xB563: 1420, + 0xB565: 1421, + 0xB569: 1422, + 0xB56B: 1423, + 0xB56C: 1424, + 0xB571: 1425, + 0xB573: 1426, + 0xB574: 1427, + 0xB575: 1428, + 0xB576: 1429, + 0xB577: 1430, + 0xB57B: 1431, + 0xB57C: 1432, + 0xB57D: 1433, + 0xB581: 1434, + 0xB585: 1435, + 0xB589: 1436, + 0xB591: 1437, + 0xB593: 1438, + 0xB595: 1439, + 0xB596: 1440, + 0xB5A1: 1441, + 0xB5A2: 1442, + 0xB5A5: 1443, + 0xB5A9: 1444, + 0xB5AA: 1445, + 0xB5AB: 1446, + 0xB5AD: 1447, + 0xB5B0: 1448, + 0xB5B1: 1449, + 0xB5B3: 1450, + 0xB5B5: 1451, + 0xB5B7: 1452, + 0xB5B9: 1453, + 0xB5C1: 1454, + 0xB5C2: 1455, + 0xB5C5: 1456, + 0xB5C9: 1457, + 0xB5D1: 1458, + 0xB5D3: 1459, + 0xB5D5: 1460, + 0xB5D6: 1461, + 0xB5D7: 1462, + 0xB5E1: 1463, + 0xB5E2: 1464, + 0xB5E5: 1465, + 0xB5F1: 1466, + 0xB5F5: 1467, + 0xB5F7: 1468, + 0xB641: 1469, + 0xB642: 1470, + 0xB645: 1471, + 0xB649: 1472, + 0xB651: 1473, + 0xB653: 1474, + 0xB655: 1475, + 0xB657: 1476, + 0xB661: 1477, + 0xB662: 1478, + 0xB665: 1479, + 0xB669: 1480, + 0xB671: 1481, + 0xB673: 1482, + 0xB675: 1483, + 0xB677: 1484, + 0xB681: 1485, + 0xB682: 1486, + 0xB685: 1487, + 0xB689: 1488, + 0xB68A: 1489, + 0xB68B: 1490, + 0xB691: 1491, + 0xB693: 1492, + 0xB695: 1493, + 0xB697: 1494, + 0xB6A1: 1495, + 0xB6A2: 1496, + 0xB6A5: 1497, + 0xB6A9: 1498, + 0xB6B1: 1499, + 0xB6B3: 1500, + 0xB6B6: 1501, + 0xB6B7: 1502, + 0xB6C1: 1503, + 0xB6C2: 1504, + 0xB6C5: 1505, + 0xB6C9: 1506, + 0xB6D1: 1507, + 0xB6D3: 1508, + 0xB6D7: 1509, + 0xB6E1: 1510, + 0xB6E2: 1511, + 0xB6E5: 1512, + 0xB6E9: 1513, + 0xB6F1: 1514, + 0xB6F3: 1515, + 0xB6F5: 1516, + 0xB6F7: 1517, + 0xB741: 1518, + 0xB742: 1519, + 0xB745: 1520, + 0xB749: 1521, + 0xB751: 1522, + 0xB753: 1523, + 0xB755: 1524, + 0xB757: 1525, + 0xB759: 1526, + 0xB761: 1527, + 0xB762: 1528, + 0xB765: 1529, + 0xB769: 1530, + 0xB76F: 1531, + 0xB771: 1532, + 0xB773: 1533, + 0xB775: 1534, + 0xB777: 1535, + 0xB778: 1536, + 0xB779: 1537, + 0xB77A: 1538, + 0xB77B: 1539, + 0xB77C: 1540, + 0xB77D: 1541, + 0xB781: 1542, + 0xB785: 1543, + 0xB789: 1544, + 0xB791: 1545, + 0xB795: 1546, + 0xB7A1: 1547, + 0xB7A2: 1548, + 0xB7A5: 1549, + 0xB7A9: 1550, + 0xB7AA: 1551, + 0xB7AB: 1552, + 0xB7B0: 1553, + 0xB7B1: 1554, + 0xB7B3: 1555, + 0xB7B5: 1556, + 0xB7B6: 1557, + 0xB7B7: 1558, + 0xB7B8: 1559, + 0xB7BC: 1560, + 0xB861: 1561, + 0xB862: 1562, + 0xB865: 1563, + 0xB867: 1564, + 0xB868: 1565, + 0xB869: 1566, + 0xB86B: 1567, + 0xB871: 1568, + 0xB873: 1569, + 0xB875: 1570, + 0xB876: 1571, + 0xB877: 1572, + 0xB878: 1573, + 0xB881: 1574, + 0xB882: 1575, + 0xB885: 1576, + 0xB889: 1577, + 0xB891: 1578, + 0xB893: 1579, + 0xB895: 1580, + 0xB896: 1581, + 0xB897: 1582, + 0xB8A1: 1583, + 0xB8A2: 1584, + 0xB8A5: 1585, + 0xB8A7: 1586, + 0xB8A9: 1587, + 0xB8B1: 1588, + 0xB8B7: 1589, + 0xB8C1: 1590, + 0xB8C5: 1591, + 0xB8C9: 1592, + 0xB8E1: 1593, + 0xB8E2: 1594, + 0xB8E5: 1595, + 0xB8E9: 1596, + 0xB8EB: 1597, + 0xB8F1: 1598, + 0xB8F3: 1599, + 0xB8F5: 1600, + 0xB8F7: 1601, + 0xB8F8: 1602, + 0xB941: 1603, + 0xB942: 1604, + 0xB945: 1605, + 0xB949: 1606, + 0xB951: 1607, + 0xB953: 1608, + 0xB955: 1609, + 0xB957: 1610, + 0xB961: 1611, + 0xB965: 1612, + 0xB969: 1613, + 0xB971: 1614, + 0xB973: 1615, + 0xB976: 1616, + 0xB977: 1617, + 0xB981: 1618, + 0xB9A1: 1619, + 0xB9A2: 1620, + 0xB9A5: 1621, + 0xB9A9: 1622, + 0xB9AB: 1623, + 0xB9B1: 1624, + 0xB9B3: 1625, + 0xB9B5: 1626, + 0xB9B7: 1627, + 0xB9B8: 1628, + 0xB9B9: 1629, + 0xB9BD: 1630, + 0xB9C1: 1631, + 0xB9C2: 1632, + 0xB9C9: 1633, + 0xB9D3: 1634, + 0xB9D5: 1635, + 0xB9D7: 1636, + 0xB9E1: 1637, + 0xB9F6: 1638, + 0xB9F7: 1639, + 0xBA41: 1640, + 0xBA45: 1641, + 0xBA49: 1642, + 0xBA51: 1643, + 0xBA53: 1644, + 0xBA55: 1645, + 0xBA57: 1646, + 0xBA61: 1647, + 0xBA62: 1648, + 0xBA65: 1649, + 0xBA77: 1650, + 0xBA81: 1651, + 0xBA82: 1652, + 0xBA85: 1653, + 0xBA89: 1654, + 0xBA8A: 1655, + 0xBA8B: 1656, + 0xBA91: 1657, + 0xBA93: 1658, + 0xBA95: 1659, + 0xBA97: 1660, + 0xBAA1: 1661, + 0xBAB6: 1662, + 0xBAC1: 1663, + 0xBAE1: 1664, + 0xBAE2: 1665, + 0xBAE5: 1666, + 0xBAE9: 1667, + 0xBAF1: 1668, + 0xBAF3: 1669, + 0xBAF5: 1670, + 0xBB41: 1671, + 0xBB45: 1672, + 0xBB49: 1673, + 0xBB51: 1674, + 0xBB61: 1675, + 0xBB62: 1676, + 0xBB65: 1677, + 0xBB69: 1678, + 0xBB71: 1679, + 0xBB73: 1680, + 0xBB75: 1681, + 0xBB77: 1682, + 0xBBA1: 1683, + 0xBBA2: 1684, + 0xBBA5: 1685, + 0xBBA8: 1686, + 0xBBA9: 1687, + 0xBBAB: 1688, + 0xBBB1: 1689, + 0xBBB3: 1690, + 0xBBB5: 1691, + 0xBBB7: 1692, + 0xBBB8: 1693, + 0xBBBB: 1694, + 0xBBBC: 1695, + 0xBC61: 1696, + 0xBC62: 1697, + 0xBC65: 1698, + 0xBC67: 1699, + 0xBC69: 1700, + 0xBC6C: 1701, + 0xBC71: 1702, + 0xBC73: 1703, + 0xBC75: 1704, + 0xBC76: 1705, + 0xBC77: 1706, + 0xBC81: 1707, + 0xBC82: 1708, + 0xBC85: 1709, + 0xBC89: 1710, + 0xBC91: 1711, + 0xBC93: 1712, + 0xBC95: 1713, + 0xBC96: 1714, + 0xBC97: 1715, + 0xBCA1: 1716, + 0xBCA5: 1717, + 0xBCB7: 1718, + 0xBCE1: 1719, + 0xBCE2: 1720, + 0xBCE5: 1721, + 0xBCE9: 1722, + 0xBCF1: 1723, + 0xBCF3: 1724, + 0xBCF5: 1725, + 0xBCF6: 1726, + 0xBCF7: 1727, + 0xBD41: 1728, + 0xBD57: 1729, + 0xBD61: 1730, + 0xBD76: 1731, + 0xBDA1: 1732, + 0xBDA2: 1733, + 0xBDA5: 1734, + 0xBDA9: 1735, + 0xBDB1: 1736, + 0xBDB3: 1737, + 0xBDB5: 1738, + 0xBDB7: 1739, + 0xBDB9: 1740, + 0xBDC1: 1741, + 0xBDC2: 1742, + 0xBDC9: 1743, + 0xBDD6: 1744, + 0xBDE1: 1745, + 0xBDF6: 1746, + 0xBE41: 1747, + 0xBE45: 1748, + 0xBE49: 1749, + 0xBE51: 1750, + 0xBE53: 1751, + 0xBE77: 1752, + 0xBE81: 1753, + 0xBE82: 1754, + 0xBE85: 1755, + 0xBE89: 1756, + 0xBE91: 1757, + 0xBE93: 1758, + 0xBE97: 1759, + 0xBEA1: 1760, + 0xBEB6: 1761, + 0xBEB7: 1762, + 0xBEE1: 1763, + 0xBF41: 1764, + 0xBF61: 1765, + 0xBF71: 1766, + 0xBF75: 1767, + 0xBF77: 1768, + 0xBFA1: 1769, + 0xBFA2: 1770, + 0xBFA5: 1771, + 0xBFA9: 1772, + 0xBFB1: 1773, + 0xBFB3: 1774, + 0xBFB7: 1775, + 0xBFB8: 1776, + 0xBFBD: 1777, + 0xC061: 1778, + 0xC062: 1779, + 0xC065: 1780, + 0xC067: 1781, + 0xC069: 1782, + 0xC071: 1783, + 0xC073: 1784, + 0xC075: 1785, + 0xC076: 1786, + 0xC077: 1787, + 0xC078: 1788, + 0xC081: 1789, + 0xC082: 1790, + 0xC085: 1791, + 0xC089: 1792, + 0xC091: 1793, + 0xC093: 1794, + 0xC095: 1795, + 0xC096: 1796, + 0xC097: 1797, + 0xC0A1: 1798, + 0xC0A5: 1799, + 0xC0A7: 1800, + 0xC0A9: 1801, + 0xC0B1: 1802, + 0xC0B7: 1803, + 0xC0E1: 1804, + 0xC0E2: 1805, + 0xC0E5: 1806, + 0xC0E9: 1807, + 0xC0F1: 1808, + 0xC0F3: 1809, + 0xC0F5: 1810, + 0xC0F6: 1811, + 0xC0F7: 1812, + 0xC141: 1813, + 0xC142: 1814, + 0xC145: 1815, + 0xC149: 1816, + 0xC151: 1817, + 0xC153: 1818, + 0xC155: 1819, + 0xC157: 1820, + 0xC161: 1821, + 0xC165: 1822, + 0xC176: 1823, + 0xC181: 1824, + 0xC185: 1825, + 0xC197: 1826, + 0xC1A1: 1827, + 0xC1A2: 1828, + 0xC1A5: 1829, + 0xC1A9: 1830, + 0xC1B1: 1831, + 0xC1B3: 1832, + 0xC1B5: 1833, + 0xC1B7: 1834, + 0xC1C1: 1835, + 0xC1C5: 1836, + 0xC1C9: 1837, + 0xC1D7: 1838, + 0xC241: 1839, + 0xC245: 1840, + 0xC249: 1841, + 0xC251: 1842, + 0xC253: 1843, + 0xC255: 1844, + 0xC257: 1845, + 0xC261: 1846, + 0xC271: 1847, + 0xC281: 1848, + 0xC282: 1849, + 0xC285: 1850, + 0xC289: 1851, + 0xC291: 1852, + 0xC293: 1853, + 0xC295: 1854, + 0xC297: 1855, + 0xC2A1: 1856, + 0xC2B6: 1857, + 0xC2C1: 1858, + 0xC2C5: 1859, + 0xC2E1: 1860, + 0xC2E5: 1861, + 0xC2E9: 1862, + 0xC2F1: 1863, + 0xC2F3: 1864, + 0xC2F5: 1865, + 0xC2F7: 1866, + 0xC341: 1867, + 0xC345: 1868, + 0xC349: 1869, + 0xC351: 1870, + 0xC357: 1871, + 0xC361: 1872, + 0xC362: 1873, + 0xC365: 1874, + 0xC369: 1875, + 0xC371: 1876, + 0xC373: 1877, + 0xC375: 1878, + 0xC377: 1879, + 0xC3A1: 1880, + 0xC3A2: 1881, + 0xC3A5: 1882, + 0xC3A8: 1883, + 0xC3A9: 1884, + 0xC3AA: 1885, + 0xC3B1: 1886, + 0xC3B3: 1887, + 0xC3B5: 1888, + 0xC3B7: 1889, + 0xC461: 1890, + 0xC462: 1891, + 0xC465: 1892, + 0xC469: 1893, + 0xC471: 1894, + 0xC473: 1895, + 0xC475: 1896, + 0xC477: 1897, + 0xC481: 1898, + 0xC482: 1899, + 0xC485: 1900, + 0xC489: 1901, + 0xC491: 1902, + 0xC493: 1903, + 0xC495: 1904, + 0xC496: 1905, + 0xC497: 1906, + 0xC4A1: 1907, + 0xC4A2: 1908, + 0xC4B7: 1909, + 0xC4E1: 1910, + 0xC4E2: 1911, + 0xC4E5: 1912, + 0xC4E8: 1913, + 0xC4E9: 1914, + 0xC4F1: 1915, + 0xC4F3: 1916, + 0xC4F5: 1917, + 0xC4F6: 1918, + 0xC4F7: 1919, + 0xC541: 1920, + 0xC542: 1921, + 0xC545: 1922, + 0xC549: 1923, + 0xC551: 1924, + 0xC553: 1925, + 0xC555: 1926, + 0xC557: 1927, + 0xC561: 1928, + 0xC565: 1929, + 0xC569: 1930, + 0xC571: 1931, + 0xC573: 1932, + 0xC575: 1933, + 0xC576: 1934, + 0xC577: 1935, + 0xC581: 1936, + 0xC5A1: 1937, + 0xC5A2: 1938, + 0xC5A5: 1939, + 0xC5A9: 1940, + 0xC5B1: 1941, + 0xC5B3: 1942, + 0xC5B5: 1943, + 0xC5B7: 1944, + 0xC5C1: 1945, + 0xC5C2: 1946, + 0xC5C5: 1947, + 0xC5C9: 1948, + 0xC5D1: 1949, + 0xC5D7: 1950, + 0xC5E1: 1951, + 0xC5F7: 1952, + 0xC641: 1953, + 0xC649: 1954, + 0xC661: 1955, + 0xC681: 1956, + 0xC682: 1957, + 0xC685: 1958, + 0xC689: 1959, + 0xC691: 1960, + 0xC693: 1961, + 0xC695: 1962, + 0xC697: 1963, + 0xC6A1: 1964, + 0xC6A5: 1965, + 0xC6A9: 1966, + 0xC6B7: 1967, + 0xC6C1: 1968, + 0xC6D7: 1969, + 0xC6E1: 1970, + 0xC6E2: 1971, + 0xC6E5: 1972, + 0xC6E9: 1973, + 0xC6F1: 1974, + 0xC6F3: 1975, + 0xC6F5: 1976, + 0xC6F7: 1977, + 0xC741: 1978, + 0xC745: 1979, + 0xC749: 1980, + 0xC751: 1981, + 0xC761: 1982, + 0xC762: 1983, + 0xC765: 1984, + 0xC769: 1985, + 0xC771: 1986, + 0xC773: 1987, + 0xC777: 1988, + 0xC7A1: 1989, + 0xC7A2: 1990, + 0xC7A5: 1991, + 0xC7A9: 1992, + 0xC7B1: 1993, + 0xC7B3: 1994, + 0xC7B5: 1995, + 0xC7B7: 1996, + 0xC861: 1997, + 0xC862: 1998, + 0xC865: 1999, + 0xC869: 2000, + 0xC86A: 2001, + 0xC871: 2002, + 0xC873: 2003, + 0xC875: 2004, + 0xC876: 2005, + 0xC877: 2006, + 0xC881: 2007, + 0xC882: 2008, + 0xC885: 2009, + 0xC889: 2010, + 0xC891: 2011, + 0xC893: 2012, + 0xC895: 2013, + 0xC896: 2014, + 0xC897: 2015, + 0xC8A1: 2016, + 0xC8B7: 2017, + 0xC8E1: 2018, + 0xC8E2: 2019, + 0xC8E5: 2020, + 0xC8E9: 2021, + 0xC8EB: 2022, + 0xC8F1: 2023, + 0xC8F3: 2024, + 0xC8F5: 2025, + 0xC8F6: 2026, + 0xC8F7: 2027, + 0xC941: 2028, + 0xC942: 2029, + 0xC945: 2030, + 0xC949: 2031, + 0xC951: 2032, + 0xC953: 2033, + 0xC955: 2034, + 0xC957: 2035, + 0xC961: 2036, + 0xC965: 2037, + 0xC976: 2038, + 0xC981: 2039, + 0xC985: 2040, + 0xC9A1: 2041, + 0xC9A2: 2042, + 0xC9A5: 2043, + 0xC9A9: 2044, + 0xC9B1: 2045, + 0xC9B3: 2046, + 0xC9B5: 2047, + 0xC9B7: 2048, + 0xC9BC: 2049, + 0xC9C1: 2050, + 0xC9C5: 2051, + 0xC9E1: 2052, + 0xCA41: 2053, + 0xCA45: 2054, + 0xCA55: 2055, + 0xCA57: 2056, + 0xCA61: 2057, + 0xCA81: 2058, + 0xCA82: 2059, + 0xCA85: 2060, + 0xCA89: 2061, + 0xCA91: 2062, + 0xCA93: 2063, + 0xCA95: 2064, + 0xCA97: 2065, + 0xCAA1: 2066, + 0xCAB6: 2067, + 0xCAC1: 2068, + 0xCAE1: 2069, + 0xCAE2: 2070, + 0xCAE5: 2071, + 0xCAE9: 2072, + 0xCAF1: 2073, + 0xCAF3: 2074, + 0xCAF7: 2075, + 0xCB41: 2076, + 0xCB45: 2077, + 0xCB49: 2078, + 0xCB51: 2079, + 0xCB57: 2080, + 0xCB61: 2081, + 0xCB62: 2082, + 0xCB65: 2083, + 0xCB68: 2084, + 0xCB69: 2085, + 0xCB6B: 2086, + 0xCB71: 2087, + 0xCB73: 2088, + 0xCB75: 2089, + 0xCB81: 2090, + 0xCB85: 2091, + 0xCB89: 2092, + 0xCB91: 2093, + 0xCB93: 2094, + 0xCBA1: 2095, + 0xCBA2: 2096, + 0xCBA5: 2097, + 0xCBA9: 2098, + 0xCBB1: 2099, + 0xCBB3: 2100, + 0xCBB5: 2101, + 0xCBB7: 2102, + 0xCC61: 2103, + 0xCC62: 2104, + 0xCC63: 2105, + 0xCC65: 2106, + 0xCC69: 2107, + 0xCC6B: 2108, + 0xCC71: 2109, + 0xCC73: 2110, + 0xCC75: 2111, + 0xCC76: 2112, + 0xCC77: 2113, + 0xCC7B: 2114, + 0xCC81: 2115, + 0xCC82: 2116, + 0xCC85: 2117, + 0xCC89: 2118, + 0xCC91: 2119, + 0xCC93: 2120, + 0xCC95: 2121, + 0xCC96: 2122, + 0xCC97: 2123, + 0xCCA1: 2124, + 0xCCA2: 2125, + 0xCCE1: 2126, + 0xCCE2: 2127, + 0xCCE5: 2128, + 0xCCE9: 2129, + 0xCCF1: 2130, + 0xCCF3: 2131, + 0xCCF5: 2132, + 0xCCF6: 2133, + 0xCCF7: 2134, + 0xCD41: 2135, + 0xCD42: 2136, + 0xCD45: 2137, + 0xCD49: 2138, + 0xCD51: 2139, + 0xCD53: 2140, + 0xCD55: 2141, + 0xCD57: 2142, + 0xCD61: 2143, + 0xCD65: 2144, + 0xCD69: 2145, + 0xCD71: 2146, + 0xCD73: 2147, + 0xCD76: 2148, + 0xCD77: 2149, + 0xCD81: 2150, + 0xCD89: 2151, + 0xCD93: 2152, + 0xCD95: 2153, + 0xCDA1: 2154, + 0xCDA2: 2155, + 0xCDA5: 2156, + 0xCDA9: 2157, + 0xCDB1: 2158, + 0xCDB3: 2159, + 0xCDB5: 2160, + 0xCDB7: 2161, + 0xCDC1: 2162, + 0xCDD7: 2163, + 0xCE41: 2164, + 0xCE45: 2165, + 0xCE61: 2166, + 0xCE65: 2167, + 0xCE69: 2168, + 0xCE73: 2169, + 0xCE75: 2170, + 0xCE81: 2171, + 0xCE82: 2172, + 0xCE85: 2173, + 0xCE88: 2174, + 0xCE89: 2175, + 0xCE8B: 2176, + 0xCE91: 2177, + 0xCE93: 2178, + 0xCE95: 2179, + 0xCE97: 2180, + 0xCEA1: 2181, + 0xCEB7: 2182, + 0xCEE1: 2183, + 0xCEE5: 2184, + 0xCEE9: 2185, + 0xCEF1: 2186, + 0xCEF5: 2187, + 0xCF41: 2188, + 0xCF45: 2189, + 0xCF49: 2190, + 0xCF51: 2191, + 0xCF55: 2192, + 0xCF57: 2193, + 0xCF61: 2194, + 0xCF65: 2195, + 0xCF69: 2196, + 0xCF71: 2197, + 0xCF73: 2198, + 0xCF75: 2199, + 0xCFA1: 2200, + 0xCFA2: 2201, + 0xCFA5: 2202, + 0xCFA9: 2203, + 0xCFB1: 2204, + 0xCFB3: 2205, + 0xCFB5: 2206, + 0xCFB7: 2207, + 0xD061: 2208, + 0xD062: 2209, + 0xD065: 2210, + 0xD069: 2211, + 0xD06E: 2212, + 0xD071: 2213, + 0xD073: 2214, + 0xD075: 2215, + 0xD077: 2216, + 0xD081: 2217, + 0xD082: 2218, + 0xD085: 2219, + 0xD089: 2220, + 0xD091: 2221, + 0xD093: 2222, + 0xD095: 2223, + 0xD096: 2224, + 0xD097: 2225, + 0xD0A1: 2226, + 0xD0B7: 2227, + 0xD0E1: 2228, + 0xD0E2: 2229, + 0xD0E5: 2230, + 0xD0E9: 2231, + 0xD0EB: 2232, + 0xD0F1: 2233, + 0xD0F3: 2234, + 0xD0F5: 2235, + 0xD0F7: 2236, + 0xD141: 2237, + 0xD142: 2238, + 0xD145: 2239, + 0xD149: 2240, + 0xD151: 2241, + 0xD153: 2242, + 0xD155: 2243, + 0xD157: 2244, + 0xD161: 2245, + 0xD162: 2246, + 0xD165: 2247, + 0xD169: 2248, + 0xD171: 2249, + 0xD173: 2250, + 0xD175: 2251, + 0xD176: 2252, + 0xD177: 2253, + 0xD181: 2254, + 0xD185: 2255, + 0xD189: 2256, + 0xD193: 2257, + 0xD1A1: 2258, + 0xD1A2: 2259, + 0xD1A5: 2260, + 0xD1A9: 2261, + 0xD1AE: 2262, + 0xD1B1: 2263, + 0xD1B3: 2264, + 0xD1B5: 2265, + 0xD1B7: 2266, + 0xD1BB: 2267, + 0xD1C1: 2268, + 0xD1C2: 2269, + 0xD1C5: 2270, + 0xD1C9: 2271, + 0xD1D5: 2272, + 0xD1D7: 2273, + 0xD1E1: 2274, + 0xD1E2: 2275, + 0xD1E5: 2276, + 0xD1F5: 2277, + 0xD1F7: 2278, + 0xD241: 2279, + 0xD242: 2280, + 0xD245: 2281, + 0xD249: 2282, + 0xD253: 2283, + 0xD255: 2284, + 0xD257: 2285, + 0xD261: 2286, + 0xD265: 2287, + 0xD269: 2288, + 0xD273: 2289, + 0xD275: 2290, + 0xD281: 2291, + 0xD282: 2292, + 0xD285: 2293, + 0xD289: 2294, + 0xD28E: 2295, + 0xD291: 2296, + 0xD295: 2297, + 0xD297: 2298, + 0xD2A1: 2299, + 0xD2A5: 2300, + 0xD2A9: 2301, + 0xD2B1: 2302, + 0xD2B7: 2303, + 0xD2C1: 2304, + 0xD2C2: 2305, + 0xD2C5: 2306, + 0xD2C9: 2307, + 0xD2D7: 2308, + 0xD2E1: 2309, + 0xD2E2: 2310, + 0xD2E5: 2311, + 0xD2E9: 2312, + 0xD2F1: 2313, + 0xD2F3: 2314, + 0xD2F5: 2315, + 0xD2F7: 2316, + 0xD341: 2317, + 0xD342: 2318, + 0xD345: 2319, + 0xD349: 2320, + 0xD351: 2321, + 0xD355: 2322, + 0xD357: 2323, + 0xD361: 2324, + 0xD362: 2325, + 0xD365: 2326, + 0xD367: 2327, + 0xD368: 2328, + 0xD369: 2329, + 0xD36A: 2330, + 0xD371: 2331, + 0xD373: 2332, + 0xD375: 2333, + 0xD377: 2334, + 0xD37B: 2335, + 0xD381: 2336, + 0xD385: 2337, + 0xD389: 2338, + 0xD391: 2339, + 0xD393: 2340, + 0xD397: 2341, + 0xD3A1: 2342, + 0xD3A2: 2343, + 0xD3A5: 2344, + 0xD3A9: 2345, + 0xD3B1: 2346, + 0xD3B3: 2347, + 0xD3B5: 2348, + 0xD3B7: 2349, +} diff --git a/virt/lib/python3.9/site-packages/pip/_vendor/chardet/langgreekmodel 3.py b/virt/lib/python3.9/site-packages/pip/_vendor/chardet/langgreekmodel 3.py new file mode 100644 index 00000000..b02a31ef --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_vendor/chardet/langgreekmodel 3.py @@ -0,0 +1,4397 @@ +from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +GREEK_LANG_MODEL = { + 60: { # 'e' + 60: 2, # 'e' + 55: 1, # 'o' + 58: 2, # 't' + 36: 1, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 55: { # 'o' + 60: 0, # 'e' + 55: 2, # 'o' + 58: 2, # 't' + 36: 1, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 1, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 1, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 58: { # 't' + 60: 2, # 'e' + 55: 1, # 'o' + 58: 1, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 1, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 36: { # '·' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 61: { # 'Ά' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 1, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 1, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 46: { # 'Έ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 2, # 'β' + 20: 2, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 2, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 1, # 'σ' + 2: 2, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 54: { # 'Ό' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 2, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 31: { # 'Α' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 2, # 'Β' + 43: 2, # 'Γ' + 41: 1, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 2, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 2, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 1, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 2, # 'Υ' + 56: 2, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 2, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 1, # 'θ' + 5: 0, # 'ι' + 11: 2, # 'κ' + 16: 3, # 'λ' + 10: 2, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 2, # 'ς' + 7: 2, # 'σ' + 2: 0, # 'τ' + 12: 3, # 'υ' + 28: 2, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 51: { # 'Β' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 1, # 'Η' + 52: 0, # 'Θ' + 47: 1, # 'Ι' + 44: 0, # 'Κ' + 53: 1, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 2, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 43: { # 'Γ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 1, # 'Α' + 51: 0, # 'Β' + 43: 2, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 1, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 1, # 'Κ' + 53: 1, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 1, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 41: { # 'Δ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 2, # 'ή' + 15: 2, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 1, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 34: { # 'Ε' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 2, # 'Γ' + 41: 2, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 2, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 1, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 2, # 'Χ' + 57: 2, # 'Ω' + 17: 3, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 3, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 1, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 1, # 'θ' + 5: 2, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 2, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 2, # 'τ' + 12: 2, # 'υ' + 28: 2, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 1, # 'ύ' + 27: 0, # 'ώ' + }, + 40: { # 'Η' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 1, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 2, # 'Θ' + 47: 0, # 'Ι' + 44: 2, # 'Κ' + 53: 0, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 1, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 1, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 1, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 52: { # 'Θ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 1, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 1, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 47: { # 'Ι' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 1, # 'Β' + 43: 1, # 'Γ' + 41: 2, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 2, # 'Κ' + 53: 2, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 0, # 'Υ' + 56: 2, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 1, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 1, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 1, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 44: { # 'Κ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 1, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 1, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 1, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 1, # 'Ω' + 17: 3, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 53: { # 'Λ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 2, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 2, # 'Σ' + 33: 0, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 1, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 38: { # 'Μ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 2, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 2, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 2, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 2, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 49: { # 'Ν' + 60: 2, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 2, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 1, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 1, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 59: { # 'Ξ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 1, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 1, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 39: { # 'Ο' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 1, # 'Β' + 43: 2, # 'Γ' + 41: 2, # 'Δ' + 34: 2, # 'Ε' + 40: 1, # 'Η' + 52: 2, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 2, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 2, # 'Υ' + 56: 2, # 'Φ' + 50: 2, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 2, # 'κ' + 16: 2, # 'λ' + 10: 2, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 2, # 'τ' + 12: 2, # 'υ' + 28: 1, # 'φ' + 23: 1, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 35: { # 'Π' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 2, # 'Λ' + 38: 1, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 1, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 2, # 'Ω' + 17: 2, # 'ά' + 18: 1, # 'έ' + 22: 1, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 3, # 'ώ' + }, + 48: { # 'Ρ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 1, # 'Γ' + 41: 1, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 1, # 'Τ' + 45: 1, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 1, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 1, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 37: { # 'Σ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 1, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 0, # 'Λ' + 38: 2, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 2, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 2, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 2, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 2, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 2, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 33: { # 'Τ' + 60: 0, # 'e' + 55: 1, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 1, # 'Τ' + 45: 1, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 2, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 3, # 'ώ' + }, + 45: { # 'Υ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 2, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 2, # 'Η' + 52: 2, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 1, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 1, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 56: { # 'Φ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 1, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 1, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 2, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 1, # 'ύ' + 27: 1, # 'ώ' + }, + 50: { # 'Χ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 1, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 1, # 'Ν' + 59: 0, # 'Ξ' + 39: 1, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 1, # 'Ω' + 17: 2, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 2, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 57: { # 'Ω' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 1, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 1, # 'Λ' + 38: 0, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 2, # 'ς' + 7: 2, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 1, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 17: { # 'ά' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 3, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 2, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 3, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 18: { # 'έ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 3, # 'ε' + 32: 2, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 3, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 22: { # 'ή' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 1, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 15: { # 'ί' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 3, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 1, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 3, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 1: { # 'α' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 3, # 'ί' + 1: 0, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 2, # 'ε' + 32: 3, # 'ζ' + 13: 1, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 29: { # 'β' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 2, # 'γ' + 21: 2, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 3, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 20: { # 'γ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 3, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 3, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 2, # 'ύ' + 27: 3, # 'ώ' + }, + 21: { # 'δ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 3: { # 'ε' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 3, # 'ί' + 1: 2, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 2, # 'ε' + 32: 2, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 3, # 'ω' + 19: 2, # 'ό' + 26: 3, # 'ύ' + 27: 2, # 'ώ' + }, + 32: { # 'ζ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 2, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 1, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 2, # 'ώ' + }, + 13: { # 'η' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 25: { # 'θ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 1, # 'λ' + 10: 3, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 5: { # 'ι' + 60: 0, # 'e' + 55: 1, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 2, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 0, # 'ύ' + 27: 3, # 'ώ' + }, + 11: { # 'κ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 2, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 2, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 2, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 16: { # 'λ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 1, # 'β' + 20: 2, # 'γ' + 21: 1, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 2, # 'θ' + 5: 3, # 'ι' + 11: 2, # 'κ' + 16: 3, # 'λ' + 10: 2, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 2, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 10: { # 'μ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 3, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 3, # 'φ' + 23: 0, # 'χ' + 42: 2, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 6: { # 'ν' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 2, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 1, # 'λ' + 10: 0, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 30: { # 'ξ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 2, # 'ό' + 26: 3, # 'ύ' + 27: 1, # 'ώ' + }, + 4: { # 'ο' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 2, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 2, # 'ω' + 19: 1, # 'ό' + 26: 3, # 'ύ' + 27: 2, # 'ώ' + }, + 9: { # 'π' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 3, # 'λ' + 10: 0, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 2, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 2, # 'ύ' + 27: 3, # 'ώ' + }, + 8: { # 'ρ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 1, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 3, # 'ο' + 9: 2, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 14: { # 'ς' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 7: { # 'σ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 3, # 'β' + 20: 0, # 'γ' + 21: 2, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 2, # 'ώ' + }, + 2: { # 'τ' + 60: 0, # 'e' + 55: 2, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 2, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 2, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 2, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 12: { # 'υ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 2, # 'ε' + 32: 2, # 'ζ' + 13: 2, # 'η' + 25: 3, # 'θ' + 5: 2, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 2, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 2, # 'ώ' + }, + 28: { # 'φ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 2, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 1, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 1, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 23: { # 'χ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 2, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 2, # 'μ' + 6: 3, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 42: { # 'ψ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 1, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 2, # 'τ' + 12: 1, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 24: { # 'ω' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 1, # 'ά' + 18: 0, # 'έ' + 22: 2, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 19: { # 'ό' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 1, # 'ε' + 32: 2, # 'ζ' + 13: 2, # 'η' + 25: 2, # 'θ' + 5: 2, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 1, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 26: { # 'ύ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 2, # 'β' + 20: 2, # 'γ' + 21: 1, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 2, # 'χ' + 42: 2, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 27: { # 'ώ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 1, # 'β' + 20: 0, # 'γ' + 21: 3, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 1, # 'η' + 25: 2, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 1, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 1, # 'φ' + 23: 1, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, +} + +# 255: Undefined characters that did not exist in training text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 +# 251: Control characters + +# Character Mapping Table(s): +WINDOWS_1253_GREEK_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 82, # 'A' + 66: 100, # 'B' + 67: 104, # 'C' + 68: 94, # 'D' + 69: 98, # 'E' + 70: 101, # 'F' + 71: 116, # 'G' + 72: 102, # 'H' + 73: 111, # 'I' + 74: 187, # 'J' + 75: 117, # 'K' + 76: 92, # 'L' + 77: 88, # 'M' + 78: 113, # 'N' + 79: 85, # 'O' + 80: 79, # 'P' + 81: 118, # 'Q' + 82: 105, # 'R' + 83: 83, # 'S' + 84: 67, # 'T' + 85: 114, # 'U' + 86: 119, # 'V' + 87: 95, # 'W' + 88: 99, # 'X' + 89: 109, # 'Y' + 90: 188, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 72, # 'a' + 98: 70, # 'b' + 99: 80, # 'c' + 100: 81, # 'd' + 101: 60, # 'e' + 102: 96, # 'f' + 103: 93, # 'g' + 104: 89, # 'h' + 105: 68, # 'i' + 106: 120, # 'j' + 107: 97, # 'k' + 108: 77, # 'l' + 109: 86, # 'm' + 110: 69, # 'n' + 111: 55, # 'o' + 112: 78, # 'p' + 113: 115, # 'q' + 114: 65, # 'r' + 115: 66, # 's' + 116: 58, # 't' + 117: 76, # 'u' + 118: 106, # 'v' + 119: 103, # 'w' + 120: 87, # 'x' + 121: 107, # 'y' + 122: 112, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 255, # '€' + 129: 255, # None + 130: 255, # '‚' + 131: 255, # 'ƒ' + 132: 255, # '„' + 133: 255, # '…' + 134: 255, # '†' + 135: 255, # '‡' + 136: 255, # None + 137: 255, # '‰' + 138: 255, # None + 139: 255, # '‹' + 140: 255, # None + 141: 255, # None + 142: 255, # None + 143: 255, # None + 144: 255, # None + 145: 255, # '‘' + 146: 255, # '’' + 147: 255, # '“' + 148: 255, # '”' + 149: 255, # '•' + 150: 255, # '–' + 151: 255, # '—' + 152: 255, # None + 153: 255, # '™' + 154: 255, # None + 155: 255, # '›' + 156: 255, # None + 157: 255, # None + 158: 255, # None + 159: 255, # None + 160: 253, # '\xa0' + 161: 233, # '΅' + 162: 61, # 'Ά' + 163: 253, # '£' + 164: 253, # '¤' + 165: 253, # '¥' + 166: 253, # '¦' + 167: 253, # '§' + 168: 253, # '¨' + 169: 253, # '©' + 170: 253, # None + 171: 253, # '«' + 172: 253, # '¬' + 173: 74, # '\xad' + 174: 253, # '®' + 175: 253, # '―' + 176: 253, # '°' + 177: 253, # '±' + 178: 253, # '²' + 179: 253, # '³' + 180: 247, # '΄' + 181: 253, # 'µ' + 182: 253, # '¶' + 183: 36, # '·' + 184: 46, # 'Έ' + 185: 71, # 'Ή' + 186: 73, # 'Ί' + 187: 253, # '»' + 188: 54, # 'Ό' + 189: 253, # '½' + 190: 108, # 'Ύ' + 191: 123, # 'Ώ' + 192: 110, # 'ΐ' + 193: 31, # 'Α' + 194: 51, # 'Β' + 195: 43, # 'Γ' + 196: 41, # 'Δ' + 197: 34, # 'Ε' + 198: 91, # 'Ζ' + 199: 40, # 'Η' + 200: 52, # 'Θ' + 201: 47, # 'Ι' + 202: 44, # 'Κ' + 203: 53, # 'Λ' + 204: 38, # 'Μ' + 205: 49, # 'Ν' + 206: 59, # 'Ξ' + 207: 39, # 'Ο' + 208: 35, # 'Π' + 209: 48, # 'Ρ' + 210: 250, # None + 211: 37, # 'Σ' + 212: 33, # 'Τ' + 213: 45, # 'Υ' + 214: 56, # 'Φ' + 215: 50, # 'Χ' + 216: 84, # 'Ψ' + 217: 57, # 'Ω' + 218: 120, # 'Ϊ' + 219: 121, # 'Ϋ' + 220: 17, # 'ά' + 221: 18, # 'έ' + 222: 22, # 'ή' + 223: 15, # 'ί' + 224: 124, # 'ΰ' + 225: 1, # 'α' + 226: 29, # 'β' + 227: 20, # 'γ' + 228: 21, # 'δ' + 229: 3, # 'ε' + 230: 32, # 'ζ' + 231: 13, # 'η' + 232: 25, # 'θ' + 233: 5, # 'ι' + 234: 11, # 'κ' + 235: 16, # 'λ' + 236: 10, # 'μ' + 237: 6, # 'ν' + 238: 30, # 'ξ' + 239: 4, # 'ο' + 240: 9, # 'π' + 241: 8, # 'ρ' + 242: 14, # 'ς' + 243: 7, # 'σ' + 244: 2, # 'τ' + 245: 12, # 'υ' + 246: 28, # 'φ' + 247: 23, # 'χ' + 248: 42, # 'ψ' + 249: 24, # 'ω' + 250: 64, # 'ϊ' + 251: 75, # 'ϋ' + 252: 19, # 'ό' + 253: 26, # 'ύ' + 254: 27, # 'ώ' + 255: 253, # None +} + +WINDOWS_1253_GREEK_MODEL = SingleByteCharSetModel( + charset_name="windows-1253", + language="Greek", + char_to_order_map=WINDOWS_1253_GREEK_CHAR_TO_ORDER, + language_model=GREEK_LANG_MODEL, + typical_positive_ratio=0.982851, + keep_ascii_letters=False, + alphabet="ΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίαβγδεζηθικλμνξοπρςστυφχψωόύώ", +) + +ISO_8859_7_GREEK_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 82, # 'A' + 66: 100, # 'B' + 67: 104, # 'C' + 68: 94, # 'D' + 69: 98, # 'E' + 70: 101, # 'F' + 71: 116, # 'G' + 72: 102, # 'H' + 73: 111, # 'I' + 74: 187, # 'J' + 75: 117, # 'K' + 76: 92, # 'L' + 77: 88, # 'M' + 78: 113, # 'N' + 79: 85, # 'O' + 80: 79, # 'P' + 81: 118, # 'Q' + 82: 105, # 'R' + 83: 83, # 'S' + 84: 67, # 'T' + 85: 114, # 'U' + 86: 119, # 'V' + 87: 95, # 'W' + 88: 99, # 'X' + 89: 109, # 'Y' + 90: 188, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 72, # 'a' + 98: 70, # 'b' + 99: 80, # 'c' + 100: 81, # 'd' + 101: 60, # 'e' + 102: 96, # 'f' + 103: 93, # 'g' + 104: 89, # 'h' + 105: 68, # 'i' + 106: 120, # 'j' + 107: 97, # 'k' + 108: 77, # 'l' + 109: 86, # 'm' + 110: 69, # 'n' + 111: 55, # 'o' + 112: 78, # 'p' + 113: 115, # 'q' + 114: 65, # 'r' + 115: 66, # 's' + 116: 58, # 't' + 117: 76, # 'u' + 118: 106, # 'v' + 119: 103, # 'w' + 120: 87, # 'x' + 121: 107, # 'y' + 122: 112, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 255, # '\x80' + 129: 255, # '\x81' + 130: 255, # '\x82' + 131: 255, # '\x83' + 132: 255, # '\x84' + 133: 255, # '\x85' + 134: 255, # '\x86' + 135: 255, # '\x87' + 136: 255, # '\x88' + 137: 255, # '\x89' + 138: 255, # '\x8a' + 139: 255, # '\x8b' + 140: 255, # '\x8c' + 141: 255, # '\x8d' + 142: 255, # '\x8e' + 143: 255, # '\x8f' + 144: 255, # '\x90' + 145: 255, # '\x91' + 146: 255, # '\x92' + 147: 255, # '\x93' + 148: 255, # '\x94' + 149: 255, # '\x95' + 150: 255, # '\x96' + 151: 255, # '\x97' + 152: 255, # '\x98' + 153: 255, # '\x99' + 154: 255, # '\x9a' + 155: 255, # '\x9b' + 156: 255, # '\x9c' + 157: 255, # '\x9d' + 158: 255, # '\x9e' + 159: 255, # '\x9f' + 160: 253, # '\xa0' + 161: 233, # '‘' + 162: 90, # '’' + 163: 253, # '£' + 164: 253, # '€' + 165: 253, # '₯' + 166: 253, # '¦' + 167: 253, # '§' + 168: 253, # '¨' + 169: 253, # '©' + 170: 253, # 'ͺ' + 171: 253, # '«' + 172: 253, # '¬' + 173: 74, # '\xad' + 174: 253, # None + 175: 253, # '―' + 176: 253, # '°' + 177: 253, # '±' + 178: 253, # '²' + 179: 253, # '³' + 180: 247, # '΄' + 181: 248, # '΅' + 182: 61, # 'Ά' + 183: 36, # '·' + 184: 46, # 'Έ' + 185: 71, # 'Ή' + 186: 73, # 'Ί' + 187: 253, # '»' + 188: 54, # 'Ό' + 189: 253, # '½' + 190: 108, # 'Ύ' + 191: 123, # 'Ώ' + 192: 110, # 'ΐ' + 193: 31, # 'Α' + 194: 51, # 'Β' + 195: 43, # 'Γ' + 196: 41, # 'Δ' + 197: 34, # 'Ε' + 198: 91, # 'Ζ' + 199: 40, # 'Η' + 200: 52, # 'Θ' + 201: 47, # 'Ι' + 202: 44, # 'Κ' + 203: 53, # 'Λ' + 204: 38, # 'Μ' + 205: 49, # 'Ν' + 206: 59, # 'Ξ' + 207: 39, # 'Ο' + 208: 35, # 'Π' + 209: 48, # 'Ρ' + 210: 250, # None + 211: 37, # 'Σ' + 212: 33, # 'Τ' + 213: 45, # 'Υ' + 214: 56, # 'Φ' + 215: 50, # 'Χ' + 216: 84, # 'Ψ' + 217: 57, # 'Ω' + 218: 120, # 'Ϊ' + 219: 121, # 'Ϋ' + 220: 17, # 'ά' + 221: 18, # 'έ' + 222: 22, # 'ή' + 223: 15, # 'ί' + 224: 124, # 'ΰ' + 225: 1, # 'α' + 226: 29, # 'β' + 227: 20, # 'γ' + 228: 21, # 'δ' + 229: 3, # 'ε' + 230: 32, # 'ζ' + 231: 13, # 'η' + 232: 25, # 'θ' + 233: 5, # 'ι' + 234: 11, # 'κ' + 235: 16, # 'λ' + 236: 10, # 'μ' + 237: 6, # 'ν' + 238: 30, # 'ξ' + 239: 4, # 'ο' + 240: 9, # 'π' + 241: 8, # 'ρ' + 242: 14, # 'ς' + 243: 7, # 'σ' + 244: 2, # 'τ' + 245: 12, # 'υ' + 246: 28, # 'φ' + 247: 23, # 'χ' + 248: 42, # 'ψ' + 249: 24, # 'ω' + 250: 64, # 'ϊ' + 251: 75, # 'ϋ' + 252: 19, # 'ό' + 253: 26, # 'ύ' + 254: 27, # 'ώ' + 255: 253, # None +} + +ISO_8859_7_GREEK_MODEL = SingleByteCharSetModel( + charset_name="ISO-8859-7", + language="Greek", + char_to_order_map=ISO_8859_7_GREEK_CHAR_TO_ORDER, + language_model=GREEK_LANG_MODEL, + typical_positive_ratio=0.982851, + keep_ascii_letters=False, + alphabet="ΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίαβγδεζηθικλμνξοπρςστυφχψωόύώ", +) diff --git a/virt/lib/python3.9/site-packages/pip/_vendor/chardet/langrussianmodel 3.py b/virt/lib/python3.9/site-packages/pip/_vendor/chardet/langrussianmodel 3.py new file mode 100644 index 00000000..597b4ce8 --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_vendor/chardet/langrussianmodel 3.py @@ -0,0 +1,5725 @@ +from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +RUSSIAN_LANG_MODEL = { + 37: { # 'А' + 37: 0, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 1, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 2, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 1, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 1, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 0, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 0, # 'и' + 23: 1, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 0, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 2, # 'ф' + 26: 2, # 'х' + 28: 0, # 'ц' + 22: 1, # 'ч' + 25: 2, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 44: { # 'Б' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 1, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 2, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 33: { # 'В' + 37: 2, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 1, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 2, # 'а' + 21: 1, # 'б' + 10: 1, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 2, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 1, # 'ъ' + 18: 3, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 0, # 'ю' + 16: 1, # 'я' + }, + 46: { # 'Г' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 2, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 1, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 41: { # 'Д' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 2, # 'Е' + 56: 1, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 2, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 3, # 'ж' + 20: 1, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 48: { # 'Е' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 1, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 2, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 2, # 'Р' + 32: 2, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 0, # 'а' + 21: 0, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 2, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 0, # 'и' + 23: 2, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 1, # 'н' + 1: 0, # 'о' + 15: 1, # 'п' + 9: 1, # 'р' + 7: 3, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 2, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 56: { # 'Ж' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 1, # 'б' + 10: 0, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 2, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 1, # 'м' + 5: 0, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 2, # 'ю' + 16: 0, # 'я' + }, + 51: { # 'З' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 0, # 'г' + 13: 2, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 1, # 'л' + 12: 1, # 'м' + 5: 2, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 1, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 1, # 'я' + }, + 42: { # 'И' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 2, # 'Е' + 56: 1, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 2, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 1, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 1, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 2, # 'з' + 4: 1, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 1, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 1, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 60: { # 'Й' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 1, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 36: { # 'К' + 37: 2, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 2, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 1, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 0, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 49: { # 'Л' + 37: 2, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 1, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 1, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 0, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 0, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 1, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 1, # 'л' + 12: 0, # 'м' + 5: 1, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 2, # 'ю' + 16: 1, # 'я' + }, + 38: { # 'М' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 1, # 'Ф' + 55: 1, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 0, # 'Ь' + 47: 1, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 1, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 1, # 'л' + 12: 1, # 'м' + 5: 2, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 1, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 31: { # 'Н' + 37: 2, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 1, # 'З' + 42: 2, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 1, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 1, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 3, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 34: { # 'О' + 37: 0, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 2, # 'Д' + 48: 1, # 'Е' + 56: 1, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 2, # 'Л' + 38: 1, # 'М' + 31: 2, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 2, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 1, # 'Ф' + 55: 1, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 1, # 'а' + 21: 2, # 'б' + 10: 1, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 0, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 0, # 'и' + 23: 1, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 0, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 1, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 2, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 35: { # 'П' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 2, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 0, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 3, # 'р' + 7: 1, # 'с' + 6: 1, # 'т' + 14: 2, # 'у' + 39: 1, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 0, # 'ю' + 16: 2, # 'я' + }, + 45: { # 'Р' + 37: 2, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 2, # 'Е' + 56: 1, # 'Ж' + 51: 0, # 'З' + 42: 2, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 2, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 1, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 2, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 2, # 'я' + }, + 32: { # 'С' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 2, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 1, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 2, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 2, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 1, # 'с' + 6: 3, # 'т' + 14: 2, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 1, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 1, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 40: { # 'Т' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 2, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 1, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 1, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 1, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 52: { # 'У' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 1, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 0, # 'Я' + 3: 1, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 1, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 2, # 'и' + 23: 1, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 1, # 'н' + 1: 2, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 0, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 53: { # 'Ф' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 1, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 55: { # 'Х' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 2, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 0, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 1, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 1, # 'ь' + 30: 1, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 58: { # 'Ц' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 1, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 0, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 1, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 50: { # 'Ч' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 1, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 1, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 1, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 3, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 1, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 57: { # 'Ш' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 1, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 1, # 'н' + 1: 2, # 'о' + 15: 2, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 63: { # 'Щ' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 1, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 1, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 1, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 1, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 1, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 62: { # 'Ы' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 0, # 'Ч' + 57: 1, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 0, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 0, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 61: { # 'Ь' + 37: 0, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 1, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 1, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 1, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 0, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 0, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 0, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 47: { # 'Э' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 1, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 0, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 2, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 0, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 1, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 59: { # 'Ю' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 0, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 1, # 'б' + 10: 0, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 0, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 2, # 'н' + 1: 0, # 'о' + 15: 1, # 'п' + 9: 1, # 'р' + 7: 1, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 43: { # 'Я' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 0, # 'а' + 21: 1, # 'б' + 10: 1, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 0, # 'е' + 24: 0, # 'ж' + 20: 1, # 'з' + 4: 0, # 'и' + 23: 1, # 'й' + 11: 1, # 'к' + 8: 1, # 'л' + 12: 1, # 'м' + 5: 2, # 'н' + 1: 0, # 'о' + 15: 1, # 'п' + 9: 1, # 'р' + 7: 1, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 3: { # 'а' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 3, # 'и' + 23: 3, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 3, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 2, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 21: { # 'б' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 1, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 0, # 'ф' + 26: 2, # 'х' + 28: 1, # 'ц' + 22: 1, # 'ч' + 25: 2, # 'ш' + 29: 3, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 2, # 'ю' + 16: 3, # 'я' + }, + 10: { # 'в' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 3, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 3, # 'ш' + 29: 2, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 3, # 'я' + }, + 19: { # 'г' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 3, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 13: { # 'д' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 3, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 3, # 'ц' + 22: 2, # 'ч' + 25: 2, # 'ш' + 29: 1, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 1, # 'э' + 27: 2, # 'ю' + 16: 3, # 'я' + }, + 2: { # 'е' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 2, # 'и' + 23: 3, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 2, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 3, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 2, # 'ю' + 16: 3, # 'я' + }, + 24: { # 'ж' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 1, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 1, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 0, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 20: { # 'з' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 3, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 3, # 'я' + }, + 4: { # 'и' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 3, # 'и' + 23: 3, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 2, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 3, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 2, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 23: { # 'й' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 1, # 'а' + 21: 1, # 'б' + 10: 1, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 2, # 'з' + 4: 1, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 2, # 'ф' + 26: 1, # 'х' + 28: 2, # 'ц' + 22: 3, # 'ч' + 25: 2, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 2, # 'я' + }, + 11: { # 'к' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 3, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 2, # 'ц' + 22: 1, # 'ч' + 25: 2, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 8: { # 'л' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 3, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 1, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 2, # 'х' + 28: 1, # 'ц' + 22: 3, # 'ч' + 25: 2, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 1, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 12: { # 'м' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 1, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 2, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 2, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 3, # 'я' + }, + 5: { # 'н' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 2, # 'х' + 28: 3, # 'ц' + 22: 3, # 'ч' + 25: 2, # 'ш' + 29: 2, # 'щ' + 54: 1, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 1, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 1: { # 'о' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 3, # 'и' + 23: 3, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 2, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 2, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 15: { # 'п' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 3, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 0, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 3, # 'я' + }, + 9: { # 'р' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 2, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 3, # 'ш' + 29: 2, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 2, # 'э' + 27: 2, # 'ю' + 16: 3, # 'я' + }, + 7: { # 'с' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 1, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 3, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 3, # 'ч' + 25: 2, # 'ш' + 29: 1, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 2, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 6: { # 'т' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 3, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 2, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 2, # 'ш' + 29: 2, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 2, # 'э' + 27: 2, # 'ю' + 16: 3, # 'я' + }, + 14: { # 'у' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 2, # 'и' + 23: 2, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 2, # 'э' + 27: 3, # 'ю' + 16: 2, # 'я' + }, + 39: { # 'ф' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 0, # 'в' + 19: 1, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 2, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 1, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 2, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 26: { # 'х' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 3, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 1, # 'п' + 9: 3, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 1, # 'ч' + 25: 2, # 'ш' + 29: 0, # 'щ' + 54: 1, # 'ъ' + 18: 0, # 'ы' + 17: 1, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 28: { # 'ц' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 1, # 'л' + 12: 1, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 1, # 'т' + 14: 3, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 1, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 1, # 'ь' + 30: 0, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 22: { # 'ч' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 1, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 1, # 'ч' + 25: 2, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 3, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 25: { # 'ш' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 1, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 1, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 3, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 29: { # 'щ' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 1, # 'м' + 5: 2, # 'н' + 1: 1, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 2, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 54: { # 'ъ' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 0, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 1, # 'ю' + 16: 2, # 'я' + }, + 18: { # 'ы' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 2, # 'и' + 23: 3, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 1, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 0, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 2, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 2, # 'я' + }, + 17: { # 'ь' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 3, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 0, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 2, # 'п' + 9: 1, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 0, # 'у' + 39: 2, # 'ф' + 26: 1, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 3, # 'ш' + 29: 2, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 30: { # 'э' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 1, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 1, # 'б' + 10: 1, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 1, # 'е' + 24: 0, # 'ж' + 20: 1, # 'з' + 4: 0, # 'и' + 23: 2, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 0, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 2, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 27: { # 'ю' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 3, # 'б' + 10: 1, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 1, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 1, # 'и' + 23: 1, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 1, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 0, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 2, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 2, # 'ю' + 16: 1, # 'я' + }, + 16: { # 'я' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 2, # 'б' + 10: 3, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 2, # 'и' + 23: 2, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 0, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 1, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 2, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 2, # 'ю' + 16: 2, # 'я' + }, +} + +# 255: Undefined characters that did not exist in training text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 +# 251: Control characters + +# Character Mapping Table(s): +IBM866_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 37, # 'А' + 129: 44, # 'Б' + 130: 33, # 'В' + 131: 46, # 'Г' + 132: 41, # 'Д' + 133: 48, # 'Е' + 134: 56, # 'Ж' + 135: 51, # 'З' + 136: 42, # 'И' + 137: 60, # 'Й' + 138: 36, # 'К' + 139: 49, # 'Л' + 140: 38, # 'М' + 141: 31, # 'Н' + 142: 34, # 'О' + 143: 35, # 'П' + 144: 45, # 'Р' + 145: 32, # 'С' + 146: 40, # 'Т' + 147: 52, # 'У' + 148: 53, # 'Ф' + 149: 55, # 'Х' + 150: 58, # 'Ц' + 151: 50, # 'Ч' + 152: 57, # 'Ш' + 153: 63, # 'Щ' + 154: 70, # 'Ъ' + 155: 62, # 'Ы' + 156: 61, # 'Ь' + 157: 47, # 'Э' + 158: 59, # 'Ю' + 159: 43, # 'Я' + 160: 3, # 'а' + 161: 21, # 'б' + 162: 10, # 'в' + 163: 19, # 'г' + 164: 13, # 'д' + 165: 2, # 'е' + 166: 24, # 'ж' + 167: 20, # 'з' + 168: 4, # 'и' + 169: 23, # 'й' + 170: 11, # 'к' + 171: 8, # 'л' + 172: 12, # 'м' + 173: 5, # 'н' + 174: 1, # 'о' + 175: 15, # 'п' + 176: 191, # '░' + 177: 192, # '▒' + 178: 193, # '▓' + 179: 194, # '│' + 180: 195, # '┤' + 181: 196, # '╡' + 182: 197, # '╢' + 183: 198, # '╖' + 184: 199, # '╕' + 185: 200, # '╣' + 186: 201, # '║' + 187: 202, # '╗' + 188: 203, # '╝' + 189: 204, # '╜' + 190: 205, # '╛' + 191: 206, # '┐' + 192: 207, # '└' + 193: 208, # '┴' + 194: 209, # '┬' + 195: 210, # '├' + 196: 211, # '─' + 197: 212, # '┼' + 198: 213, # '╞' + 199: 214, # '╟' + 200: 215, # '╚' + 201: 216, # '╔' + 202: 217, # '╩' + 203: 218, # '╦' + 204: 219, # '╠' + 205: 220, # '═' + 206: 221, # '╬' + 207: 222, # '╧' + 208: 223, # '╨' + 209: 224, # '╤' + 210: 225, # '╥' + 211: 226, # '╙' + 212: 227, # '╘' + 213: 228, # '╒' + 214: 229, # '╓' + 215: 230, # '╫' + 216: 231, # '╪' + 217: 232, # '┘' + 218: 233, # '┌' + 219: 234, # '█' + 220: 235, # '▄' + 221: 236, # '▌' + 222: 237, # '▐' + 223: 238, # '▀' + 224: 9, # 'р' + 225: 7, # 'с' + 226: 6, # 'т' + 227: 14, # 'у' + 228: 39, # 'ф' + 229: 26, # 'х' + 230: 28, # 'ц' + 231: 22, # 'ч' + 232: 25, # 'ш' + 233: 29, # 'щ' + 234: 54, # 'ъ' + 235: 18, # 'ы' + 236: 17, # 'ь' + 237: 30, # 'э' + 238: 27, # 'ю' + 239: 16, # 'я' + 240: 239, # 'Ё' + 241: 68, # 'ё' + 242: 240, # 'Є' + 243: 241, # 'є' + 244: 242, # 'Ї' + 245: 243, # 'ї' + 246: 244, # 'Ў' + 247: 245, # 'ў' + 248: 246, # '°' + 249: 247, # '∙' + 250: 248, # '·' + 251: 249, # '√' + 252: 250, # '№' + 253: 251, # '¤' + 254: 252, # '■' + 255: 255, # '\xa0' +} + +IBM866_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name="IBM866", + language="Russian", + char_to_order_map=IBM866_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", +) + +WINDOWS_1251_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 191, # 'Ђ' + 129: 192, # 'Ѓ' + 130: 193, # '‚' + 131: 194, # 'ѓ' + 132: 195, # '„' + 133: 196, # '…' + 134: 197, # '†' + 135: 198, # '‡' + 136: 199, # '€' + 137: 200, # '‰' + 138: 201, # 'Љ' + 139: 202, # '‹' + 140: 203, # 'Њ' + 141: 204, # 'Ќ' + 142: 205, # 'Ћ' + 143: 206, # 'Џ' + 144: 207, # 'ђ' + 145: 208, # '‘' + 146: 209, # '’' + 147: 210, # '“' + 148: 211, # '”' + 149: 212, # '•' + 150: 213, # '–' + 151: 214, # '—' + 152: 215, # None + 153: 216, # '™' + 154: 217, # 'љ' + 155: 218, # '›' + 156: 219, # 'њ' + 157: 220, # 'ќ' + 158: 221, # 'ћ' + 159: 222, # 'џ' + 160: 223, # '\xa0' + 161: 224, # 'Ў' + 162: 225, # 'ў' + 163: 226, # 'Ј' + 164: 227, # '¤' + 165: 228, # 'Ґ' + 166: 229, # '¦' + 167: 230, # '§' + 168: 231, # 'Ё' + 169: 232, # '©' + 170: 233, # 'Є' + 171: 234, # '«' + 172: 235, # '¬' + 173: 236, # '\xad' + 174: 237, # '®' + 175: 238, # 'Ї' + 176: 239, # '°' + 177: 240, # '±' + 178: 241, # 'І' + 179: 242, # 'і' + 180: 243, # 'ґ' + 181: 244, # 'µ' + 182: 245, # '¶' + 183: 246, # '·' + 184: 68, # 'ё' + 185: 247, # '№' + 186: 248, # 'є' + 187: 249, # '»' + 188: 250, # 'ј' + 189: 251, # 'Ѕ' + 190: 252, # 'ѕ' + 191: 253, # 'ї' + 192: 37, # 'А' + 193: 44, # 'Б' + 194: 33, # 'В' + 195: 46, # 'Г' + 196: 41, # 'Д' + 197: 48, # 'Е' + 198: 56, # 'Ж' + 199: 51, # 'З' + 200: 42, # 'И' + 201: 60, # 'Й' + 202: 36, # 'К' + 203: 49, # 'Л' + 204: 38, # 'М' + 205: 31, # 'Н' + 206: 34, # 'О' + 207: 35, # 'П' + 208: 45, # 'Р' + 209: 32, # 'С' + 210: 40, # 'Т' + 211: 52, # 'У' + 212: 53, # 'Ф' + 213: 55, # 'Х' + 214: 58, # 'Ц' + 215: 50, # 'Ч' + 216: 57, # 'Ш' + 217: 63, # 'Щ' + 218: 70, # 'Ъ' + 219: 62, # 'Ы' + 220: 61, # 'Ь' + 221: 47, # 'Э' + 222: 59, # 'Ю' + 223: 43, # 'Я' + 224: 3, # 'а' + 225: 21, # 'б' + 226: 10, # 'в' + 227: 19, # 'г' + 228: 13, # 'д' + 229: 2, # 'е' + 230: 24, # 'ж' + 231: 20, # 'з' + 232: 4, # 'и' + 233: 23, # 'й' + 234: 11, # 'к' + 235: 8, # 'л' + 236: 12, # 'м' + 237: 5, # 'н' + 238: 1, # 'о' + 239: 15, # 'п' + 240: 9, # 'р' + 241: 7, # 'с' + 242: 6, # 'т' + 243: 14, # 'у' + 244: 39, # 'ф' + 245: 26, # 'х' + 246: 28, # 'ц' + 247: 22, # 'ч' + 248: 25, # 'ш' + 249: 29, # 'щ' + 250: 54, # 'ъ' + 251: 18, # 'ы' + 252: 17, # 'ь' + 253: 30, # 'э' + 254: 27, # 'ю' + 255: 16, # 'я' +} + +WINDOWS_1251_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name="windows-1251", + language="Russian", + char_to_order_map=WINDOWS_1251_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", +) + +IBM855_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 191, # 'ђ' + 129: 192, # 'Ђ' + 130: 193, # 'ѓ' + 131: 194, # 'Ѓ' + 132: 68, # 'ё' + 133: 195, # 'Ё' + 134: 196, # 'є' + 135: 197, # 'Є' + 136: 198, # 'ѕ' + 137: 199, # 'Ѕ' + 138: 200, # 'і' + 139: 201, # 'І' + 140: 202, # 'ї' + 141: 203, # 'Ї' + 142: 204, # 'ј' + 143: 205, # 'Ј' + 144: 206, # 'љ' + 145: 207, # 'Љ' + 146: 208, # 'њ' + 147: 209, # 'Њ' + 148: 210, # 'ћ' + 149: 211, # 'Ћ' + 150: 212, # 'ќ' + 151: 213, # 'Ќ' + 152: 214, # 'ў' + 153: 215, # 'Ў' + 154: 216, # 'џ' + 155: 217, # 'Џ' + 156: 27, # 'ю' + 157: 59, # 'Ю' + 158: 54, # 'ъ' + 159: 70, # 'Ъ' + 160: 3, # 'а' + 161: 37, # 'А' + 162: 21, # 'б' + 163: 44, # 'Б' + 164: 28, # 'ц' + 165: 58, # 'Ц' + 166: 13, # 'д' + 167: 41, # 'Д' + 168: 2, # 'е' + 169: 48, # 'Е' + 170: 39, # 'ф' + 171: 53, # 'Ф' + 172: 19, # 'г' + 173: 46, # 'Г' + 174: 218, # '«' + 175: 219, # '»' + 176: 220, # '░' + 177: 221, # '▒' + 178: 222, # '▓' + 179: 223, # '│' + 180: 224, # '┤' + 181: 26, # 'х' + 182: 55, # 'Х' + 183: 4, # 'и' + 184: 42, # 'И' + 185: 225, # '╣' + 186: 226, # '║' + 187: 227, # '╗' + 188: 228, # '╝' + 189: 23, # 'й' + 190: 60, # 'Й' + 191: 229, # '┐' + 192: 230, # '└' + 193: 231, # '┴' + 194: 232, # '┬' + 195: 233, # '├' + 196: 234, # '─' + 197: 235, # '┼' + 198: 11, # 'к' + 199: 36, # 'К' + 200: 236, # '╚' + 201: 237, # '╔' + 202: 238, # '╩' + 203: 239, # '╦' + 204: 240, # '╠' + 205: 241, # '═' + 206: 242, # '╬' + 207: 243, # '¤' + 208: 8, # 'л' + 209: 49, # 'Л' + 210: 12, # 'м' + 211: 38, # 'М' + 212: 5, # 'н' + 213: 31, # 'Н' + 214: 1, # 'о' + 215: 34, # 'О' + 216: 15, # 'п' + 217: 244, # '┘' + 218: 245, # '┌' + 219: 246, # '█' + 220: 247, # '▄' + 221: 35, # 'П' + 222: 16, # 'я' + 223: 248, # '▀' + 224: 43, # 'Я' + 225: 9, # 'р' + 226: 45, # 'Р' + 227: 7, # 'с' + 228: 32, # 'С' + 229: 6, # 'т' + 230: 40, # 'Т' + 231: 14, # 'у' + 232: 52, # 'У' + 233: 24, # 'ж' + 234: 56, # 'Ж' + 235: 10, # 'в' + 236: 33, # 'В' + 237: 17, # 'ь' + 238: 61, # 'Ь' + 239: 249, # '№' + 240: 250, # '\xad' + 241: 18, # 'ы' + 242: 62, # 'Ы' + 243: 20, # 'з' + 244: 51, # 'З' + 245: 25, # 'ш' + 246: 57, # 'Ш' + 247: 30, # 'э' + 248: 47, # 'Э' + 249: 29, # 'щ' + 250: 63, # 'Щ' + 251: 22, # 'ч' + 252: 50, # 'Ч' + 253: 251, # '§' + 254: 252, # '■' + 255: 255, # '\xa0' +} + +IBM855_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name="IBM855", + language="Russian", + char_to_order_map=IBM855_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", +) + +KOI8_R_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 191, # '─' + 129: 192, # '│' + 130: 193, # '┌' + 131: 194, # '┐' + 132: 195, # '└' + 133: 196, # '┘' + 134: 197, # '├' + 135: 198, # '┤' + 136: 199, # '┬' + 137: 200, # '┴' + 138: 201, # '┼' + 139: 202, # '▀' + 140: 203, # '▄' + 141: 204, # '█' + 142: 205, # '▌' + 143: 206, # '▐' + 144: 207, # '░' + 145: 208, # '▒' + 146: 209, # '▓' + 147: 210, # '⌠' + 148: 211, # '■' + 149: 212, # '∙' + 150: 213, # '√' + 151: 214, # '≈' + 152: 215, # '≤' + 153: 216, # '≥' + 154: 217, # '\xa0' + 155: 218, # '⌡' + 156: 219, # '°' + 157: 220, # '²' + 158: 221, # '·' + 159: 222, # '÷' + 160: 223, # '═' + 161: 224, # '║' + 162: 225, # '╒' + 163: 68, # 'ё' + 164: 226, # '╓' + 165: 227, # '╔' + 166: 228, # '╕' + 167: 229, # '╖' + 168: 230, # '╗' + 169: 231, # '╘' + 170: 232, # '╙' + 171: 233, # '╚' + 172: 234, # '╛' + 173: 235, # '╜' + 174: 236, # '╝' + 175: 237, # '╞' + 176: 238, # '╟' + 177: 239, # '╠' + 178: 240, # '╡' + 179: 241, # 'Ё' + 180: 242, # '╢' + 181: 243, # '╣' + 182: 244, # '╤' + 183: 245, # '╥' + 184: 246, # '╦' + 185: 247, # '╧' + 186: 248, # '╨' + 187: 249, # '╩' + 188: 250, # '╪' + 189: 251, # '╫' + 190: 252, # '╬' + 191: 253, # '©' + 192: 27, # 'ю' + 193: 3, # 'а' + 194: 21, # 'б' + 195: 28, # 'ц' + 196: 13, # 'д' + 197: 2, # 'е' + 198: 39, # 'ф' + 199: 19, # 'г' + 200: 26, # 'х' + 201: 4, # 'и' + 202: 23, # 'й' + 203: 11, # 'к' + 204: 8, # 'л' + 205: 12, # 'м' + 206: 5, # 'н' + 207: 1, # 'о' + 208: 15, # 'п' + 209: 16, # 'я' + 210: 9, # 'р' + 211: 7, # 'с' + 212: 6, # 'т' + 213: 14, # 'у' + 214: 24, # 'ж' + 215: 10, # 'в' + 216: 17, # 'ь' + 217: 18, # 'ы' + 218: 20, # 'з' + 219: 25, # 'ш' + 220: 30, # 'э' + 221: 29, # 'щ' + 222: 22, # 'ч' + 223: 54, # 'ъ' + 224: 59, # 'Ю' + 225: 37, # 'А' + 226: 44, # 'Б' + 227: 58, # 'Ц' + 228: 41, # 'Д' + 229: 48, # 'Е' + 230: 53, # 'Ф' + 231: 46, # 'Г' + 232: 55, # 'Х' + 233: 42, # 'И' + 234: 60, # 'Й' + 235: 36, # 'К' + 236: 49, # 'Л' + 237: 38, # 'М' + 238: 31, # 'Н' + 239: 34, # 'О' + 240: 35, # 'П' + 241: 43, # 'Я' + 242: 45, # 'Р' + 243: 32, # 'С' + 244: 40, # 'Т' + 245: 52, # 'У' + 246: 56, # 'Ж' + 247: 33, # 'В' + 248: 61, # 'Ь' + 249: 62, # 'Ы' + 250: 51, # 'З' + 251: 57, # 'Ш' + 252: 47, # 'Э' + 253: 63, # 'Щ' + 254: 50, # 'Ч' + 255: 70, # 'Ъ' +} + +KOI8_R_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name="KOI8-R", + language="Russian", + char_to_order_map=KOI8_R_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", +) + +MACCYRILLIC_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 37, # 'А' + 129: 44, # 'Б' + 130: 33, # 'В' + 131: 46, # 'Г' + 132: 41, # 'Д' + 133: 48, # 'Е' + 134: 56, # 'Ж' + 135: 51, # 'З' + 136: 42, # 'И' + 137: 60, # 'Й' + 138: 36, # 'К' + 139: 49, # 'Л' + 140: 38, # 'М' + 141: 31, # 'Н' + 142: 34, # 'О' + 143: 35, # 'П' + 144: 45, # 'Р' + 145: 32, # 'С' + 146: 40, # 'Т' + 147: 52, # 'У' + 148: 53, # 'Ф' + 149: 55, # 'Х' + 150: 58, # 'Ц' + 151: 50, # 'Ч' + 152: 57, # 'Ш' + 153: 63, # 'Щ' + 154: 70, # 'Ъ' + 155: 62, # 'Ы' + 156: 61, # 'Ь' + 157: 47, # 'Э' + 158: 59, # 'Ю' + 159: 43, # 'Я' + 160: 191, # '†' + 161: 192, # '°' + 162: 193, # 'Ґ' + 163: 194, # '£' + 164: 195, # '§' + 165: 196, # '•' + 166: 197, # '¶' + 167: 198, # 'І' + 168: 199, # '®' + 169: 200, # '©' + 170: 201, # '™' + 171: 202, # 'Ђ' + 172: 203, # 'ђ' + 173: 204, # '≠' + 174: 205, # 'Ѓ' + 175: 206, # 'ѓ' + 176: 207, # '∞' + 177: 208, # '±' + 178: 209, # '≤' + 179: 210, # '≥' + 180: 211, # 'і' + 181: 212, # 'µ' + 182: 213, # 'ґ' + 183: 214, # 'Ј' + 184: 215, # 'Є' + 185: 216, # 'є' + 186: 217, # 'Ї' + 187: 218, # 'ї' + 188: 219, # 'Љ' + 189: 220, # 'љ' + 190: 221, # 'Њ' + 191: 222, # 'њ' + 192: 223, # 'ј' + 193: 224, # 'Ѕ' + 194: 225, # '¬' + 195: 226, # '√' + 196: 227, # 'ƒ' + 197: 228, # '≈' + 198: 229, # '∆' + 199: 230, # '«' + 200: 231, # '»' + 201: 232, # '…' + 202: 233, # '\xa0' + 203: 234, # 'Ћ' + 204: 235, # 'ћ' + 205: 236, # 'Ќ' + 206: 237, # 'ќ' + 207: 238, # 'ѕ' + 208: 239, # '–' + 209: 240, # '—' + 210: 241, # '“' + 211: 242, # '”' + 212: 243, # '‘' + 213: 244, # '’' + 214: 245, # '÷' + 215: 246, # '„' + 216: 247, # 'Ў' + 217: 248, # 'ў' + 218: 249, # 'Џ' + 219: 250, # 'џ' + 220: 251, # '№' + 221: 252, # 'Ё' + 222: 68, # 'ё' + 223: 16, # 'я' + 224: 3, # 'а' + 225: 21, # 'б' + 226: 10, # 'в' + 227: 19, # 'г' + 228: 13, # 'д' + 229: 2, # 'е' + 230: 24, # 'ж' + 231: 20, # 'з' + 232: 4, # 'и' + 233: 23, # 'й' + 234: 11, # 'к' + 235: 8, # 'л' + 236: 12, # 'м' + 237: 5, # 'н' + 238: 1, # 'о' + 239: 15, # 'п' + 240: 9, # 'р' + 241: 7, # 'с' + 242: 6, # 'т' + 243: 14, # 'у' + 244: 39, # 'ф' + 245: 26, # 'х' + 246: 28, # 'ц' + 247: 22, # 'ч' + 248: 25, # 'ш' + 249: 29, # 'щ' + 250: 54, # 'ъ' + 251: 18, # 'ы' + 252: 17, # 'ь' + 253: 30, # 'э' + 254: 27, # 'ю' + 255: 255, # '€' +} + +MACCYRILLIC_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name="MacCyrillic", + language="Russian", + char_to_order_map=MACCYRILLIC_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", +) + +ISO_8859_5_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 191, # '\x80' + 129: 192, # '\x81' + 130: 193, # '\x82' + 131: 194, # '\x83' + 132: 195, # '\x84' + 133: 196, # '\x85' + 134: 197, # '\x86' + 135: 198, # '\x87' + 136: 199, # '\x88' + 137: 200, # '\x89' + 138: 201, # '\x8a' + 139: 202, # '\x8b' + 140: 203, # '\x8c' + 141: 204, # '\x8d' + 142: 205, # '\x8e' + 143: 206, # '\x8f' + 144: 207, # '\x90' + 145: 208, # '\x91' + 146: 209, # '\x92' + 147: 210, # '\x93' + 148: 211, # '\x94' + 149: 212, # '\x95' + 150: 213, # '\x96' + 151: 214, # '\x97' + 152: 215, # '\x98' + 153: 216, # '\x99' + 154: 217, # '\x9a' + 155: 218, # '\x9b' + 156: 219, # '\x9c' + 157: 220, # '\x9d' + 158: 221, # '\x9e' + 159: 222, # '\x9f' + 160: 223, # '\xa0' + 161: 224, # 'Ё' + 162: 225, # 'Ђ' + 163: 226, # 'Ѓ' + 164: 227, # 'Є' + 165: 228, # 'Ѕ' + 166: 229, # 'І' + 167: 230, # 'Ї' + 168: 231, # 'Ј' + 169: 232, # 'Љ' + 170: 233, # 'Њ' + 171: 234, # 'Ћ' + 172: 235, # 'Ќ' + 173: 236, # '\xad' + 174: 237, # 'Ў' + 175: 238, # 'Џ' + 176: 37, # 'А' + 177: 44, # 'Б' + 178: 33, # 'В' + 179: 46, # 'Г' + 180: 41, # 'Д' + 181: 48, # 'Е' + 182: 56, # 'Ж' + 183: 51, # 'З' + 184: 42, # 'И' + 185: 60, # 'Й' + 186: 36, # 'К' + 187: 49, # 'Л' + 188: 38, # 'М' + 189: 31, # 'Н' + 190: 34, # 'О' + 191: 35, # 'П' + 192: 45, # 'Р' + 193: 32, # 'С' + 194: 40, # 'Т' + 195: 52, # 'У' + 196: 53, # 'Ф' + 197: 55, # 'Х' + 198: 58, # 'Ц' + 199: 50, # 'Ч' + 200: 57, # 'Ш' + 201: 63, # 'Щ' + 202: 70, # 'Ъ' + 203: 62, # 'Ы' + 204: 61, # 'Ь' + 205: 47, # 'Э' + 206: 59, # 'Ю' + 207: 43, # 'Я' + 208: 3, # 'а' + 209: 21, # 'б' + 210: 10, # 'в' + 211: 19, # 'г' + 212: 13, # 'д' + 213: 2, # 'е' + 214: 24, # 'ж' + 215: 20, # 'з' + 216: 4, # 'и' + 217: 23, # 'й' + 218: 11, # 'к' + 219: 8, # 'л' + 220: 12, # 'м' + 221: 5, # 'н' + 222: 1, # 'о' + 223: 15, # 'п' + 224: 9, # 'р' + 225: 7, # 'с' + 226: 6, # 'т' + 227: 14, # 'у' + 228: 39, # 'ф' + 229: 26, # 'х' + 230: 28, # 'ц' + 231: 22, # 'ч' + 232: 25, # 'ш' + 233: 29, # 'щ' + 234: 54, # 'ъ' + 235: 18, # 'ы' + 236: 17, # 'ь' + 237: 30, # 'э' + 238: 27, # 'ю' + 239: 16, # 'я' + 240: 239, # '№' + 241: 68, # 'ё' + 242: 240, # 'ђ' + 243: 241, # 'ѓ' + 244: 242, # 'є' + 245: 243, # 'ѕ' + 246: 244, # 'і' + 247: 245, # 'ї' + 248: 246, # 'ј' + 249: 247, # 'љ' + 250: 248, # 'њ' + 251: 249, # 'ћ' + 252: 250, # 'ќ' + 253: 251, # '§' + 254: 252, # 'ў' + 255: 255, # 'џ' +} + +ISO_8859_5_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name="ISO-8859-5", + language="Russian", + char_to_order_map=ISO_8859_5_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", +) diff --git a/virt/lib/python3.9/site-packages/pip/_vendor/chardet/langthaimodel 3.py b/virt/lib/python3.9/site-packages/pip/_vendor/chardet/langthaimodel 3.py new file mode 100644 index 00000000..302d053c --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_vendor/chardet/langthaimodel 3.py @@ -0,0 +1,4380 @@ +from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +THAI_LANG_MODEL = { + 5: { # 'ก' + 5: 2, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 2, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 3, # 'ฎ' + 57: 2, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 2, # 'ณ' + 20: 2, # 'ด' + 19: 3, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 1, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 1, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 3, # 'ร' + 61: 2, # 'ฤ' + 15: 3, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 3, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 1, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 2, # 'ื' + 32: 2, # 'ุ' + 35: 1, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 3, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 30: { # 'ข' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 1, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 2, # 'ณ' + 20: 0, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 2, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 2, # 'ี' + 40: 3, # 'ึ' + 27: 1, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 2, # '่' + 7: 3, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 24: { # 'ค' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 2, # 'ค' + 8: 2, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 2, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 0, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 3, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 2, # 'า' + 36: 3, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 3, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 8: { # 'ง' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 3, # 'ค' + 8: 2, # 'ง' + 26: 2, # 'จ' + 52: 1, # 'ฉ' + 34: 2, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 1, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 2, # 'ศ' + 46: 1, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 1, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 1, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 3, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 26: { # 'จ' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 0, # 'ค' + 8: 2, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 1, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 1, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 3, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 2, # 'ิ' + 13: 1, # 'ี' + 40: 3, # 'ึ' + 27: 1, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 2, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 52: { # 'ฉ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 3, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 3, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 1, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 1, # 'ั' + 1: 1, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 34: { # 'ช' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 1, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 1, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 1, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 1, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 51: { # 'ซ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 1, # 'ั' + 1: 1, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 2, # 'ี' + 40: 3, # 'ึ' + 27: 2, # 'ื' + 32: 1, # 'ุ' + 35: 1, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 1, # '่' + 7: 2, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 47: { # 'ญ' + 5: 1, # 'ก' + 30: 1, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 3, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 2, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 0, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 58: { # 'ฎ' + 5: 2, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 1, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 57: { # 'ฏ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 49: { # 'ฐ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 53: { # 'ฑ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 55: { # 'ฒ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 43: { # 'ณ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 3, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 3, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 3, # 'ะ' + 10: 0, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 20: { # 'ด' + 5: 2, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 3, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 2, # 'า' + 36: 2, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 1, # 'ึ' + 27: 2, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 2, # 'ๆ' + 37: 2, # '็' + 6: 1, # '่' + 7: 3, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 19: { # 'ต' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 1, # 'ต' + 44: 2, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 2, # 'ภ' + 9: 1, # 'ม' + 16: 1, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 0, # 'ห' + 4: 3, # 'อ' + 63: 1, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 1, # 'ึ' + 27: 1, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 2, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 44: { # 'ถ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 1, # 'ี' + 40: 3, # 'ึ' + 27: 2, # 'ื' + 32: 2, # 'ุ' + 35: 3, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 14: { # 'ท' + 5: 1, # 'ก' + 30: 1, # 'ข' + 24: 3, # 'ค' + 8: 1, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 3, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 3, # 'ย' + 2: 3, # 'ร' + 61: 1, # 'ฤ' + 15: 1, # 'ล' + 12: 2, # 'ว' + 42: 3, # 'ศ' + 46: 1, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 2, # 'ิ' + 13: 3, # 'ี' + 40: 2, # 'ึ' + 27: 1, # 'ื' + 32: 3, # 'ุ' + 35: 1, # 'ู' + 11: 0, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 48: { # 'ธ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 1, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 2, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 2, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 3: { # 'น' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 3, # 'ค' + 8: 1, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 1, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 2, # 'ถ' + 14: 3, # 'ท' + 48: 3, # 'ธ' + 3: 2, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 1, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 1, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 3, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 3, # 'โ' + 29: 3, # 'ใ' + 33: 3, # 'ไ' + 50: 2, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 17: { # 'บ' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 1, # 'ง' + 26: 1, # 'จ' + 52: 1, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 2, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 2, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 2, # '่' + 7: 2, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 25: { # 'ป' + 5: 2, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 1, # 'ฎ' + 57: 3, # 'ฏ' + 49: 1, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 0, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 1, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 1, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 3, # 'ั' + 1: 1, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 2, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 3, # '็' + 6: 1, # '่' + 7: 2, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 39: { # 'ผ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 1, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 2, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 1, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 1, # 'ื' + 32: 0, # 'ุ' + 35: 3, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 1, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 62: { # 'ฝ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 1, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 1, # 'ี' + 40: 2, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 1, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 31: { # 'พ' + 5: 1, # 'ก' + 30: 1, # 'ข' + 24: 1, # 'ค' + 8: 1, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 1, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 2, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 1, # 'ึ' + 27: 3, # 'ื' + 32: 1, # 'ุ' + 35: 2, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 0, # '่' + 7: 1, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 54: { # 'ฟ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 2, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 2, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 1, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 2, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 45: { # 'ภ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 3, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 2, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 9: { # 'ม' + 5: 2, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 2, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 3, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 2, # 'ร' + 61: 2, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 1, # 'ศ' + 46: 1, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 3, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 2, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 2, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 16: { # 'ย' + 5: 3, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 2, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 3, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 3, # 'ี' + 40: 1, # 'ึ' + 27: 2, # 'ื' + 32: 2, # 'ุ' + 35: 3, # 'ู' + 11: 2, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 2, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 2, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 2: { # 'ร' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 2, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 3, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 3, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 3, # 'ถ' + 14: 3, # 'ท' + 48: 1, # 'ธ' + 3: 2, # 'น' + 17: 2, # 'บ' + 25: 3, # 'ป' + 39: 2, # 'ผ' + 62: 1, # 'ฝ' + 31: 2, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 2, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 1, # 'ฯ' + 22: 3, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 2, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 3, # 'ู' + 11: 3, # 'เ' + 28: 3, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 3, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 61: { # 'ฤ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 2, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 15: { # 'ล' + 5: 2, # 'ก' + 30: 3, # 'ข' + 24: 1, # 'ค' + 8: 3, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 3, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 3, # 'อ' + 63: 2, # 'ฯ' + 22: 3, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 2, # 'ึ' + 27: 3, # 'ื' + 32: 2, # 'ุ' + 35: 3, # 'ู' + 11: 2, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 2, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 12: { # 'ว' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 1, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 2, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 42: { # 'ศ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 1, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 2, # 'ว' + 42: 1, # 'ศ' + 46: 2, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 0, # 'ี' + 40: 3, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 2, # 'ู' + 11: 0, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 46: { # 'ษ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 2, # 'ฎ' + 57: 1, # 'ฏ' + 49: 2, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 3, # 'ณ' + 20: 0, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 18: { # 'ส' + 5: 2, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 2, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 3, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 2, # 'ภ' + 9: 3, # 'ม' + 16: 1, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 2, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 3, # 'ู' + 11: 2, # 'เ' + 28: 0, # 'แ' + 41: 1, # 'โ' + 29: 0, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 1, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 21: { # 'ห' + 5: 3, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 1, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 3, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 0, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 1, # 'ุ' + 35: 1, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 3, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 4: { # 'อ' + 5: 3, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 2, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 1, # '็' + 6: 2, # '่' + 7: 2, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 63: { # 'ฯ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 22: { # 'ะ' + 5: 3, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 1, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 3, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 1, # 'ธ' + 3: 2, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 1, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 10: { # 'ั' + 5: 3, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 3, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 3, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 2, # 'ฐ' + 53: 0, # 'ฑ' + 55: 3, # 'ฒ' + 43: 3, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 1: { # 'า' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 3, # 'ค' + 8: 3, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 3, # 'ช' + 51: 1, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 3, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 2, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 1, # 'ฝ' + 31: 3, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 3, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 3, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 36: { # 'ำ' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 3, # 'ค' + 8: 2, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 1, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 3, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 23: { # 'ิ' + 5: 3, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 3, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 3, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 3, # 'พ' + 54: 1, # 'ฟ' + 45: 2, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 3, # 'ศ' + 46: 2, # 'ษ' + 18: 2, # 'ส' + 21: 3, # 'ห' + 4: 1, # 'อ' + 63: 1, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 2, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 13: { # 'ี' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 3, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 2, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 40: { # 'ึ' + 5: 3, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 3, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 27: { # 'ื' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 3, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 32: { # 'ุ' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 3, # 'ค' + 8: 3, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 1, # 'ฒ' + 43: 3, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 2, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 1, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 1, # 'ว' + 42: 1, # 'ศ' + 46: 2, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 1, # 'โ' + 29: 0, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 2, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 35: { # 'ู' + 5: 3, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 2, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 0, # 'บ' + 25: 3, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 11: { # 'เ' + 5: 3, # 'ก' + 30: 3, # 'ข' + 24: 3, # 'ค' + 8: 2, # 'ง' + 26: 3, # 'จ' + 52: 3, # 'ฉ' + 34: 3, # 'ช' + 51: 2, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 3, # 'ป' + 39: 2, # 'ผ' + 62: 1, # 'ฝ' + 31: 3, # 'พ' + 54: 1, # 'ฟ' + 45: 3, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 28: { # 'แ' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 1, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 3, # 'ต' + 44: 2, # 'ถ' + 14: 3, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 3, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 2, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 41: { # 'โ' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 1, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 1, # 'บ' + 25: 3, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 0, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 29: { # 'ใ' + 5: 2, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 3, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 1, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 33: { # 'ไ' + 5: 1, # 'ก' + 30: 2, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 3, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 1, # 'บ' + 25: 3, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 2, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 0, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 3, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 2, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 50: { # 'ๆ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 37: { # '็' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 2, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 1, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 6: { # '่' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 1, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 1, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 1, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 0, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 7: { # '้' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 3, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 38: { # '์' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 1, # 'ฤ' + 15: 1, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 56: { # '๑' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 2, # '๑' + 59: 1, # '๒' + 60: 1, # '๕' + }, + 59: { # '๒' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 1, # '๑' + 59: 1, # '๒' + 60: 3, # '๕' + }, + 60: { # '๕' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 2, # '๑' + 59: 1, # '๒' + 60: 0, # '๕' + }, +} + +# 255: Undefined characters that did not exist in training text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 +# 251: Control characters + +# Character Mapping Table(s): +TIS_620_THAI_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 182, # 'A' + 66: 106, # 'B' + 67: 107, # 'C' + 68: 100, # 'D' + 69: 183, # 'E' + 70: 184, # 'F' + 71: 185, # 'G' + 72: 101, # 'H' + 73: 94, # 'I' + 74: 186, # 'J' + 75: 187, # 'K' + 76: 108, # 'L' + 77: 109, # 'M' + 78: 110, # 'N' + 79: 111, # 'O' + 80: 188, # 'P' + 81: 189, # 'Q' + 82: 190, # 'R' + 83: 89, # 'S' + 84: 95, # 'T' + 85: 112, # 'U' + 86: 113, # 'V' + 87: 191, # 'W' + 88: 192, # 'X' + 89: 193, # 'Y' + 90: 194, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 64, # 'a' + 98: 72, # 'b' + 99: 73, # 'c' + 100: 114, # 'd' + 101: 74, # 'e' + 102: 115, # 'f' + 103: 116, # 'g' + 104: 102, # 'h' + 105: 81, # 'i' + 106: 201, # 'j' + 107: 117, # 'k' + 108: 90, # 'l' + 109: 103, # 'm' + 110: 78, # 'n' + 111: 82, # 'o' + 112: 96, # 'p' + 113: 202, # 'q' + 114: 91, # 'r' + 115: 79, # 's' + 116: 84, # 't' + 117: 104, # 'u' + 118: 105, # 'v' + 119: 97, # 'w' + 120: 98, # 'x' + 121: 92, # 'y' + 122: 203, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 209, # '\x80' + 129: 210, # '\x81' + 130: 211, # '\x82' + 131: 212, # '\x83' + 132: 213, # '\x84' + 133: 88, # '\x85' + 134: 214, # '\x86' + 135: 215, # '\x87' + 136: 216, # '\x88' + 137: 217, # '\x89' + 138: 218, # '\x8a' + 139: 219, # '\x8b' + 140: 220, # '\x8c' + 141: 118, # '\x8d' + 142: 221, # '\x8e' + 143: 222, # '\x8f' + 144: 223, # '\x90' + 145: 224, # '\x91' + 146: 99, # '\x92' + 147: 85, # '\x93' + 148: 83, # '\x94' + 149: 225, # '\x95' + 150: 226, # '\x96' + 151: 227, # '\x97' + 152: 228, # '\x98' + 153: 229, # '\x99' + 154: 230, # '\x9a' + 155: 231, # '\x9b' + 156: 232, # '\x9c' + 157: 233, # '\x9d' + 158: 234, # '\x9e' + 159: 235, # '\x9f' + 160: 236, # None + 161: 5, # 'ก' + 162: 30, # 'ข' + 163: 237, # 'ฃ' + 164: 24, # 'ค' + 165: 238, # 'ฅ' + 166: 75, # 'ฆ' + 167: 8, # 'ง' + 168: 26, # 'จ' + 169: 52, # 'ฉ' + 170: 34, # 'ช' + 171: 51, # 'ซ' + 172: 119, # 'ฌ' + 173: 47, # 'ญ' + 174: 58, # 'ฎ' + 175: 57, # 'ฏ' + 176: 49, # 'ฐ' + 177: 53, # 'ฑ' + 178: 55, # 'ฒ' + 179: 43, # 'ณ' + 180: 20, # 'ด' + 181: 19, # 'ต' + 182: 44, # 'ถ' + 183: 14, # 'ท' + 184: 48, # 'ธ' + 185: 3, # 'น' + 186: 17, # 'บ' + 187: 25, # 'ป' + 188: 39, # 'ผ' + 189: 62, # 'ฝ' + 190: 31, # 'พ' + 191: 54, # 'ฟ' + 192: 45, # 'ภ' + 193: 9, # 'ม' + 194: 16, # 'ย' + 195: 2, # 'ร' + 196: 61, # 'ฤ' + 197: 15, # 'ล' + 198: 239, # 'ฦ' + 199: 12, # 'ว' + 200: 42, # 'ศ' + 201: 46, # 'ษ' + 202: 18, # 'ส' + 203: 21, # 'ห' + 204: 76, # 'ฬ' + 205: 4, # 'อ' + 206: 66, # 'ฮ' + 207: 63, # 'ฯ' + 208: 22, # 'ะ' + 209: 10, # 'ั' + 210: 1, # 'า' + 211: 36, # 'ำ' + 212: 23, # 'ิ' + 213: 13, # 'ี' + 214: 40, # 'ึ' + 215: 27, # 'ื' + 216: 32, # 'ุ' + 217: 35, # 'ู' + 218: 86, # 'ฺ' + 219: 240, # None + 220: 241, # None + 221: 242, # None + 222: 243, # None + 223: 244, # '฿' + 224: 11, # 'เ' + 225: 28, # 'แ' + 226: 41, # 'โ' + 227: 29, # 'ใ' + 228: 33, # 'ไ' + 229: 245, # 'ๅ' + 230: 50, # 'ๆ' + 231: 37, # '็' + 232: 6, # '่' + 233: 7, # '้' + 234: 67, # '๊' + 235: 77, # '๋' + 236: 38, # '์' + 237: 93, # 'ํ' + 238: 246, # '๎' + 239: 247, # '๏' + 240: 68, # '๐' + 241: 56, # '๑' + 242: 59, # '๒' + 243: 65, # '๓' + 244: 69, # '๔' + 245: 60, # '๕' + 246: 70, # '๖' + 247: 80, # '๗' + 248: 71, # '๘' + 249: 87, # '๙' + 250: 248, # '๚' + 251: 249, # '๛' + 252: 250, # None + 253: 251, # None + 254: 252, # None + 255: 253, # None +} + +TIS_620_THAI_MODEL = SingleByteCharSetModel( + charset_name="TIS-620", + language="Thai", + char_to_order_map=TIS_620_THAI_CHAR_TO_ORDER, + language_model=THAI_LANG_MODEL, + typical_positive_ratio=0.926386, + keep_ascii_letters=False, + alphabet="กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛", +) diff --git a/virt/lib/python3.9/site-packages/pip/_vendor/chardet/langturkishmodel 3.py b/virt/lib/python3.9/site-packages/pip/_vendor/chardet/langturkishmodel 3.py new file mode 100644 index 00000000..6a7ee30c --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_vendor/chardet/langturkishmodel 3.py @@ -0,0 +1,4380 @@ +from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +TURKISH_LANG_MODEL = { + 23: { # 'A' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 1, # 'i' + 24: 0, # 'j' + 10: 2, # 'k' + 5: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 37: { # 'B' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 2, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 47: { # 'C' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 1, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 2, # 'l' + 13: 2, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 2, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 39: { # 'D' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 1, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 1, # 'Ş' + 19: 0, # 'ş' + }, + 29: { # 'E' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 1, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 1, # 'j' + 10: 0, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 1, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 52: { # 'F' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 1, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 2, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 1, # 'b' + 28: 1, # 'c' + 12: 1, # 'd' + 2: 0, # 'e' + 18: 1, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 1, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 2, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 2, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 2, # 'ş' + }, + 36: { # 'G' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 2, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 2, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 1, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 1, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 0, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 1, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 45: { # 'H' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 2, # 'G' + 45: 1, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 1, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 2, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 1, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 2, # 'ğ' + 41: 1, # 'İ' + 6: 0, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 53: { # 'I' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 2, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 60: { # 'J' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 0, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 1, # 's' + 9: 0, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 16: { # 'K' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 1, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 0, # 'u' + 32: 3, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 49: { # 'L' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 2, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 2, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 0, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 1, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 2, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 1, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 20: { # 'M' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 2, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 0, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 46: { # 'N' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 1, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 1, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 1, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 42: { # 'O' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 1, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 2, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 2, # 'İ' + 6: 1, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 48: { # 'P' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 2, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 0, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 44: { # 'R' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 1, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 2, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 1, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 35: { # 'S' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 1, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 1, # 'l' + 13: 2, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 1, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 2, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 31: { # 'T' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 2, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 1, # 'j' + 10: 2, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 2, # 'r' + 8: 0, # 's' + 9: 2, # 't' + 14: 2, # 'u' + 32: 1, # 'v' + 57: 1, # 'w' + 58: 1, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 51: { # 'U' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 1, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 1, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 38: { # 'V' + 23: 1, # 'A' + 37: 1, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 2, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 1, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 1, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 62: { # 'W' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 0, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 43: { # 'Y' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 1, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 2, # 'N' + 42: 0, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 1, # 'j' + 10: 1, # 'k' + 5: 1, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 1, # 'Ü' + 59: 1, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 0, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 56: { # 'Z' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 2, # 'Z' + 1: 2, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 1, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 1, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 1: { # 'a' + 23: 3, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 2, # 'Z' + 1: 2, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 2, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 3, # 'v' + 57: 2, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 1, # 'î' + 34: 1, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 21: { # 'b' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 3, # 'g' + 25: 1, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 1, # 'r' + 8: 2, # 's' + 9: 2, # 't' + 14: 2, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 28: { # 'c' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 2, # 'E' + 52: 0, # 'F' + 36: 2, # 'G' + 45: 2, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 2, # 'T' + 51: 2, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 3, # 'Y' + 56: 0, # 'Z' + 1: 1, # 'a' + 21: 1, # 'b' + 28: 2, # 'c' + 12: 2, # 'd' + 2: 1, # 'e' + 18: 1, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 1, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 2, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 1, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 1, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 1, # 'î' + 34: 2, # 'ö' + 17: 2, # 'ü' + 30: 2, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 2, # 'ş' + }, + 12: { # 'd' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 2, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 1, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 2, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 1, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 2: { # 'e' + 23: 2, # 'A' + 37: 0, # 'B' + 47: 2, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 2, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 3, # 'v' + 57: 2, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 1, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 18: { # 'f' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 1, # 'i' + 24: 1, # 'j' + 10: 1, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 1, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 1, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 27: { # 'g' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 2, # 'r' + 8: 2, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 25: { # 'h' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 3: { # 'i' + 23: 2, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 1, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 3, # 'g' + 25: 1, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 1, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 1, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 1, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 24: { # 'j' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 1, # 'j' + 10: 2, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 2, # 'r' + 8: 3, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 10: { # 'k' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 1, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 2, # 'r' + 8: 2, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 3, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 5: { # 'l' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 1, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 1, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 2, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 13: { # 'm' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 2, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 2, # 'u' + 32: 2, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 4: { # 'n' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 2, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 1, # 'f' + 27: 2, # 'g' + 25: 3, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 3, # 'p' + 7: 2, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 15: { # 'o' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 2, # 'L' + 20: 0, # 'M' + 46: 2, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 2, # 'ğ' + 41: 2, # 'İ' + 6: 3, # 'ı' + 40: 2, # 'Ş' + 19: 2, # 'ş' + }, + 26: { # 'p' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 1, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 2, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 7: { # 'r' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 2, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 1, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 3, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 8: { # 's' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 2, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 9: { # 't' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 3, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 14: { # 'u' + 23: 3, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 2, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 2, # 'Z' + 1: 2, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 2, # 'e' + 18: 2, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 2, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 32: { # 'v' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 1, # 'j' + 10: 1, # 'k' + 5: 3, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 1, # 'r' + 8: 2, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 57: { # 'w' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 1, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 1, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 1, # 's' + 9: 0, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 2, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 58: { # 'x' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 1, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 1, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 2, # 's' + 9: 1, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 11: { # 'y' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 1, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 2, # 'r' + 8: 1, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 22: { # 'z' + 23: 2, # 'A' + 37: 2, # 'B' + 47: 1, # 'C' + 39: 2, # 'D' + 29: 3, # 'E' + 52: 1, # 'F' + 36: 2, # 'G' + 45: 2, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 2, # 'N' + 42: 2, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 3, # 'T' + 51: 2, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 1, # 'Z' + 1: 1, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 2, # 'd' + 2: 2, # 'e' + 18: 3, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 2, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 0, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 2, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 2, # 'Ü' + 59: 1, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 2, # 'ü' + 30: 2, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 1, # 'Ş' + 19: 2, # 'ş' + }, + 63: { # '·' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 1, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 54: { # 'Ç' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 0, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 0, # 'h' + 3: 3, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 2, # 'r' + 8: 0, # 's' + 9: 1, # 't' + 14: 0, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 2, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 50: { # 'Ö' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 2, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 2, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 1, # 'N' + 42: 2, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 2, # 'd' + 2: 0, # 'e' + 18: 1, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 0, # 'j' + 10: 2, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 3, # 'n' + 15: 2, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 1, # 's' + 9: 2, # 't' + 14: 0, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 2, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 55: { # 'Ü' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 1, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 59: { # 'â' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 1, # 'Ş' + 19: 0, # 'ş' + }, + 33: { # 'ç' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 0, # 'e' + 18: 2, # 'f' + 27: 1, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 0, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 2, # 's' + 9: 3, # 't' + 14: 0, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 61: { # 'î' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 1, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 1, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 34: { # 'ö' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 1, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 2, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 1, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 3, # 's' + 9: 1, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 2, # 'ğ' + 41: 1, # 'İ' + 6: 1, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 17: { # 'ü' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 1, # 'f' + 27: 2, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 1, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 2, # 'r' + 8: 3, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 1, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 30: { # 'ğ' + 23: 0, # 'A' + 37: 2, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 2, # 'N' + 42: 2, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 3, # 'j' + 10: 1, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 2, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 2, # 'İ' + 6: 2, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 41: { # 'İ' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 1, # 'E' + 52: 0, # 'F' + 36: 2, # 'G' + 45: 2, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 1, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 2, # 'd' + 2: 1, # 'e' + 18: 0, # 'f' + 27: 3, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 2, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 2, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 1, # 'Ü' + 59: 1, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 1, # 'ü' + 30: 2, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 6: { # 'ı' + 23: 2, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 2, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 3, # 'v' + 57: 1, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 40: { # 'Ş' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 1, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 2, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 2, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 1, # 'Z' + 1: 0, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 0, # 'e' + 18: 3, # 'f' + 27: 0, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 3, # 'r' + 8: 2, # 's' + 9: 2, # 't' + 14: 1, # 'u' + 32: 3, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 1, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 1, # 'ü' + 30: 2, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 1, # 'Ş' + 19: 2, # 'ş' + }, + 19: { # 'ş' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 2, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 1, # 'h' + 3: 1, # 'i' + 24: 0, # 'j' + 10: 2, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 1, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 1, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, +} + +# 255: Undefined characters that did not exist in training text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 +# 251: Control characters + +# Character Mapping Table(s): +ISO_8859_9_TURKISH_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 255, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 255, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 255, # ' ' + 33: 255, # '!' + 34: 255, # '"' + 35: 255, # '#' + 36: 255, # '$' + 37: 255, # '%' + 38: 255, # '&' + 39: 255, # "'" + 40: 255, # '(' + 41: 255, # ')' + 42: 255, # '*' + 43: 255, # '+' + 44: 255, # ',' + 45: 255, # '-' + 46: 255, # '.' + 47: 255, # '/' + 48: 255, # '0' + 49: 255, # '1' + 50: 255, # '2' + 51: 255, # '3' + 52: 255, # '4' + 53: 255, # '5' + 54: 255, # '6' + 55: 255, # '7' + 56: 255, # '8' + 57: 255, # '9' + 58: 255, # ':' + 59: 255, # ';' + 60: 255, # '<' + 61: 255, # '=' + 62: 255, # '>' + 63: 255, # '?' + 64: 255, # '@' + 65: 23, # 'A' + 66: 37, # 'B' + 67: 47, # 'C' + 68: 39, # 'D' + 69: 29, # 'E' + 70: 52, # 'F' + 71: 36, # 'G' + 72: 45, # 'H' + 73: 53, # 'I' + 74: 60, # 'J' + 75: 16, # 'K' + 76: 49, # 'L' + 77: 20, # 'M' + 78: 46, # 'N' + 79: 42, # 'O' + 80: 48, # 'P' + 81: 69, # 'Q' + 82: 44, # 'R' + 83: 35, # 'S' + 84: 31, # 'T' + 85: 51, # 'U' + 86: 38, # 'V' + 87: 62, # 'W' + 88: 65, # 'X' + 89: 43, # 'Y' + 90: 56, # 'Z' + 91: 255, # '[' + 92: 255, # '\\' + 93: 255, # ']' + 94: 255, # '^' + 95: 255, # '_' + 96: 255, # '`' + 97: 1, # 'a' + 98: 21, # 'b' + 99: 28, # 'c' + 100: 12, # 'd' + 101: 2, # 'e' + 102: 18, # 'f' + 103: 27, # 'g' + 104: 25, # 'h' + 105: 3, # 'i' + 106: 24, # 'j' + 107: 10, # 'k' + 108: 5, # 'l' + 109: 13, # 'm' + 110: 4, # 'n' + 111: 15, # 'o' + 112: 26, # 'p' + 113: 64, # 'q' + 114: 7, # 'r' + 115: 8, # 's' + 116: 9, # 't' + 117: 14, # 'u' + 118: 32, # 'v' + 119: 57, # 'w' + 120: 58, # 'x' + 121: 11, # 'y' + 122: 22, # 'z' + 123: 255, # '{' + 124: 255, # '|' + 125: 255, # '}' + 126: 255, # '~' + 127: 255, # '\x7f' + 128: 180, # '\x80' + 129: 179, # '\x81' + 130: 178, # '\x82' + 131: 177, # '\x83' + 132: 176, # '\x84' + 133: 175, # '\x85' + 134: 174, # '\x86' + 135: 173, # '\x87' + 136: 172, # '\x88' + 137: 171, # '\x89' + 138: 170, # '\x8a' + 139: 169, # '\x8b' + 140: 168, # '\x8c' + 141: 167, # '\x8d' + 142: 166, # '\x8e' + 143: 165, # '\x8f' + 144: 164, # '\x90' + 145: 163, # '\x91' + 146: 162, # '\x92' + 147: 161, # '\x93' + 148: 160, # '\x94' + 149: 159, # '\x95' + 150: 101, # '\x96' + 151: 158, # '\x97' + 152: 157, # '\x98' + 153: 156, # '\x99' + 154: 155, # '\x9a' + 155: 154, # '\x9b' + 156: 153, # '\x9c' + 157: 152, # '\x9d' + 158: 151, # '\x9e' + 159: 106, # '\x9f' + 160: 150, # '\xa0' + 161: 149, # '¡' + 162: 148, # '¢' + 163: 147, # '£' + 164: 146, # '¤' + 165: 145, # '¥' + 166: 144, # '¦' + 167: 100, # '§' + 168: 143, # '¨' + 169: 142, # '©' + 170: 141, # 'ª' + 171: 140, # '«' + 172: 139, # '¬' + 173: 138, # '\xad' + 174: 137, # '®' + 175: 136, # '¯' + 176: 94, # '°' + 177: 80, # '±' + 178: 93, # '²' + 179: 135, # '³' + 180: 105, # '´' + 181: 134, # 'µ' + 182: 133, # '¶' + 183: 63, # '·' + 184: 132, # '¸' + 185: 131, # '¹' + 186: 130, # 'º' + 187: 129, # '»' + 188: 128, # '¼' + 189: 127, # '½' + 190: 126, # '¾' + 191: 125, # '¿' + 192: 124, # 'À' + 193: 104, # 'Á' + 194: 73, # 'Â' + 195: 99, # 'Ã' + 196: 79, # 'Ä' + 197: 85, # 'Å' + 198: 123, # 'Æ' + 199: 54, # 'Ç' + 200: 122, # 'È' + 201: 98, # 'É' + 202: 92, # 'Ê' + 203: 121, # 'Ë' + 204: 120, # 'Ì' + 205: 91, # 'Í' + 206: 103, # 'Î' + 207: 119, # 'Ï' + 208: 68, # 'Ğ' + 209: 118, # 'Ñ' + 210: 117, # 'Ò' + 211: 97, # 'Ó' + 212: 116, # 'Ô' + 213: 115, # 'Õ' + 214: 50, # 'Ö' + 215: 90, # '×' + 216: 114, # 'Ø' + 217: 113, # 'Ù' + 218: 112, # 'Ú' + 219: 111, # 'Û' + 220: 55, # 'Ü' + 221: 41, # 'İ' + 222: 40, # 'Ş' + 223: 86, # 'ß' + 224: 89, # 'à' + 225: 70, # 'á' + 226: 59, # 'â' + 227: 78, # 'ã' + 228: 71, # 'ä' + 229: 82, # 'å' + 230: 88, # 'æ' + 231: 33, # 'ç' + 232: 77, # 'è' + 233: 66, # 'é' + 234: 84, # 'ê' + 235: 83, # 'ë' + 236: 110, # 'ì' + 237: 75, # 'í' + 238: 61, # 'î' + 239: 96, # 'ï' + 240: 30, # 'ğ' + 241: 67, # 'ñ' + 242: 109, # 'ò' + 243: 74, # 'ó' + 244: 87, # 'ô' + 245: 102, # 'õ' + 246: 34, # 'ö' + 247: 95, # '÷' + 248: 81, # 'ø' + 249: 108, # 'ù' + 250: 76, # 'ú' + 251: 72, # 'û' + 252: 17, # 'ü' + 253: 6, # 'ı' + 254: 19, # 'ş' + 255: 107, # 'ÿ' +} + +ISO_8859_9_TURKISH_MODEL = SingleByteCharSetModel( + charset_name="ISO-8859-9", + language="Turkish", + char_to_order_map=ISO_8859_9_TURKISH_CHAR_TO_ORDER, + language_model=TURKISH_LANG_MODEL, + typical_positive_ratio=0.97029, + keep_ascii_letters=True, + alphabet="ABCDEFGHIJKLMNOPRSTUVYZabcdefghijklmnoprstuvyzÂÇÎÖÛÜâçîöûüĞğİıŞş", +) diff --git a/virt/lib/python3.9/site-packages/pip/_vendor/distlib/database 3.py b/virt/lib/python3.9/site-packages/pip/_vendor/distlib/database 3.py new file mode 100644 index 00000000..61682e96 --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_vendor/distlib/database 3.py @@ -0,0 +1,1350 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2017 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""PEP 376 implementation.""" + +from __future__ import unicode_literals + +import base64 +import codecs +import contextlib +import hashlib +import logging +import os +import posixpath +import sys +import zipimport + +from . import DistlibException, resources +from .compat import StringIO +from .version import get_scheme, UnsupportedVersionError +from .metadata import (Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME, + LEGACY_METADATA_FILENAME) +from .util import (parse_requirement, cached_property, parse_name_and_version, + read_exports, write_exports, CSVReader, CSVWriter) + + +__all__ = ['Distribution', 'BaseInstalledDistribution', + 'InstalledDistribution', 'EggInfoDistribution', + 'DistributionPath'] + + +logger = logging.getLogger(__name__) + +EXPORTS_FILENAME = 'pydist-exports.json' +COMMANDS_FILENAME = 'pydist-commands.json' + +DIST_FILES = ('INSTALLER', METADATA_FILENAME, 'RECORD', 'REQUESTED', + 'RESOURCES', EXPORTS_FILENAME, 'SHARED') + +DISTINFO_EXT = '.dist-info' + + +class _Cache(object): + """ + A simple cache mapping names and .dist-info paths to distributions + """ + def __init__(self): + """ + Initialise an instance. There is normally one for each DistributionPath. + """ + self.name = {} + self.path = {} + self.generated = False + + def clear(self): + """ + Clear the cache, setting it to its initial state. + """ + self.name.clear() + self.path.clear() + self.generated = False + + def add(self, dist): + """ + Add a distribution to the cache. + :param dist: The distribution to add. + """ + if dist.path not in self.path: + self.path[dist.path] = dist + self.name.setdefault(dist.key, []).append(dist) + + +class DistributionPath(object): + """ + Represents a set of distributions installed on a path (typically sys.path). + """ + def __init__(self, path=None, include_egg=False): + """ + Create an instance from a path, optionally including legacy (distutils/ + setuptools/distribute) distributions. + :param path: The path to use, as a list of directories. If not specified, + sys.path is used. + :param include_egg: If True, this instance will look for and return legacy + distributions as well as those based on PEP 376. + """ + if path is None: + path = sys.path + self.path = path + self._include_dist = True + self._include_egg = include_egg + + self._cache = _Cache() + self._cache_egg = _Cache() + self._cache_enabled = True + self._scheme = get_scheme('default') + + def _get_cache_enabled(self): + return self._cache_enabled + + def _set_cache_enabled(self, value): + self._cache_enabled = value + + cache_enabled = property(_get_cache_enabled, _set_cache_enabled) + + def clear_cache(self): + """ + Clears the internal cache. + """ + self._cache.clear() + self._cache_egg.clear() + + + def _yield_distributions(self): + """ + Yield .dist-info and/or .egg(-info) distributions. + """ + # We need to check if we've seen some resources already, because on + # some Linux systems (e.g. some Debian/Ubuntu variants) there are + # symlinks which alias other files in the environment. + seen = set() + for path in self.path: + finder = resources.finder_for_path(path) + if finder is None: + continue + r = finder.find('') + if not r or not r.is_container: + continue + rset = sorted(r.resources) + for entry in rset: + r = finder.find(entry) + if not r or r.path in seen: + continue + try: + if self._include_dist and entry.endswith(DISTINFO_EXT): + possible_filenames = [METADATA_FILENAME, + WHEEL_METADATA_FILENAME, + LEGACY_METADATA_FILENAME] + for metadata_filename in possible_filenames: + metadata_path = posixpath.join(entry, metadata_filename) + pydist = finder.find(metadata_path) + if pydist: + break + else: + continue + + with contextlib.closing(pydist.as_stream()) as stream: + metadata = Metadata(fileobj=stream, scheme='legacy') + logger.debug('Found %s', r.path) + seen.add(r.path) + yield new_dist_class(r.path, metadata=metadata, + env=self) + elif self._include_egg and entry.endswith(('.egg-info', + '.egg')): + logger.debug('Found %s', r.path) + seen.add(r.path) + yield old_dist_class(r.path, self) + except Exception as e: + msg = 'Unable to read distribution at %s, perhaps due to bad metadata: %s' + logger.warning(msg, r.path, e) + import warnings + warnings.warn(msg % (r.path, e), stacklevel=2) + + def _generate_cache(self): + """ + Scan the path for distributions and populate the cache with + those that are found. + """ + gen_dist = not self._cache.generated + gen_egg = self._include_egg and not self._cache_egg.generated + if gen_dist or gen_egg: + for dist in self._yield_distributions(): + if isinstance(dist, InstalledDistribution): + self._cache.add(dist) + else: + self._cache_egg.add(dist) + + if gen_dist: + self._cache.generated = True + if gen_egg: + self._cache_egg.generated = True + + @classmethod + def distinfo_dirname(cls, name, version): + """ + The *name* and *version* parameters are converted into their + filename-escaped form, i.e. any ``'-'`` characters are replaced + with ``'_'`` other than the one in ``'dist-info'`` and the one + separating the name from the version number. + + :parameter name: is converted to a standard distribution name by replacing + any runs of non- alphanumeric characters with a single + ``'-'``. + :type name: string + :parameter version: is converted to a standard version string. Spaces + become dots, and all other non-alphanumeric characters + (except dots) become dashes, with runs of multiple + dashes condensed to a single dash. + :type version: string + :returns: directory name + :rtype: string""" + name = name.replace('-', '_') + return '-'.join([name, version]) + DISTINFO_EXT + + def get_distributions(self): + """ + Provides an iterator that looks for distributions and returns + :class:`InstalledDistribution` or + :class:`EggInfoDistribution` instances for each one of them. + + :rtype: iterator of :class:`InstalledDistribution` and + :class:`EggInfoDistribution` instances + """ + if not self._cache_enabled: + for dist in self._yield_distributions(): + yield dist + else: + self._generate_cache() + + for dist in self._cache.path.values(): + yield dist + + if self._include_egg: + for dist in self._cache_egg.path.values(): + yield dist + + def get_distribution(self, name): + """ + Looks for a named distribution on the path. + + This function only returns the first result found, as no more than one + value is expected. If nothing is found, ``None`` is returned. + + :rtype: :class:`InstalledDistribution`, :class:`EggInfoDistribution` + or ``None`` + """ + result = None + name = name.lower() + if not self._cache_enabled: + for dist in self._yield_distributions(): + if dist.key == name: + result = dist + break + else: + self._generate_cache() + + if name in self._cache.name: + result = self._cache.name[name][0] + elif self._include_egg and name in self._cache_egg.name: + result = self._cache_egg.name[name][0] + return result + + def provides_distribution(self, name, version=None): + """ + Iterates over all distributions to find which distributions provide *name*. + If a *version* is provided, it will be used to filter the results. + + This function only returns the first result found, since no more than + one values are expected. If the directory is not found, returns ``None``. + + :parameter version: a version specifier that indicates the version + required, conforming to the format in ``PEP-345`` + + :type name: string + :type version: string + """ + matcher = None + if version is not None: + try: + matcher = self._scheme.matcher('%s (%s)' % (name, version)) + except ValueError: + raise DistlibException('invalid name or version: %r, %r' % + (name, version)) + + for dist in self.get_distributions(): + # We hit a problem on Travis where enum34 was installed and doesn't + # have a provides attribute ... + if not hasattr(dist, 'provides'): + logger.debug('No "provides": %s', dist) + else: + provided = dist.provides + + for p in provided: + p_name, p_ver = parse_name_and_version(p) + if matcher is None: + if p_name == name: + yield dist + break + else: + if p_name == name and matcher.match(p_ver): + yield dist + break + + def get_file_path(self, name, relative_path): + """ + Return the path to a resource file. + """ + dist = self.get_distribution(name) + if dist is None: + raise LookupError('no distribution named %r found' % name) + return dist.get_resource_path(relative_path) + + def get_exported_entries(self, category, name=None): + """ + Return all of the exported entries in a particular category. + + :param category: The category to search for entries. + :param name: If specified, only entries with that name are returned. + """ + for dist in self.get_distributions(): + r = dist.exports + if category in r: + d = r[category] + if name is not None: + if name in d: + yield d[name] + else: + for v in d.values(): + yield v + + +class Distribution(object): + """ + A base class for distributions, whether installed or from indexes. + Either way, it must have some metadata, so that's all that's needed + for construction. + """ + + build_time_dependency = False + """ + Set to True if it's known to be only a build-time dependency (i.e. + not needed after installation). + """ + + requested = False + """A boolean that indicates whether the ``REQUESTED`` metadata file is + present (in other words, whether the package was installed by user + request or it was installed as a dependency).""" + + def __init__(self, metadata): + """ + Initialise an instance. + :param metadata: The instance of :class:`Metadata` describing this + distribution. + """ + self.metadata = metadata + self.name = metadata.name + self.key = self.name.lower() # for case-insensitive comparisons + self.version = metadata.version + self.locator = None + self.digest = None + self.extras = None # additional features requested + self.context = None # environment marker overrides + self.download_urls = set() + self.digests = {} + + @property + def source_url(self): + """ + The source archive download URL for this distribution. + """ + return self.metadata.source_url + + download_url = source_url # Backward compatibility + + @property + def name_and_version(self): + """ + A utility property which displays the name and version in parentheses. + """ + return '%s (%s)' % (self.name, self.version) + + @property + def provides(self): + """ + A set of distribution names and versions provided by this distribution. + :return: A set of "name (version)" strings. + """ + plist = self.metadata.provides + s = '%s (%s)' % (self.name, self.version) + if s not in plist: + plist.append(s) + return plist + + def _get_requirements(self, req_attr): + md = self.metadata + reqts = getattr(md, req_attr) + logger.debug('%s: got requirements %r from metadata: %r', self.name, req_attr, + reqts) + return set(md.get_requirements(reqts, extras=self.extras, + env=self.context)) + + @property + def run_requires(self): + return self._get_requirements('run_requires') + + @property + def meta_requires(self): + return self._get_requirements('meta_requires') + + @property + def build_requires(self): + return self._get_requirements('build_requires') + + @property + def test_requires(self): + return self._get_requirements('test_requires') + + @property + def dev_requires(self): + return self._get_requirements('dev_requires') + + def matches_requirement(self, req): + """ + Say if this instance matches (fulfills) a requirement. + :param req: The requirement to match. + :rtype req: str + :return: True if it matches, else False. + """ + # Requirement may contain extras - parse to lose those + # from what's passed to the matcher + r = parse_requirement(req) + scheme = get_scheme(self.metadata.scheme) + try: + matcher = scheme.matcher(r.requirement) + except UnsupportedVersionError: + # XXX compat-mode if cannot read the version + logger.warning('could not read version %r - using name only', + req) + name = req.split()[0] + matcher = scheme.matcher(name) + + name = matcher.key # case-insensitive + + result = False + for p in self.provides: + p_name, p_ver = parse_name_and_version(p) + if p_name != name: + continue + try: + result = matcher.match(p_ver) + break + except UnsupportedVersionError: + pass + return result + + def __repr__(self): + """ + Return a textual representation of this instance, + """ + if self.source_url: + suffix = ' [%s]' % self.source_url + else: + suffix = '' + return '<Distribution %s (%s)%s>' % (self.name, self.version, suffix) + + def __eq__(self, other): + """ + See if this distribution is the same as another. + :param other: The distribution to compare with. To be equal to one + another. distributions must have the same type, name, + version and source_url. + :return: True if it is the same, else False. + """ + if type(other) is not type(self): + result = False + else: + result = (self.name == other.name and + self.version == other.version and + self.source_url == other.source_url) + return result + + def __hash__(self): + """ + Compute hash in a way which matches the equality test. + """ + return hash(self.name) + hash(self.version) + hash(self.source_url) + + +class BaseInstalledDistribution(Distribution): + """ + This is the base class for installed distributions (whether PEP 376 or + legacy). + """ + + hasher = None + + def __init__(self, metadata, path, env=None): + """ + Initialise an instance. + :param metadata: An instance of :class:`Metadata` which describes the + distribution. This will normally have been initialised + from a metadata file in the ``path``. + :param path: The path of the ``.dist-info`` or ``.egg-info`` + directory for the distribution. + :param env: This is normally the :class:`DistributionPath` + instance where this distribution was found. + """ + super(BaseInstalledDistribution, self).__init__(metadata) + self.path = path + self.dist_path = env + + def get_hash(self, data, hasher=None): + """ + Get the hash of some data, using a particular hash algorithm, if + specified. + + :param data: The data to be hashed. + :type data: bytes + :param hasher: The name of a hash implementation, supported by hashlib, + or ``None``. Examples of valid values are ``'sha1'``, + ``'sha224'``, ``'sha384'``, '``sha256'``, ``'md5'`` and + ``'sha512'``. If no hasher is specified, the ``hasher`` + attribute of the :class:`InstalledDistribution` instance + is used. If the hasher is determined to be ``None``, MD5 + is used as the hashing algorithm. + :returns: The hash of the data. If a hasher was explicitly specified, + the returned hash will be prefixed with the specified hasher + followed by '='. + :rtype: str + """ + if hasher is None: + hasher = self.hasher + if hasher is None: + hasher = hashlib.md5 + prefix = '' + else: + hasher = getattr(hashlib, hasher) + prefix = '%s=' % self.hasher + digest = hasher(data).digest() + digest = base64.urlsafe_b64encode(digest).rstrip(b'=').decode('ascii') + return '%s%s' % (prefix, digest) + + +class InstalledDistribution(BaseInstalledDistribution): + """ + Created with the *path* of the ``.dist-info`` directory provided to the + constructor. It reads the metadata contained in ``pydist.json`` when it is + instantiated., or uses a passed in Metadata instance (useful for when + dry-run mode is being used). + """ + + hasher = 'sha256' + + def __init__(self, path, metadata=None, env=None): + self.modules = [] + self.finder = finder = resources.finder_for_path(path) + if finder is None: + raise ValueError('finder unavailable for %s' % path) + if env and env._cache_enabled and path in env._cache.path: + metadata = env._cache.path[path].metadata + elif metadata is None: + r = finder.find(METADATA_FILENAME) + # Temporary - for Wheel 0.23 support + if r is None: + r = finder.find(WHEEL_METADATA_FILENAME) + # Temporary - for legacy support + if r is None: + r = finder.find(LEGACY_METADATA_FILENAME) + if r is None: + raise ValueError('no %s found in %s' % (METADATA_FILENAME, + path)) + with contextlib.closing(r.as_stream()) as stream: + metadata = Metadata(fileobj=stream, scheme='legacy') + + super(InstalledDistribution, self).__init__(metadata, path, env) + + if env and env._cache_enabled: + env._cache.add(self) + + r = finder.find('REQUESTED') + self.requested = r is not None + p = os.path.join(path, 'top_level.txt') + if os.path.exists(p): + with open(p, 'rb') as f: + data = f.read().decode('utf-8') + self.modules = data.splitlines() + + def __repr__(self): + return '<InstalledDistribution %r %s at %r>' % ( + self.name, self.version, self.path) + + def __str__(self): + return "%s %s" % (self.name, self.version) + + def _get_records(self): + """ + Get the list of installed files for the distribution + :return: A list of tuples of path, hash and size. Note that hash and + size might be ``None`` for some entries. The path is exactly + as stored in the file (which is as in PEP 376). + """ + results = [] + r = self.get_distinfo_resource('RECORD') + with contextlib.closing(r.as_stream()) as stream: + with CSVReader(stream=stream) as record_reader: + # Base location is parent dir of .dist-info dir + #base_location = os.path.dirname(self.path) + #base_location = os.path.abspath(base_location) + for row in record_reader: + missing = [None for i in range(len(row), 3)] + path, checksum, size = row + missing + #if not os.path.isabs(path): + # path = path.replace('/', os.sep) + # path = os.path.join(base_location, path) + results.append((path, checksum, size)) + return results + + @cached_property + def exports(self): + """ + Return the information exported by this distribution. + :return: A dictionary of exports, mapping an export category to a dict + of :class:`ExportEntry` instances describing the individual + export entries, and keyed by name. + """ + result = {} + r = self.get_distinfo_resource(EXPORTS_FILENAME) + if r: + result = self.read_exports() + return result + + def read_exports(self): + """ + Read exports data from a file in .ini format. + + :return: A dictionary of exports, mapping an export category to a list + of :class:`ExportEntry` instances describing the individual + export entries. + """ + result = {} + r = self.get_distinfo_resource(EXPORTS_FILENAME) + if r: + with contextlib.closing(r.as_stream()) as stream: + result = read_exports(stream) + return result + + def write_exports(self, exports): + """ + Write a dictionary of exports to a file in .ini format. + :param exports: A dictionary of exports, mapping an export category to + a list of :class:`ExportEntry` instances describing the + individual export entries. + """ + rf = self.get_distinfo_file(EXPORTS_FILENAME) + with open(rf, 'w') as f: + write_exports(exports, f) + + def get_resource_path(self, relative_path): + """ + NOTE: This API may change in the future. + + Return the absolute path to a resource file with the given relative + path. + + :param relative_path: The path, relative to .dist-info, of the resource + of interest. + :return: The absolute path where the resource is to be found. + """ + r = self.get_distinfo_resource('RESOURCES') + with contextlib.closing(r.as_stream()) as stream: + with CSVReader(stream=stream) as resources_reader: + for relative, destination in resources_reader: + if relative == relative_path: + return destination + raise KeyError('no resource file with relative path %r ' + 'is installed' % relative_path) + + def list_installed_files(self): + """ + Iterates over the ``RECORD`` entries and returns a tuple + ``(path, hash, size)`` for each line. + + :returns: iterator of (path, hash, size) + """ + for result in self._get_records(): + yield result + + def write_installed_files(self, paths, prefix, dry_run=False): + """ + Writes the ``RECORD`` file, using the ``paths`` iterable passed in. Any + existing ``RECORD`` file is silently overwritten. + + prefix is used to determine when to write absolute paths. + """ + prefix = os.path.join(prefix, '') + base = os.path.dirname(self.path) + base_under_prefix = base.startswith(prefix) + base = os.path.join(base, '') + record_path = self.get_distinfo_file('RECORD') + logger.info('creating %s', record_path) + if dry_run: + return None + with CSVWriter(record_path) as writer: + for path in paths: + if os.path.isdir(path) or path.endswith(('.pyc', '.pyo')): + # do not put size and hash, as in PEP-376 + hash_value = size = '' + else: + size = '%d' % os.path.getsize(path) + with open(path, 'rb') as fp: + hash_value = self.get_hash(fp.read()) + if path.startswith(base) or (base_under_prefix and + path.startswith(prefix)): + path = os.path.relpath(path, base) + writer.writerow((path, hash_value, size)) + + # add the RECORD file itself + if record_path.startswith(base): + record_path = os.path.relpath(record_path, base) + writer.writerow((record_path, '', '')) + return record_path + + def check_installed_files(self): + """ + Checks that the hashes and sizes of the files in ``RECORD`` are + matched by the files themselves. Returns a (possibly empty) list of + mismatches. Each entry in the mismatch list will be a tuple consisting + of the path, 'exists', 'size' or 'hash' according to what didn't match + (existence is checked first, then size, then hash), the expected + value and the actual value. + """ + mismatches = [] + base = os.path.dirname(self.path) + record_path = self.get_distinfo_file('RECORD') + for path, hash_value, size in self.list_installed_files(): + if not os.path.isabs(path): + path = os.path.join(base, path) + if path == record_path: + continue + if not os.path.exists(path): + mismatches.append((path, 'exists', True, False)) + elif os.path.isfile(path): + actual_size = str(os.path.getsize(path)) + if size and actual_size != size: + mismatches.append((path, 'size', size, actual_size)) + elif hash_value: + if '=' in hash_value: + hasher = hash_value.split('=', 1)[0] + else: + hasher = None + + with open(path, 'rb') as f: + actual_hash = self.get_hash(f.read(), hasher) + if actual_hash != hash_value: + mismatches.append((path, 'hash', hash_value, actual_hash)) + return mismatches + + @cached_property + def shared_locations(self): + """ + A dictionary of shared locations whose keys are in the set 'prefix', + 'purelib', 'platlib', 'scripts', 'headers', 'data' and 'namespace'. + The corresponding value is the absolute path of that category for + this distribution, and takes into account any paths selected by the + user at installation time (e.g. via command-line arguments). In the + case of the 'namespace' key, this would be a list of absolute paths + for the roots of namespace packages in this distribution. + + The first time this property is accessed, the relevant information is + read from the SHARED file in the .dist-info directory. + """ + result = {} + shared_path = os.path.join(self.path, 'SHARED') + if os.path.isfile(shared_path): + with codecs.open(shared_path, 'r', encoding='utf-8') as f: + lines = f.read().splitlines() + for line in lines: + key, value = line.split('=', 1) + if key == 'namespace': + result.setdefault(key, []).append(value) + else: + result[key] = value + return result + + def write_shared_locations(self, paths, dry_run=False): + """ + Write shared location information to the SHARED file in .dist-info. + :param paths: A dictionary as described in the documentation for + :meth:`shared_locations`. + :param dry_run: If True, the action is logged but no file is actually + written. + :return: The path of the file written to. + """ + shared_path = os.path.join(self.path, 'SHARED') + logger.info('creating %s', shared_path) + if dry_run: + return None + lines = [] + for key in ('prefix', 'lib', 'headers', 'scripts', 'data'): + path = paths[key] + if os.path.isdir(paths[key]): + lines.append('%s=%s' % (key, path)) + for ns in paths.get('namespace', ()): + lines.append('namespace=%s' % ns) + + with codecs.open(shared_path, 'w', encoding='utf-8') as f: + f.write('\n'.join(lines)) + return shared_path + + def get_distinfo_resource(self, path): + if path not in DIST_FILES: + raise DistlibException('invalid path for a dist-info file: ' + '%r at %r' % (path, self.path)) + finder = resources.finder_for_path(self.path) + if finder is None: + raise DistlibException('Unable to get a finder for %s' % self.path) + return finder.find(path) + + def get_distinfo_file(self, path): + """ + Returns a path located under the ``.dist-info`` directory. Returns a + string representing the path. + + :parameter path: a ``'/'``-separated path relative to the + ``.dist-info`` directory or an absolute path; + If *path* is an absolute path and doesn't start + with the ``.dist-info`` directory path, + a :class:`DistlibException` is raised + :type path: str + :rtype: str + """ + # Check if it is an absolute path # XXX use relpath, add tests + if path.find(os.sep) >= 0: + # it's an absolute path? + distinfo_dirname, path = path.split(os.sep)[-2:] + if distinfo_dirname != self.path.split(os.sep)[-1]: + raise DistlibException( + 'dist-info file %r does not belong to the %r %s ' + 'distribution' % (path, self.name, self.version)) + + # The file must be relative + if path not in DIST_FILES: + raise DistlibException('invalid path for a dist-info file: ' + '%r at %r' % (path, self.path)) + + return os.path.join(self.path, path) + + def list_distinfo_files(self): + """ + Iterates over the ``RECORD`` entries and returns paths for each line if + the path is pointing to a file located in the ``.dist-info`` directory + or one of its subdirectories. + + :returns: iterator of paths + """ + base = os.path.dirname(self.path) + for path, checksum, size in self._get_records(): + # XXX add separator or use real relpath algo + if not os.path.isabs(path): + path = os.path.join(base, path) + if path.startswith(self.path): + yield path + + def __eq__(self, other): + return (isinstance(other, InstalledDistribution) and + self.path == other.path) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + + +class EggInfoDistribution(BaseInstalledDistribution): + """Created with the *path* of the ``.egg-info`` directory or file provided + to the constructor. It reads the metadata contained in the file itself, or + if the given path happens to be a directory, the metadata is read from the + file ``PKG-INFO`` under that directory.""" + + requested = True # as we have no way of knowing, assume it was + shared_locations = {} + + def __init__(self, path, env=None): + def set_name_and_version(s, n, v): + s.name = n + s.key = n.lower() # for case-insensitive comparisons + s.version = v + + self.path = path + self.dist_path = env + if env and env._cache_enabled and path in env._cache_egg.path: + metadata = env._cache_egg.path[path].metadata + set_name_and_version(self, metadata.name, metadata.version) + else: + metadata = self._get_metadata(path) + + # Need to be set before caching + set_name_and_version(self, metadata.name, metadata.version) + + if env and env._cache_enabled: + env._cache_egg.add(self) + super(EggInfoDistribution, self).__init__(metadata, path, env) + + def _get_metadata(self, path): + requires = None + + def parse_requires_data(data): + """Create a list of dependencies from a requires.txt file. + + *data*: the contents of a setuptools-produced requires.txt file. + """ + reqs = [] + lines = data.splitlines() + for line in lines: + line = line.strip() + if line.startswith('['): + logger.warning('Unexpected line: quitting requirement scan: %r', + line) + break + r = parse_requirement(line) + if not r: + logger.warning('Not recognised as a requirement: %r', line) + continue + if r.extras: + logger.warning('extra requirements in requires.txt are ' + 'not supported') + if not r.constraints: + reqs.append(r.name) + else: + cons = ', '.join('%s%s' % c for c in r.constraints) + reqs.append('%s (%s)' % (r.name, cons)) + return reqs + + def parse_requires_path(req_path): + """Create a list of dependencies from a requires.txt file. + + *req_path*: the path to a setuptools-produced requires.txt file. + """ + + reqs = [] + try: + with codecs.open(req_path, 'r', 'utf-8') as fp: + reqs = parse_requires_data(fp.read()) + except IOError: + pass + return reqs + + tl_path = tl_data = None + if path.endswith('.egg'): + if os.path.isdir(path): + p = os.path.join(path, 'EGG-INFO') + meta_path = os.path.join(p, 'PKG-INFO') + metadata = Metadata(path=meta_path, scheme='legacy') + req_path = os.path.join(p, 'requires.txt') + tl_path = os.path.join(p, 'top_level.txt') + requires = parse_requires_path(req_path) + else: + # FIXME handle the case where zipfile is not available + zipf = zipimport.zipimporter(path) + fileobj = StringIO( + zipf.get_data('EGG-INFO/PKG-INFO').decode('utf8')) + metadata = Metadata(fileobj=fileobj, scheme='legacy') + try: + data = zipf.get_data('EGG-INFO/requires.txt') + tl_data = zipf.get_data('EGG-INFO/top_level.txt').decode('utf-8') + requires = parse_requires_data(data.decode('utf-8')) + except IOError: + requires = None + elif path.endswith('.egg-info'): + if os.path.isdir(path): + req_path = os.path.join(path, 'requires.txt') + requires = parse_requires_path(req_path) + path = os.path.join(path, 'PKG-INFO') + tl_path = os.path.join(path, 'top_level.txt') + metadata = Metadata(path=path, scheme='legacy') + else: + raise DistlibException('path must end with .egg-info or .egg, ' + 'got %r' % path) + + if requires: + metadata.add_requirements(requires) + # look for top-level modules in top_level.txt, if present + if tl_data is None: + if tl_path is not None and os.path.exists(tl_path): + with open(tl_path, 'rb') as f: + tl_data = f.read().decode('utf-8') + if not tl_data: + tl_data = [] + else: + tl_data = tl_data.splitlines() + self.modules = tl_data + return metadata + + def __repr__(self): + return '<EggInfoDistribution %r %s at %r>' % ( + self.name, self.version, self.path) + + def __str__(self): + return "%s %s" % (self.name, self.version) + + def check_installed_files(self): + """ + Checks that the hashes and sizes of the files in ``RECORD`` are + matched by the files themselves. Returns a (possibly empty) list of + mismatches. Each entry in the mismatch list will be a tuple consisting + of the path, 'exists', 'size' or 'hash' according to what didn't match + (existence is checked first, then size, then hash), the expected + value and the actual value. + """ + mismatches = [] + record_path = os.path.join(self.path, 'installed-files.txt') + if os.path.exists(record_path): + for path, _, _ in self.list_installed_files(): + if path == record_path: + continue + if not os.path.exists(path): + mismatches.append((path, 'exists', True, False)) + return mismatches + + def list_installed_files(self): + """ + Iterates over the ``installed-files.txt`` entries and returns a tuple + ``(path, hash, size)`` for each line. + + :returns: a list of (path, hash, size) + """ + + def _md5(path): + f = open(path, 'rb') + try: + content = f.read() + finally: + f.close() + return hashlib.md5(content).hexdigest() + + def _size(path): + return os.stat(path).st_size + + record_path = os.path.join(self.path, 'installed-files.txt') + result = [] + if os.path.exists(record_path): + with codecs.open(record_path, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + p = os.path.normpath(os.path.join(self.path, line)) + # "./" is present as a marker between installed files + # and installation metadata files + if not os.path.exists(p): + logger.warning('Non-existent file: %s', p) + if p.endswith(('.pyc', '.pyo')): + continue + #otherwise fall through and fail + if not os.path.isdir(p): + result.append((p, _md5(p), _size(p))) + result.append((record_path, None, None)) + return result + + def list_distinfo_files(self, absolute=False): + """ + Iterates over the ``installed-files.txt`` entries and returns paths for + each line if the path is pointing to a file located in the + ``.egg-info`` directory or one of its subdirectories. + + :parameter absolute: If *absolute* is ``True``, each returned path is + transformed into a local absolute path. Otherwise the + raw value from ``installed-files.txt`` is returned. + :type absolute: boolean + :returns: iterator of paths + """ + record_path = os.path.join(self.path, 'installed-files.txt') + if os.path.exists(record_path): + skip = True + with codecs.open(record_path, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + if line == './': + skip = False + continue + if not skip: + p = os.path.normpath(os.path.join(self.path, line)) + if p.startswith(self.path): + if absolute: + yield p + else: + yield line + + def __eq__(self, other): + return (isinstance(other, EggInfoDistribution) and + self.path == other.path) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + +new_dist_class = InstalledDistribution +old_dist_class = EggInfoDistribution + + +class DependencyGraph(object): + """ + Represents a dependency graph between distributions. + + The dependency relationships are stored in an ``adjacency_list`` that maps + distributions to a list of ``(other, label)`` tuples where ``other`` + is a distribution and the edge is labeled with ``label`` (i.e. the version + specifier, if such was provided). Also, for more efficient traversal, for + every distribution ``x``, a list of predecessors is kept in + ``reverse_list[x]``. An edge from distribution ``a`` to + distribution ``b`` means that ``a`` depends on ``b``. If any missing + dependencies are found, they are stored in ``missing``, which is a + dictionary that maps distributions to a list of requirements that were not + provided by any other distributions. + """ + + def __init__(self): + self.adjacency_list = {} + self.reverse_list = {} + self.missing = {} + + def add_distribution(self, distribution): + """Add the *distribution* to the graph. + + :type distribution: :class:`distutils2.database.InstalledDistribution` + or :class:`distutils2.database.EggInfoDistribution` + """ + self.adjacency_list[distribution] = [] + self.reverse_list[distribution] = [] + #self.missing[distribution] = [] + + def add_edge(self, x, y, label=None): + """Add an edge from distribution *x* to distribution *y* with the given + *label*. + + :type x: :class:`distutils2.database.InstalledDistribution` or + :class:`distutils2.database.EggInfoDistribution` + :type y: :class:`distutils2.database.InstalledDistribution` or + :class:`distutils2.database.EggInfoDistribution` + :type label: ``str`` or ``None`` + """ + self.adjacency_list[x].append((y, label)) + # multiple edges are allowed, so be careful + if x not in self.reverse_list[y]: + self.reverse_list[y].append(x) + + def add_missing(self, distribution, requirement): + """ + Add a missing *requirement* for the given *distribution*. + + :type distribution: :class:`distutils2.database.InstalledDistribution` + or :class:`distutils2.database.EggInfoDistribution` + :type requirement: ``str`` + """ + logger.debug('%s missing %r', distribution, requirement) + self.missing.setdefault(distribution, []).append(requirement) + + def _repr_dist(self, dist): + return '%s %s' % (dist.name, dist.version) + + def repr_node(self, dist, level=1): + """Prints only a subgraph""" + output = [self._repr_dist(dist)] + for other, label in self.adjacency_list[dist]: + dist = self._repr_dist(other) + if label is not None: + dist = '%s [%s]' % (dist, label) + output.append(' ' * level + str(dist)) + suboutput = self.repr_node(other, level + 1) + subs = suboutput.split('\n') + output.extend(subs[1:]) + return '\n'.join(output) + + def to_dot(self, f, skip_disconnected=True): + """Writes a DOT output for the graph to the provided file *f*. + + If *skip_disconnected* is set to ``True``, then all distributions + that are not dependent on any other distribution are skipped. + + :type f: has to support ``file``-like operations + :type skip_disconnected: ``bool`` + """ + disconnected = [] + + f.write("digraph dependencies {\n") + for dist, adjs in self.adjacency_list.items(): + if len(adjs) == 0 and not skip_disconnected: + disconnected.append(dist) + for other, label in adjs: + if not label is None: + f.write('"%s" -> "%s" [label="%s"]\n' % + (dist.name, other.name, label)) + else: + f.write('"%s" -> "%s"\n' % (dist.name, other.name)) + if not skip_disconnected and len(disconnected) > 0: + f.write('subgraph disconnected {\n') + f.write('label = "Disconnected"\n') + f.write('bgcolor = red\n') + + for dist in disconnected: + f.write('"%s"' % dist.name) + f.write('\n') + f.write('}\n') + f.write('}\n') + + def topological_sort(self): + """ + Perform a topological sort of the graph. + :return: A tuple, the first element of which is a topologically sorted + list of distributions, and the second element of which is a + list of distributions that cannot be sorted because they have + circular dependencies and so form a cycle. + """ + result = [] + # Make a shallow copy of the adjacency list + alist = {} + for k, v in self.adjacency_list.items(): + alist[k] = v[:] + while True: + # See what we can remove in this run + to_remove = [] + for k, v in list(alist.items())[:]: + if not v: + to_remove.append(k) + del alist[k] + if not to_remove: + # What's left in alist (if anything) is a cycle. + break + # Remove from the adjacency list of others + for k, v in alist.items(): + alist[k] = [(d, r) for d, r in v if d not in to_remove] + logger.debug('Moving to result: %s', + ['%s (%s)' % (d.name, d.version) for d in to_remove]) + result.extend(to_remove) + return result, list(alist.keys()) + + def __repr__(self): + """Representation of the graph""" + output = [] + for dist, adjs in self.adjacency_list.items(): + output.append(self.repr_node(dist)) + return '\n'.join(output) + + +def make_graph(dists, scheme='default'): + """Makes a dependency graph from the given distributions. + + :parameter dists: a list of distributions + :type dists: list of :class:`distutils2.database.InstalledDistribution` and + :class:`distutils2.database.EggInfoDistribution` instances + :rtype: a :class:`DependencyGraph` instance + """ + scheme = get_scheme(scheme) + graph = DependencyGraph() + provided = {} # maps names to lists of (version, dist) tuples + + # first, build the graph and find out what's provided + for dist in dists: + graph.add_distribution(dist) + + for p in dist.provides: + name, version = parse_name_and_version(p) + logger.debug('Add to provided: %s, %s, %s', name, version, dist) + provided.setdefault(name, []).append((version, dist)) + + # now make the edges + for dist in dists: + requires = (dist.run_requires | dist.meta_requires | + dist.build_requires | dist.dev_requires) + for req in requires: + try: + matcher = scheme.matcher(req) + except UnsupportedVersionError: + # XXX compat-mode if cannot read the version + logger.warning('could not read version %r - using name only', + req) + name = req.split()[0] + matcher = scheme.matcher(name) + + name = matcher.key # case-insensitive + + matched = False + if name in provided: + for version, provider in provided[name]: + try: + match = matcher.match(version) + except UnsupportedVersionError: + match = False + + if match: + graph.add_edge(dist, provider, req) + matched = True + break + if not matched: + graph.add_missing(dist, req) + return graph + + +def get_dependent_dists(dists, dist): + """Recursively generate a list of distributions from *dists* that are + dependent on *dist*. + + :param dists: a list of distributions + :param dist: a distribution, member of *dists* for which we are interested + """ + if dist not in dists: + raise DistlibException('given distribution %r is not a member ' + 'of the list' % dist.name) + graph = make_graph(dists) + + dep = [dist] # dependent distributions + todo = graph.reverse_list[dist] # list of nodes we should inspect + + while todo: + d = todo.pop() + dep.append(d) + for succ in graph.reverse_list[d]: + if succ not in dep: + todo.append(succ) + + dep.pop(0) # remove dist from dep, was there to prevent infinite loops + return dep + + +def get_required_dists(dists, dist): + """Recursively generate a list of distributions from *dists* that are + required by *dist*. + + :param dists: a list of distributions + :param dist: a distribution, member of *dists* for which we are interested + in finding the dependencies. + """ + if dist not in dists: + raise DistlibException('given distribution %r is not a member ' + 'of the list' % dist.name) + graph = make_graph(dists) + + req = set() # required distributions + todo = graph.adjacency_list[dist] # list of nodes we should inspect + seen = set(t[0] for t in todo) # already added to todo + + while todo: + d = todo.pop()[0] + req.add(d) + pred_list = graph.adjacency_list[d] + for pred in pred_list: + d = pred[0] + if d not in req and d not in seen: + seen.add(d) + todo.append(pred) + return req + + +def make_dist(name, version, **kwargs): + """ + A convenience method for making a dist given just a name and version. + """ + summary = kwargs.pop('summary', 'Placeholder for summary') + md = Metadata(**kwargs) + md.name = name + md.version = version + md.summary = summary or 'Placeholder for summary' + return Distribution(md) diff --git a/virt/lib/python3.9/site-packages/pip/_vendor/distlib/index 3.py b/virt/lib/python3.9/site-packages/pip/_vendor/distlib/index 3.py new file mode 100644 index 00000000..2f1f5d58 --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_vendor/distlib/index 3.py @@ -0,0 +1,508 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +import hashlib +import logging +import os +import shutil +import subprocess +import tempfile +try: + from threading import Thread +except ImportError: # pragma: no cover + from dummy_threading import Thread + +from . import DistlibException +from .compat import (HTTPBasicAuthHandler, Request, HTTPPasswordMgr, + urlparse, build_opener, string_types) +from .util import zip_dir, ServerProxy + +logger = logging.getLogger(__name__) + +DEFAULT_INDEX = 'https://pypi.org/pypi' +DEFAULT_REALM = 'pypi' + +class PackageIndex(object): + """ + This class represents a package index compatible with PyPI, the Python + Package Index. + """ + + boundary = b'----------ThIs_Is_tHe_distlib_index_bouNdaRY_$' + + def __init__(self, url=None): + """ + Initialise an instance. + + :param url: The URL of the index. If not specified, the URL for PyPI is + used. + """ + self.url = url or DEFAULT_INDEX + self.read_configuration() + scheme, netloc, path, params, query, frag = urlparse(self.url) + if params or query or frag or scheme not in ('http', 'https'): + raise DistlibException('invalid repository: %s' % self.url) + self.password_handler = None + self.ssl_verifier = None + self.gpg = None + self.gpg_home = None + with open(os.devnull, 'w') as sink: + # Use gpg by default rather than gpg2, as gpg2 insists on + # prompting for passwords + for s in ('gpg', 'gpg2'): + try: + rc = subprocess.check_call([s, '--version'], stdout=sink, + stderr=sink) + if rc == 0: + self.gpg = s + break + except OSError: + pass + + def _get_pypirc_command(self): + """ + Get the distutils command for interacting with PyPI configurations. + :return: the command. + """ + from .util import _get_pypirc_command as cmd + return cmd() + + def read_configuration(self): + """ + Read the PyPI access configuration as supported by distutils. This populates + ``username``, ``password``, ``realm`` and ``url`` attributes from the + configuration. + """ + from .util import _load_pypirc + cfg = _load_pypirc(self) + self.username = cfg.get('username') + self.password = cfg.get('password') + self.realm = cfg.get('realm', 'pypi') + self.url = cfg.get('repository', self.url) + + def save_configuration(self): + """ + Save the PyPI access configuration. You must have set ``username`` and + ``password`` attributes before calling this method. + """ + self.check_credentials() + from .util import _store_pypirc + _store_pypirc(self) + + def check_credentials(self): + """ + Check that ``username`` and ``password`` have been set, and raise an + exception if not. + """ + if self.username is None or self.password is None: + raise DistlibException('username and password must be set') + pm = HTTPPasswordMgr() + _, netloc, _, _, _, _ = urlparse(self.url) + pm.add_password(self.realm, netloc, self.username, self.password) + self.password_handler = HTTPBasicAuthHandler(pm) + + def register(self, metadata): # pragma: no cover + """ + Register a distribution on PyPI, using the provided metadata. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the distribution to be + registered. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + metadata.validate() + d = metadata.todict() + d[':action'] = 'verify' + request = self.encode_request(d.items(), []) + response = self.send_request(request) + d[':action'] = 'submit' + request = self.encode_request(d.items(), []) + return self.send_request(request) + + def _reader(self, name, stream, outbuf): + """ + Thread runner for reading lines of from a subprocess into a buffer. + + :param name: The logical name of the stream (used for logging only). + :param stream: The stream to read from. This will typically a pipe + connected to the output stream of a subprocess. + :param outbuf: The list to append the read lines to. + """ + while True: + s = stream.readline() + if not s: + break + s = s.decode('utf-8').rstrip() + outbuf.append(s) + logger.debug('%s: %s' % (name, s)) + stream.close() + + def get_sign_command(self, filename, signer, sign_password, keystore=None): # pragma: no cover + """ + Return a suitable command for signing a file. + + :param filename: The pathname to the file to be signed. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :param keystore: The path to a directory which contains the keys + used in verification. If not specified, the + instance's ``gpg_home`` attribute is used instead. + :return: The signing command as a list suitable to be + passed to :class:`subprocess.Popen`. + """ + cmd = [self.gpg, '--status-fd', '2', '--no-tty'] + if keystore is None: + keystore = self.gpg_home + if keystore: + cmd.extend(['--homedir', keystore]) + if sign_password is not None: + cmd.extend(['--batch', '--passphrase-fd', '0']) + td = tempfile.mkdtemp() + sf = os.path.join(td, os.path.basename(filename) + '.asc') + cmd.extend(['--detach-sign', '--armor', '--local-user', + signer, '--output', sf, filename]) + logger.debug('invoking: %s', ' '.join(cmd)) + return cmd, sf + + def run_command(self, cmd, input_data=None): + """ + Run a command in a child process , passing it any input data specified. + + :param cmd: The command to run. + :param input_data: If specified, this must be a byte string containing + data to be sent to the child process. + :return: A tuple consisting of the subprocess' exit code, a list of + lines read from the subprocess' ``stdout``, and a list of + lines read from the subprocess' ``stderr``. + """ + kwargs = { + 'stdout': subprocess.PIPE, + 'stderr': subprocess.PIPE, + } + if input_data is not None: + kwargs['stdin'] = subprocess.PIPE + stdout = [] + stderr = [] + p = subprocess.Popen(cmd, **kwargs) + # We don't use communicate() here because we may need to + # get clever with interacting with the command + t1 = Thread(target=self._reader, args=('stdout', p.stdout, stdout)) + t1.start() + t2 = Thread(target=self._reader, args=('stderr', p.stderr, stderr)) + t2.start() + if input_data is not None: + p.stdin.write(input_data) + p.stdin.close() + + p.wait() + t1.join() + t2.join() + return p.returncode, stdout, stderr + + def sign_file(self, filename, signer, sign_password, keystore=None): # pragma: no cover + """ + Sign a file. + + :param filename: The pathname to the file to be signed. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :param keystore: The path to a directory which contains the keys + used in signing. If not specified, the instance's + ``gpg_home`` attribute is used instead. + :return: The absolute pathname of the file where the signature is + stored. + """ + cmd, sig_file = self.get_sign_command(filename, signer, sign_password, + keystore) + rc, stdout, stderr = self.run_command(cmd, + sign_password.encode('utf-8')) + if rc != 0: + raise DistlibException('sign command failed with error ' + 'code %s' % rc) + return sig_file + + def upload_file(self, metadata, filename, signer=None, sign_password=None, + filetype='sdist', pyversion='source', keystore=None): + """ + Upload a release file to the index. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the file to be uploaded. + :param filename: The pathname of the file to be uploaded. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :param filetype: The type of the file being uploaded. This is the + distutils command which produced that file, e.g. + ``sdist`` or ``bdist_wheel``. + :param pyversion: The version of Python which the release relates + to. For code compatible with any Python, this would + be ``source``, otherwise it would be e.g. ``3.2``. + :param keystore: The path to a directory which contains the keys + used in signing. If not specified, the instance's + ``gpg_home`` attribute is used instead. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + if not os.path.exists(filename): + raise DistlibException('not found: %s' % filename) + metadata.validate() + d = metadata.todict() + sig_file = None + if signer: + if not self.gpg: + logger.warning('no signing program available - not signed') + else: + sig_file = self.sign_file(filename, signer, sign_password, + keystore) + with open(filename, 'rb') as f: + file_data = f.read() + md5_digest = hashlib.md5(file_data).hexdigest() + sha256_digest = hashlib.sha256(file_data).hexdigest() + d.update({ + ':action': 'file_upload', + 'protocol_version': '1', + 'filetype': filetype, + 'pyversion': pyversion, + 'md5_digest': md5_digest, + 'sha256_digest': sha256_digest, + }) + files = [('content', os.path.basename(filename), file_data)] + if sig_file: + with open(sig_file, 'rb') as f: + sig_data = f.read() + files.append(('gpg_signature', os.path.basename(sig_file), + sig_data)) + shutil.rmtree(os.path.dirname(sig_file)) + request = self.encode_request(d.items(), files) + return self.send_request(request) + + def upload_documentation(self, metadata, doc_dir): # pragma: no cover + """ + Upload documentation to the index. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the documentation to be + uploaded. + :param doc_dir: The pathname of the directory which contains the + documentation. This should be the directory that + contains the ``index.html`` for the documentation. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + if not os.path.isdir(doc_dir): + raise DistlibException('not a directory: %r' % doc_dir) + fn = os.path.join(doc_dir, 'index.html') + if not os.path.exists(fn): + raise DistlibException('not found: %r' % fn) + metadata.validate() + name, version = metadata.name, metadata.version + zip_data = zip_dir(doc_dir).getvalue() + fields = [(':action', 'doc_upload'), + ('name', name), ('version', version)] + files = [('content', name, zip_data)] + request = self.encode_request(fields, files) + return self.send_request(request) + + def get_verify_command(self, signature_filename, data_filename, + keystore=None): + """ + Return a suitable command for verifying a file. + + :param signature_filename: The pathname to the file containing the + signature. + :param data_filename: The pathname to the file containing the + signed data. + :param keystore: The path to a directory which contains the keys + used in verification. If not specified, the + instance's ``gpg_home`` attribute is used instead. + :return: The verifying command as a list suitable to be + passed to :class:`subprocess.Popen`. + """ + cmd = [self.gpg, '--status-fd', '2', '--no-tty'] + if keystore is None: + keystore = self.gpg_home + if keystore: + cmd.extend(['--homedir', keystore]) + cmd.extend(['--verify', signature_filename, data_filename]) + logger.debug('invoking: %s', ' '.join(cmd)) + return cmd + + def verify_signature(self, signature_filename, data_filename, + keystore=None): + """ + Verify a signature for a file. + + :param signature_filename: The pathname to the file containing the + signature. + :param data_filename: The pathname to the file containing the + signed data. + :param keystore: The path to a directory which contains the keys + used in verification. If not specified, the + instance's ``gpg_home`` attribute is used instead. + :return: True if the signature was verified, else False. + """ + if not self.gpg: + raise DistlibException('verification unavailable because gpg ' + 'unavailable') + cmd = self.get_verify_command(signature_filename, data_filename, + keystore) + rc, stdout, stderr = self.run_command(cmd) + if rc not in (0, 1): + raise DistlibException('verify command failed with error ' + 'code %s' % rc) + return rc == 0 + + def download_file(self, url, destfile, digest=None, reporthook=None): + """ + This is a convenience method for downloading a file from an URL. + Normally, this will be a file from the index, though currently + no check is made for this (i.e. a file can be downloaded from + anywhere). + + The method is just like the :func:`urlretrieve` function in the + standard library, except that it allows digest computation to be + done during download and checking that the downloaded data + matched any expected value. + + :param url: The URL of the file to be downloaded (assumed to be + available via an HTTP GET request). + :param destfile: The pathname where the downloaded file is to be + saved. + :param digest: If specified, this must be a (hasher, value) + tuple, where hasher is the algorithm used (e.g. + ``'md5'``) and ``value`` is the expected value. + :param reporthook: The same as for :func:`urlretrieve` in the + standard library. + """ + if digest is None: + digester = None + logger.debug('No digest specified') + else: + if isinstance(digest, (list, tuple)): + hasher, digest = digest + else: + hasher = 'md5' + digester = getattr(hashlib, hasher)() + logger.debug('Digest specified: %s' % digest) + # The following code is equivalent to urlretrieve. + # We need to do it this way so that we can compute the + # digest of the file as we go. + with open(destfile, 'wb') as dfp: + # addinfourl is not a context manager on 2.x + # so we have to use try/finally + sfp = self.send_request(Request(url)) + try: + headers = sfp.info() + blocksize = 8192 + size = -1 + read = 0 + blocknum = 0 + if "content-length" in headers: + size = int(headers["Content-Length"]) + if reporthook: + reporthook(blocknum, blocksize, size) + while True: + block = sfp.read(blocksize) + if not block: + break + read += len(block) + dfp.write(block) + if digester: + digester.update(block) + blocknum += 1 + if reporthook: + reporthook(blocknum, blocksize, size) + finally: + sfp.close() + + # check that we got the whole file, if we can + if size >= 0 and read < size: + raise DistlibException( + 'retrieval incomplete: got only %d out of %d bytes' + % (read, size)) + # if we have a digest, it must match. + if digester: + actual = digester.hexdigest() + if digest != actual: + raise DistlibException('%s digest mismatch for %s: expected ' + '%s, got %s' % (hasher, destfile, + digest, actual)) + logger.debug('Digest verified: %s', digest) + + def send_request(self, req): + """ + Send a standard library :class:`Request` to PyPI and return its + response. + + :param req: The request to send. + :return: The HTTP response from PyPI (a standard library HTTPResponse). + """ + handlers = [] + if self.password_handler: + handlers.append(self.password_handler) + if self.ssl_verifier: + handlers.append(self.ssl_verifier) + opener = build_opener(*handlers) + return opener.open(req) + + def encode_request(self, fields, files): + """ + Encode fields and files for posting to an HTTP server. + + :param fields: The fields to send as a list of (fieldname, value) + tuples. + :param files: The files to send as a list of (fieldname, filename, + file_bytes) tuple. + """ + # Adapted from packaging, which in turn was adapted from + # http://code.activestate.com/recipes/146306 + + parts = [] + boundary = self.boundary + for k, values in fields: + if not isinstance(values, (list, tuple)): + values = [values] + + for v in values: + parts.extend(( + b'--' + boundary, + ('Content-Disposition: form-data; name="%s"' % + k).encode('utf-8'), + b'', + v.encode('utf-8'))) + for key, filename, value in files: + parts.extend(( + b'--' + boundary, + ('Content-Disposition: form-data; name="%s"; filename="%s"' % + (key, filename)).encode('utf-8'), + b'', + value)) + + parts.extend((b'--' + boundary + b'--', b'')) + + body = b'\r\n'.join(parts) + ct = b'multipart/form-data; boundary=' + boundary + headers = { + 'Content-type': ct, + 'Content-length': str(len(body)) + } + return Request(self.url, body, headers) + + def search(self, terms, operator=None): # pragma: no cover + if isinstance(terms, string_types): + terms = {'name': terms} + rpc_proxy = ServerProxy(self.url, timeout=3.0) + try: + return rpc_proxy.search(terms, operator or 'and') + finally: + rpc_proxy('close')() diff --git a/virt/lib/python3.9/site-packages/pip/_vendor/distlib/scripts 3.py b/virt/lib/python3.9/site-packages/pip/_vendor/distlib/scripts 3.py new file mode 100644 index 00000000..7ebb76ad --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_vendor/distlib/scripts 3.py @@ -0,0 +1,437 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2015 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from io import BytesIO +import logging +import os +import re +import struct +import sys +import time +from zipfile import ZipInfo + +from .compat import sysconfig, detect_encoding, ZipFile +from .resources import finder +from .util import (FileOperator, get_export_entry, convert_path, + get_executable, get_platform, in_venv) + +logger = logging.getLogger(__name__) + +_DEFAULT_MANIFEST = ''' +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <assemblyIdentity version="1.0.0.0" + processorArchitecture="X86" + name="%s" + type="win32"/> + + <!-- Identify the application security requirements. --> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> + <security> + <requestedPrivileges> + <requestedExecutionLevel level="asInvoker" uiAccess="false"/> + </requestedPrivileges> + </security> + </trustInfo> +</assembly>'''.strip() + +# check if Python is called on the first line with this expression +FIRST_LINE_RE = re.compile(b'^#!.*pythonw?[0-9.]*([ \t].*)?$') +SCRIPT_TEMPLATE = r'''# -*- coding: utf-8 -*- +import re +import sys +from %(module)s import %(import_name)s +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(%(func)s()) +''' + + +def enquote_executable(executable): + if ' ' in executable: + # make sure we quote only the executable in case of env + # for example /usr/bin/env "/dir with spaces/bin/jython" + # instead of "/usr/bin/env /dir with spaces/bin/jython" + # otherwise whole + if executable.startswith('/usr/bin/env '): + env, _executable = executable.split(' ', 1) + if ' ' in _executable and not _executable.startswith('"'): + executable = '%s "%s"' % (env, _executable) + else: + if not executable.startswith('"'): + executable = '"%s"' % executable + return executable + +# Keep the old name around (for now), as there is at least one project using it! +_enquote_executable = enquote_executable + +class ScriptMaker(object): + """ + A class to copy or create scripts from source scripts or callable + specifications. + """ + script_template = SCRIPT_TEMPLATE + + executable = None # for shebangs + + def __init__(self, source_dir, target_dir, add_launchers=True, + dry_run=False, fileop=None): + self.source_dir = source_dir + self.target_dir = target_dir + self.add_launchers = add_launchers + self.force = False + self.clobber = False + # It only makes sense to set mode bits on POSIX. + self.set_mode = (os.name == 'posix') or (os.name == 'java' and + os._name == 'posix') + self.variants = set(('', 'X.Y')) + self._fileop = fileop or FileOperator(dry_run) + + self._is_nt = os.name == 'nt' or ( + os.name == 'java' and os._name == 'nt') + self.version_info = sys.version_info + + def _get_alternate_executable(self, executable, options): + if options.get('gui', False) and self._is_nt: # pragma: no cover + dn, fn = os.path.split(executable) + fn = fn.replace('python', 'pythonw') + executable = os.path.join(dn, fn) + return executable + + if sys.platform.startswith('java'): # pragma: no cover + def _is_shell(self, executable): + """ + Determine if the specified executable is a script + (contains a #! line) + """ + try: + with open(executable) as fp: + return fp.read(2) == '#!' + except (OSError, IOError): + logger.warning('Failed to open %s', executable) + return False + + def _fix_jython_executable(self, executable): + if self._is_shell(executable): + # Workaround for Jython is not needed on Linux systems. + import java + + if java.lang.System.getProperty('os.name') == 'Linux': + return executable + elif executable.lower().endswith('jython.exe'): + # Use wrapper exe for Jython on Windows + return executable + return '/usr/bin/env %s' % executable + + def _build_shebang(self, executable, post_interp): + """ + Build a shebang line. In the simple case (on Windows, or a shebang line + which is not too long or contains spaces) use a simple formulation for + the shebang. Otherwise, use /bin/sh as the executable, with a contrived + shebang which allows the script to run either under Python or sh, using + suitable quoting. Thanks to Harald Nordgren for his input. + + See also: http://www.in-ulm.de/~mascheck/various/shebang/#length + https://hg.mozilla.org/mozilla-central/file/tip/mach + """ + if os.name != 'posix': + simple_shebang = True + else: + # Add 3 for '#!' prefix and newline suffix. + shebang_length = len(executable) + len(post_interp) + 3 + if sys.platform == 'darwin': + max_shebang_length = 512 + else: + max_shebang_length = 127 + simple_shebang = ((b' ' not in executable) and + (shebang_length <= max_shebang_length)) + + if simple_shebang: + result = b'#!' + executable + post_interp + b'\n' + else: + result = b'#!/bin/sh\n' + result += b"'''exec' " + executable + post_interp + b' "$0" "$@"\n' + result += b"' '''" + return result + + def _get_shebang(self, encoding, post_interp=b'', options=None): + enquote = True + if self.executable: + executable = self.executable + enquote = False # assume this will be taken care of + elif not sysconfig.is_python_build(): + executable = get_executable() + elif in_venv(): # pragma: no cover + executable = os.path.join(sysconfig.get_path('scripts'), + 'python%s' % sysconfig.get_config_var('EXE')) + else: # pragma: no cover + executable = os.path.join( + sysconfig.get_config_var('BINDIR'), + 'python%s%s' % (sysconfig.get_config_var('VERSION'), + sysconfig.get_config_var('EXE'))) + if not os.path.isfile(executable): + # for Python builds from source on Windows, no Python executables with + # a version suffix are created, so we use python.exe + executable = os.path.join(sysconfig.get_config_var('BINDIR'), + 'python%s' % (sysconfig.get_config_var('EXE'))) + if options: + executable = self._get_alternate_executable(executable, options) + + if sys.platform.startswith('java'): # pragma: no cover + executable = self._fix_jython_executable(executable) + + # Normalise case for Windows - COMMENTED OUT + # executable = os.path.normcase(executable) + # N.B. The normalising operation above has been commented out: See + # issue #124. Although paths in Windows are generally case-insensitive, + # they aren't always. For example, a path containing a ẞ (which is a + # LATIN CAPITAL LETTER SHARP S - U+1E9E) is normcased to ß (which is a + # LATIN SMALL LETTER SHARP S' - U+00DF). The two are not considered by + # Windows as equivalent in path names. + + # If the user didn't specify an executable, it may be necessary to + # cater for executable paths with spaces (not uncommon on Windows) + if enquote: + executable = enquote_executable(executable) + # Issue #51: don't use fsencode, since we later try to + # check that the shebang is decodable using utf-8. + executable = executable.encode('utf-8') + # in case of IronPython, play safe and enable frames support + if (sys.platform == 'cli' and '-X:Frames' not in post_interp + and '-X:FullFrames' not in post_interp): # pragma: no cover + post_interp += b' -X:Frames' + shebang = self._build_shebang(executable, post_interp) + # Python parser starts to read a script using UTF-8 until + # it gets a #coding:xxx cookie. The shebang has to be the + # first line of a file, the #coding:xxx cookie cannot be + # written before. So the shebang has to be decodable from + # UTF-8. + try: + shebang.decode('utf-8') + except UnicodeDecodeError: # pragma: no cover + raise ValueError( + 'The shebang (%r) is not decodable from utf-8' % shebang) + # If the script is encoded to a custom encoding (use a + # #coding:xxx cookie), the shebang has to be decodable from + # the script encoding too. + if encoding != 'utf-8': + try: + shebang.decode(encoding) + except UnicodeDecodeError: # pragma: no cover + raise ValueError( + 'The shebang (%r) is not decodable ' + 'from the script encoding (%r)' % (shebang, encoding)) + return shebang + + def _get_script_text(self, entry): + return self.script_template % dict(module=entry.prefix, + import_name=entry.suffix.split('.')[0], + func=entry.suffix) + + manifest = _DEFAULT_MANIFEST + + def get_manifest(self, exename): + base = os.path.basename(exename) + return self.manifest % base + + def _write_script(self, names, shebang, script_bytes, filenames, ext): + use_launcher = self.add_launchers and self._is_nt + linesep = os.linesep.encode('utf-8') + if not shebang.endswith(linesep): + shebang += linesep + if not use_launcher: + script_bytes = shebang + script_bytes + else: # pragma: no cover + if ext == 'py': + launcher = self._get_launcher('t') + else: + launcher = self._get_launcher('w') + stream = BytesIO() + with ZipFile(stream, 'w') as zf: + source_date_epoch = os.environ.get('SOURCE_DATE_EPOCH') + if source_date_epoch: + date_time = time.gmtime(int(source_date_epoch))[:6] + zinfo = ZipInfo(filename='__main__.py', date_time=date_time) + zf.writestr(zinfo, script_bytes) + else: + zf.writestr('__main__.py', script_bytes) + zip_data = stream.getvalue() + script_bytes = launcher + shebang + zip_data + for name in names: + outname = os.path.join(self.target_dir, name) + if use_launcher: # pragma: no cover + n, e = os.path.splitext(outname) + if e.startswith('.py'): + outname = n + outname = '%s.exe' % outname + try: + self._fileop.write_binary_file(outname, script_bytes) + except Exception: + # Failed writing an executable - it might be in use. + logger.warning('Failed to write executable - trying to ' + 'use .deleteme logic') + dfname = '%s.deleteme' % outname + if os.path.exists(dfname): + os.remove(dfname) # Not allowed to fail here + os.rename(outname, dfname) # nor here + self._fileop.write_binary_file(outname, script_bytes) + logger.debug('Able to replace executable using ' + '.deleteme logic') + try: + os.remove(dfname) + except Exception: + pass # still in use - ignore error + else: + if self._is_nt and not outname.endswith('.' + ext): # pragma: no cover + outname = '%s.%s' % (outname, ext) + if os.path.exists(outname) and not self.clobber: + logger.warning('Skipping existing file %s', outname) + continue + self._fileop.write_binary_file(outname, script_bytes) + if self.set_mode: + self._fileop.set_executable_mode([outname]) + filenames.append(outname) + + variant_separator = '-' + + def get_script_filenames(self, name): + result = set() + if '' in self.variants: + result.add(name) + if 'X' in self.variants: + result.add('%s%s' % (name, self.version_info[0])) + if 'X.Y' in self.variants: + result.add('%s%s%s.%s' % (name, self.variant_separator, + self.version_info[0], self.version_info[1])) + return result + + def _make_script(self, entry, filenames, options=None): + post_interp = b'' + if options: + args = options.get('interpreter_args', []) + if args: + args = ' %s' % ' '.join(args) + post_interp = args.encode('utf-8') + shebang = self._get_shebang('utf-8', post_interp, options=options) + script = self._get_script_text(entry).encode('utf-8') + scriptnames = self.get_script_filenames(entry.name) + if options and options.get('gui', False): + ext = 'pyw' + else: + ext = 'py' + self._write_script(scriptnames, shebang, script, filenames, ext) + + def _copy_script(self, script, filenames): + adjust = False + script = os.path.join(self.source_dir, convert_path(script)) + outname = os.path.join(self.target_dir, os.path.basename(script)) + if not self.force and not self._fileop.newer(script, outname): + logger.debug('not copying %s (up-to-date)', script) + return + + # Always open the file, but ignore failures in dry-run mode -- + # that way, we'll get accurate feedback if we can read the + # script. + try: + f = open(script, 'rb') + except IOError: # pragma: no cover + if not self.dry_run: + raise + f = None + else: + first_line = f.readline() + if not first_line: # pragma: no cover + logger.warning('%s is an empty file (skipping)', script) + return + + match = FIRST_LINE_RE.match(first_line.replace(b'\r\n', b'\n')) + if match: + adjust = True + post_interp = match.group(1) or b'' + + if not adjust: + if f: + f.close() + self._fileop.copy_file(script, outname) + if self.set_mode: + self._fileop.set_executable_mode([outname]) + filenames.append(outname) + else: + logger.info('copying and adjusting %s -> %s', script, + self.target_dir) + if not self._fileop.dry_run: + encoding, lines = detect_encoding(f.readline) + f.seek(0) + shebang = self._get_shebang(encoding, post_interp) + if b'pythonw' in first_line: # pragma: no cover + ext = 'pyw' + else: + ext = 'py' + n = os.path.basename(outname) + self._write_script([n], shebang, f.read(), filenames, ext) + if f: + f.close() + + @property + def dry_run(self): + return self._fileop.dry_run + + @dry_run.setter + def dry_run(self, value): + self._fileop.dry_run = value + + if os.name == 'nt' or (os.name == 'java' and os._name == 'nt'): # pragma: no cover + # Executable launcher support. + # Launchers are from https://bitbucket.org/vinay.sajip/simple_launcher/ + + def _get_launcher(self, kind): + if struct.calcsize('P') == 8: # 64-bit + bits = '64' + else: + bits = '32' + platform_suffix = '-arm' if get_platform() == 'win-arm64' else '' + name = '%s%s%s.exe' % (kind, bits, platform_suffix) + # Issue 31: don't hardcode an absolute package name, but + # determine it relative to the current package + distlib_package = __name__.rsplit('.', 1)[0] + resource = finder(distlib_package).find(name) + if not resource: + msg = ('Unable to find resource %s in package %s' % (name, + distlib_package)) + raise ValueError(msg) + return resource.bytes + + # Public API follows + + def make(self, specification, options=None): + """ + Make a script. + + :param specification: The specification, which is either a valid export + entry specification (to make a script from a + callable) or a filename (to make a script by + copying from a source location). + :param options: A dictionary of options controlling script generation. + :return: A list of all absolute pathnames written to. + """ + filenames = [] + entry = get_export_entry(specification) + if entry is None: + self._copy_script(specification, filenames) + else: + self._make_script(entry, filenames, options=options) + return filenames + + def make_multiple(self, specifications, options=None): + """ + Take a list of specifications and make scripts from them, + :param specifications: A list of specifications. + :return: A list of all absolute pathnames written to, + """ + filenames = [] + for specification in specifications: + filenames.extend(self.make(specification, options)) + return filenames diff --git a/virt/lib/python3.9/site-packages/pip/_vendor/distlib/wheel 3.py b/virt/lib/python3.9/site-packages/pip/_vendor/distlib/wheel 3.py new file mode 100644 index 00000000..0c42e439 --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_vendor/distlib/wheel 3.py @@ -0,0 +1,1082 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2020 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from __future__ import unicode_literals + +import base64 +import codecs +import datetime +from email import message_from_file +import hashlib +import json +import logging +import os +import posixpath +import re +import shutil +import sys +import tempfile +import zipfile + +from . import __version__, DistlibException +from .compat import sysconfig, ZipFile, fsdecode, text_type, filter +from .database import InstalledDistribution +from .metadata import (Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME, + LEGACY_METADATA_FILENAME) +from .util import (FileOperator, convert_path, CSVReader, CSVWriter, Cache, + cached_property, get_cache_base, read_exports, tempdir, + get_platform) +from .version import NormalizedVersion, UnsupportedVersionError + +logger = logging.getLogger(__name__) + +cache = None # created when needed + +if hasattr(sys, 'pypy_version_info'): # pragma: no cover + IMP_PREFIX = 'pp' +elif sys.platform.startswith('java'): # pragma: no cover + IMP_PREFIX = 'jy' +elif sys.platform == 'cli': # pragma: no cover + IMP_PREFIX = 'ip' +else: + IMP_PREFIX = 'cp' + +VER_SUFFIX = sysconfig.get_config_var('py_version_nodot') +if not VER_SUFFIX: # pragma: no cover + VER_SUFFIX = '%s%s' % sys.version_info[:2] +PYVER = 'py' + VER_SUFFIX +IMPVER = IMP_PREFIX + VER_SUFFIX + +ARCH = get_platform().replace('-', '_').replace('.', '_') + +ABI = sysconfig.get_config_var('SOABI') +if ABI and ABI.startswith('cpython-'): + ABI = ABI.replace('cpython-', 'cp').split('-')[0] +else: + def _derive_abi(): + parts = ['cp', VER_SUFFIX] + if sysconfig.get_config_var('Py_DEBUG'): + parts.append('d') + if IMP_PREFIX == 'cp': + vi = sys.version_info[:2] + if vi < (3, 8): + wpm = sysconfig.get_config_var('WITH_PYMALLOC') + if wpm is None: + wpm = True + if wpm: + parts.append('m') + if vi < (3, 3): + us = sysconfig.get_config_var('Py_UNICODE_SIZE') + if us == 4 or (us is None and sys.maxunicode == 0x10FFFF): + parts.append('u') + return ''.join(parts) + ABI = _derive_abi() + del _derive_abi + +FILENAME_RE = re.compile(r''' +(?P<nm>[^-]+) +-(?P<vn>\d+[^-]*) +(-(?P<bn>\d+[^-]*))? +-(?P<py>\w+\d+(\.\w+\d+)*) +-(?P<bi>\w+) +-(?P<ar>\w+(\.\w+)*) +\.whl$ +''', re.IGNORECASE | re.VERBOSE) + +NAME_VERSION_RE = re.compile(r''' +(?P<nm>[^-]+) +-(?P<vn>\d+[^-]*) +(-(?P<bn>\d+[^-]*))?$ +''', re.IGNORECASE | re.VERBOSE) + +SHEBANG_RE = re.compile(br'\s*#![^\r\n]*') +SHEBANG_DETAIL_RE = re.compile(br'^(\s*#!("[^"]+"|\S+))\s+(.*)$') +SHEBANG_PYTHON = b'#!python' +SHEBANG_PYTHONW = b'#!pythonw' + +if os.sep == '/': + to_posix = lambda o: o +else: + to_posix = lambda o: o.replace(os.sep, '/') + +if sys.version_info[0] < 3: + import imp +else: + imp = None + import importlib.machinery + import importlib.util + +def _get_suffixes(): + if imp: + return [s[0] for s in imp.get_suffixes()] + else: + return importlib.machinery.EXTENSION_SUFFIXES + +def _load_dynamic(name, path): + # https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly + if imp: + return imp.load_dynamic(name, path) + else: + spec = importlib.util.spec_from_file_location(name, path) + module = importlib.util.module_from_spec(spec) + sys.modules[name] = module + spec.loader.exec_module(module) + return module + +class Mounter(object): + def __init__(self): + self.impure_wheels = {} + self.libs = {} + + def add(self, pathname, extensions): + self.impure_wheels[pathname] = extensions + self.libs.update(extensions) + + def remove(self, pathname): + extensions = self.impure_wheels.pop(pathname) + for k, v in extensions: + if k in self.libs: + del self.libs[k] + + def find_module(self, fullname, path=None): + if fullname in self.libs: + result = self + else: + result = None + return result + + def load_module(self, fullname): + if fullname in sys.modules: + result = sys.modules[fullname] + else: + if fullname not in self.libs: + raise ImportError('unable to find extension for %s' % fullname) + result = _load_dynamic(fullname, self.libs[fullname]) + result.__loader__ = self + parts = fullname.rsplit('.', 1) + if len(parts) > 1: + result.__package__ = parts[0] + return result + +_hook = Mounter() + + +class Wheel(object): + """ + Class to build and install from Wheel files (PEP 427). + """ + + wheel_version = (1, 1) + hash_kind = 'sha256' + + def __init__(self, filename=None, sign=False, verify=False): + """ + Initialise an instance using a (valid) filename. + """ + self.sign = sign + self.should_verify = verify + self.buildver = '' + self.pyver = [PYVER] + self.abi = ['none'] + self.arch = ['any'] + self.dirname = os.getcwd() + if filename is None: + self.name = 'dummy' + self.version = '0.1' + self._filename = self.filename + else: + m = NAME_VERSION_RE.match(filename) + if m: + info = m.groupdict('') + self.name = info['nm'] + # Reinstate the local version separator + self.version = info['vn'].replace('_', '-') + self.buildver = info['bn'] + self._filename = self.filename + else: + dirname, filename = os.path.split(filename) + m = FILENAME_RE.match(filename) + if not m: + raise DistlibException('Invalid name or ' + 'filename: %r' % filename) + if dirname: + self.dirname = os.path.abspath(dirname) + self._filename = filename + info = m.groupdict('') + self.name = info['nm'] + self.version = info['vn'] + self.buildver = info['bn'] + self.pyver = info['py'].split('.') + self.abi = info['bi'].split('.') + self.arch = info['ar'].split('.') + + @property + def filename(self): + """ + Build and return a filename from the various components. + """ + if self.buildver: + buildver = '-' + self.buildver + else: + buildver = '' + pyver = '.'.join(self.pyver) + abi = '.'.join(self.abi) + arch = '.'.join(self.arch) + # replace - with _ as a local version separator + version = self.version.replace('-', '_') + return '%s-%s%s-%s-%s-%s.whl' % (self.name, version, buildver, + pyver, abi, arch) + + @property + def exists(self): + path = os.path.join(self.dirname, self.filename) + return os.path.isfile(path) + + @property + def tags(self): + for pyver in self.pyver: + for abi in self.abi: + for arch in self.arch: + yield pyver, abi, arch + + @cached_property + def metadata(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + wrapper = codecs.getreader('utf-8') + with ZipFile(pathname, 'r') as zf: + wheel_metadata = self.get_wheel_metadata(zf) + wv = wheel_metadata['Wheel-Version'].split('.', 1) + file_version = tuple([int(i) for i in wv]) + # if file_version < (1, 1): + # fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME, + # LEGACY_METADATA_FILENAME] + # else: + # fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME] + fns = [WHEEL_METADATA_FILENAME, LEGACY_METADATA_FILENAME] + result = None + for fn in fns: + try: + metadata_filename = posixpath.join(info_dir, fn) + with zf.open(metadata_filename) as bf: + wf = wrapper(bf) + result = Metadata(fileobj=wf) + if result: + break + except KeyError: + pass + if not result: + raise ValueError('Invalid wheel, because metadata is ' + 'missing: looked in %s' % ', '.join(fns)) + return result + + def get_wheel_metadata(self, zf): + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + metadata_filename = posixpath.join(info_dir, 'WHEEL') + with zf.open(metadata_filename) as bf: + wf = codecs.getreader('utf-8')(bf) + message = message_from_file(wf) + return dict(message) + + @cached_property + def info(self): + pathname = os.path.join(self.dirname, self.filename) + with ZipFile(pathname, 'r') as zf: + result = self.get_wheel_metadata(zf) + return result + + def process_shebang(self, data): + m = SHEBANG_RE.match(data) + if m: + end = m.end() + shebang, data_after_shebang = data[:end], data[end:] + # Preserve any arguments after the interpreter + if b'pythonw' in shebang.lower(): + shebang_python = SHEBANG_PYTHONW + else: + shebang_python = SHEBANG_PYTHON + m = SHEBANG_DETAIL_RE.match(shebang) + if m: + args = b' ' + m.groups()[-1] + else: + args = b'' + shebang = shebang_python + args + data = shebang + data_after_shebang + else: + cr = data.find(b'\r') + lf = data.find(b'\n') + if cr < 0 or cr > lf: + term = b'\n' + else: + if data[cr:cr + 2] == b'\r\n': + term = b'\r\n' + else: + term = b'\r' + data = SHEBANG_PYTHON + term + data + return data + + def get_hash(self, data, hash_kind=None): + if hash_kind is None: + hash_kind = self.hash_kind + try: + hasher = getattr(hashlib, hash_kind) + except AttributeError: + raise DistlibException('Unsupported hash algorithm: %r' % hash_kind) + result = hasher(data).digest() + result = base64.urlsafe_b64encode(result).rstrip(b'=').decode('ascii') + return hash_kind, result + + def write_record(self, records, record_path, archive_record_path): + records = list(records) # make a copy, as mutated + records.append((archive_record_path, '', '')) + with CSVWriter(record_path) as writer: + for row in records: + writer.writerow(row) + + def write_records(self, info, libdir, archive_paths): + records = [] + distinfo, info_dir = info + hasher = getattr(hashlib, self.hash_kind) + for ap, p in archive_paths: + with open(p, 'rb') as f: + data = f.read() + digest = '%s=%s' % self.get_hash(data) + size = os.path.getsize(p) + records.append((ap, digest, size)) + + p = os.path.join(distinfo, 'RECORD') + ap = to_posix(os.path.join(info_dir, 'RECORD')) + self.write_record(records, p, ap) + archive_paths.append((ap, p)) + + def build_zip(self, pathname, archive_paths): + with ZipFile(pathname, 'w', zipfile.ZIP_DEFLATED) as zf: + for ap, p in archive_paths: + logger.debug('Wrote %s to %s in wheel', p, ap) + zf.write(p, ap) + + def build(self, paths, tags=None, wheel_version=None): + """ + Build a wheel from files in specified paths, and use any specified tags + when determining the name of the wheel. + """ + if tags is None: + tags = {} + + libkey = list(filter(lambda o: o in paths, ('purelib', 'platlib')))[0] + if libkey == 'platlib': + is_pure = 'false' + default_pyver = [IMPVER] + default_abi = [ABI] + default_arch = [ARCH] + else: + is_pure = 'true' + default_pyver = [PYVER] + default_abi = ['none'] + default_arch = ['any'] + + self.pyver = tags.get('pyver', default_pyver) + self.abi = tags.get('abi', default_abi) + self.arch = tags.get('arch', default_arch) + + libdir = paths[libkey] + + name_ver = '%s-%s' % (self.name, self.version) + data_dir = '%s.data' % name_ver + info_dir = '%s.dist-info' % name_ver + + archive_paths = [] + + # First, stuff which is not in site-packages + for key in ('data', 'headers', 'scripts'): + if key not in paths: + continue + path = paths[key] + if os.path.isdir(path): + for root, dirs, files in os.walk(path): + for fn in files: + p = fsdecode(os.path.join(root, fn)) + rp = os.path.relpath(p, path) + ap = to_posix(os.path.join(data_dir, key, rp)) + archive_paths.append((ap, p)) + if key == 'scripts' and not p.endswith('.exe'): + with open(p, 'rb') as f: + data = f.read() + data = self.process_shebang(data) + with open(p, 'wb') as f: + f.write(data) + + # Now, stuff which is in site-packages, other than the + # distinfo stuff. + path = libdir + distinfo = None + for root, dirs, files in os.walk(path): + if root == path: + # At the top level only, save distinfo for later + # and skip it for now + for i, dn in enumerate(dirs): + dn = fsdecode(dn) + if dn.endswith('.dist-info'): + distinfo = os.path.join(root, dn) + del dirs[i] + break + assert distinfo, '.dist-info directory expected, not found' + + for fn in files: + # comment out next suite to leave .pyc files in + if fsdecode(fn).endswith(('.pyc', '.pyo')): + continue + p = os.path.join(root, fn) + rp = to_posix(os.path.relpath(p, path)) + archive_paths.append((rp, p)) + + # Now distinfo. Assumed to be flat, i.e. os.listdir is enough. + files = os.listdir(distinfo) + for fn in files: + if fn not in ('RECORD', 'INSTALLER', 'SHARED', 'WHEEL'): + p = fsdecode(os.path.join(distinfo, fn)) + ap = to_posix(os.path.join(info_dir, fn)) + archive_paths.append((ap, p)) + + wheel_metadata = [ + 'Wheel-Version: %d.%d' % (wheel_version or self.wheel_version), + 'Generator: distlib %s' % __version__, + 'Root-Is-Purelib: %s' % is_pure, + ] + for pyver, abi, arch in self.tags: + wheel_metadata.append('Tag: %s-%s-%s' % (pyver, abi, arch)) + p = os.path.join(distinfo, 'WHEEL') + with open(p, 'w') as f: + f.write('\n'.join(wheel_metadata)) + ap = to_posix(os.path.join(info_dir, 'WHEEL')) + archive_paths.append((ap, p)) + + # sort the entries by archive path. Not needed by any spec, but it + # keeps the archive listing and RECORD tidier than they would otherwise + # be. Use the number of path segments to keep directory entries together, + # and keep the dist-info stuff at the end. + def sorter(t): + ap = t[0] + n = ap.count('/') + if '.dist-info' in ap: + n += 10000 + return (n, ap) + archive_paths = sorted(archive_paths, key=sorter) + + # Now, at last, RECORD. + # Paths in here are archive paths - nothing else makes sense. + self.write_records((distinfo, info_dir), libdir, archive_paths) + # Now, ready to build the zip file + pathname = os.path.join(self.dirname, self.filename) + self.build_zip(pathname, archive_paths) + return pathname + + def skip_entry(self, arcname): + """ + Determine whether an archive entry should be skipped when verifying + or installing. + """ + # The signature file won't be in RECORD, + # and we don't currently don't do anything with it + # We also skip directories, as they won't be in RECORD + # either. See: + # + # https://github.com/pypa/wheel/issues/294 + # https://github.com/pypa/wheel/issues/287 + # https://github.com/pypa/wheel/pull/289 + # + return arcname.endswith(('/', '/RECORD.jws')) + + def install(self, paths, maker, **kwargs): + """ + Install a wheel to the specified paths. If kwarg ``warner`` is + specified, it should be a callable, which will be called with two + tuples indicating the wheel version of this software and the wheel + version in the file, if there is a discrepancy in the versions. + This can be used to issue any warnings to raise any exceptions. + If kwarg ``lib_only`` is True, only the purelib/platlib files are + installed, and the headers, scripts, data and dist-info metadata are + not written. If kwarg ``bytecode_hashed_invalidation`` is True, written + bytecode will try to use file-hash based invalidation (PEP-552) on + supported interpreter versions (CPython 2.7+). + + The return value is a :class:`InstalledDistribution` instance unless + ``options.lib_only`` is True, in which case the return value is ``None``. + """ + + dry_run = maker.dry_run + warner = kwargs.get('warner') + lib_only = kwargs.get('lib_only', False) + bc_hashed_invalidation = kwargs.get('bytecode_hashed_invalidation', False) + + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + data_dir = '%s.data' % name_ver + info_dir = '%s.dist-info' % name_ver + + metadata_name = posixpath.join(info_dir, LEGACY_METADATA_FILENAME) + wheel_metadata_name = posixpath.join(info_dir, 'WHEEL') + record_name = posixpath.join(info_dir, 'RECORD') + + wrapper = codecs.getreader('utf-8') + + with ZipFile(pathname, 'r') as zf: + with zf.open(wheel_metadata_name) as bwf: + wf = wrapper(bwf) + message = message_from_file(wf) + wv = message['Wheel-Version'].split('.', 1) + file_version = tuple([int(i) for i in wv]) + if (file_version != self.wheel_version) and warner: + warner(self.wheel_version, file_version) + + if message['Root-Is-Purelib'] == 'true': + libdir = paths['purelib'] + else: + libdir = paths['platlib'] + + records = {} + with zf.open(record_name) as bf: + with CSVReader(stream=bf) as reader: + for row in reader: + p = row[0] + records[p] = row + + data_pfx = posixpath.join(data_dir, '') + info_pfx = posixpath.join(info_dir, '') + script_pfx = posixpath.join(data_dir, 'scripts', '') + + # make a new instance rather than a copy of maker's, + # as we mutate it + fileop = FileOperator(dry_run=dry_run) + fileop.record = True # so we can rollback if needed + + bc = not sys.dont_write_bytecode # Double negatives. Lovely! + + outfiles = [] # for RECORD writing + + # for script copying/shebang processing + workdir = tempfile.mkdtemp() + # set target dir later + # we default add_launchers to False, as the + # Python Launcher should be used instead + maker.source_dir = workdir + maker.target_dir = None + try: + for zinfo in zf.infolist(): + arcname = zinfo.filename + if isinstance(arcname, text_type): + u_arcname = arcname + else: + u_arcname = arcname.decode('utf-8') + if self.skip_entry(u_arcname): + continue + row = records[u_arcname] + if row[2] and str(zinfo.file_size) != row[2]: + raise DistlibException('size mismatch for ' + '%s' % u_arcname) + if row[1]: + kind, value = row[1].split('=', 1) + with zf.open(arcname) as bf: + data = bf.read() + _, digest = self.get_hash(data, kind) + if digest != value: + raise DistlibException('digest mismatch for ' + '%s' % arcname) + + if lib_only and u_arcname.startswith((info_pfx, data_pfx)): + logger.debug('lib_only: skipping %s', u_arcname) + continue + is_script = (u_arcname.startswith(script_pfx) + and not u_arcname.endswith('.exe')) + + if u_arcname.startswith(data_pfx): + _, where, rp = u_arcname.split('/', 2) + outfile = os.path.join(paths[where], convert_path(rp)) + else: + # meant for site-packages. + if u_arcname in (wheel_metadata_name, record_name): + continue + outfile = os.path.join(libdir, convert_path(u_arcname)) + if not is_script: + with zf.open(arcname) as bf: + fileop.copy_stream(bf, outfile) + # Issue #147: permission bits aren't preserved. Using + # zf.extract(zinfo, libdir) should have worked, but didn't, + # see https://www.thetopsites.net/article/53834422.shtml + # So ... manually preserve permission bits as given in zinfo + if os.name == 'posix': + # just set the normal permission bits + os.chmod(outfile, (zinfo.external_attr >> 16) & 0x1FF) + outfiles.append(outfile) + # Double check the digest of the written file + if not dry_run and row[1]: + with open(outfile, 'rb') as bf: + data = bf.read() + _, newdigest = self.get_hash(data, kind) + if newdigest != digest: + raise DistlibException('digest mismatch ' + 'on write for ' + '%s' % outfile) + if bc and outfile.endswith('.py'): + try: + pyc = fileop.byte_compile(outfile, + hashed_invalidation=bc_hashed_invalidation) + outfiles.append(pyc) + except Exception: + # Don't give up if byte-compilation fails, + # but log it and perhaps warn the user + logger.warning('Byte-compilation failed', + exc_info=True) + else: + fn = os.path.basename(convert_path(arcname)) + workname = os.path.join(workdir, fn) + with zf.open(arcname) as bf: + fileop.copy_stream(bf, workname) + + dn, fn = os.path.split(outfile) + maker.target_dir = dn + filenames = maker.make(fn) + fileop.set_executable_mode(filenames) + outfiles.extend(filenames) + + if lib_only: + logger.debug('lib_only: returning None') + dist = None + else: + # Generate scripts + + # Try to get pydist.json so we can see if there are + # any commands to generate. If this fails (e.g. because + # of a legacy wheel), log a warning but don't give up. + commands = None + file_version = self.info['Wheel-Version'] + if file_version == '1.0': + # Use legacy info + ep = posixpath.join(info_dir, 'entry_points.txt') + try: + with zf.open(ep) as bwf: + epdata = read_exports(bwf) + commands = {} + for key in ('console', 'gui'): + k = '%s_scripts' % key + if k in epdata: + commands['wrap_%s' % key] = d = {} + for v in epdata[k].values(): + s = '%s:%s' % (v.prefix, v.suffix) + if v.flags: + s += ' [%s]' % ','.join(v.flags) + d[v.name] = s + except Exception: + logger.warning('Unable to read legacy script ' + 'metadata, so cannot generate ' + 'scripts') + else: + try: + with zf.open(metadata_name) as bwf: + wf = wrapper(bwf) + commands = json.load(wf).get('extensions') + if commands: + commands = commands.get('python.commands') + except Exception: + logger.warning('Unable to read JSON metadata, so ' + 'cannot generate scripts') + if commands: + console_scripts = commands.get('wrap_console', {}) + gui_scripts = commands.get('wrap_gui', {}) + if console_scripts or gui_scripts: + script_dir = paths.get('scripts', '') + if not os.path.isdir(script_dir): + raise ValueError('Valid script path not ' + 'specified') + maker.target_dir = script_dir + for k, v in console_scripts.items(): + script = '%s = %s' % (k, v) + filenames = maker.make(script) + fileop.set_executable_mode(filenames) + + if gui_scripts: + options = {'gui': True } + for k, v in gui_scripts.items(): + script = '%s = %s' % (k, v) + filenames = maker.make(script, options) + fileop.set_executable_mode(filenames) + + p = os.path.join(libdir, info_dir) + dist = InstalledDistribution(p) + + # Write SHARED + paths = dict(paths) # don't change passed in dict + del paths['purelib'] + del paths['platlib'] + paths['lib'] = libdir + p = dist.write_shared_locations(paths, dry_run) + if p: + outfiles.append(p) + + # Write RECORD + dist.write_installed_files(outfiles, paths['prefix'], + dry_run) + return dist + except Exception: # pragma: no cover + logger.exception('installation failed.') + fileop.rollback() + raise + finally: + shutil.rmtree(workdir) + + def _get_dylib_cache(self): + global cache + if cache is None: + # Use native string to avoid issues on 2.x: see Python #20140. + base = os.path.join(get_cache_base(), str('dylib-cache'), + '%s.%s' % sys.version_info[:2]) + cache = Cache(base) + return cache + + def _get_extensions(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + arcname = posixpath.join(info_dir, 'EXTENSIONS') + wrapper = codecs.getreader('utf-8') + result = [] + with ZipFile(pathname, 'r') as zf: + try: + with zf.open(arcname) as bf: + wf = wrapper(bf) + extensions = json.load(wf) + cache = self._get_dylib_cache() + prefix = cache.prefix_to_dir(pathname) + cache_base = os.path.join(cache.base, prefix) + if not os.path.isdir(cache_base): + os.makedirs(cache_base) + for name, relpath in extensions.items(): + dest = os.path.join(cache_base, convert_path(relpath)) + if not os.path.exists(dest): + extract = True + else: + file_time = os.stat(dest).st_mtime + file_time = datetime.datetime.fromtimestamp(file_time) + info = zf.getinfo(relpath) + wheel_time = datetime.datetime(*info.date_time) + extract = wheel_time > file_time + if extract: + zf.extract(relpath, cache_base) + result.append((name, dest)) + except KeyError: + pass + return result + + def is_compatible(self): + """ + Determine if a wheel is compatible with the running system. + """ + return is_compatible(self) + + def is_mountable(self): + """ + Determine if a wheel is asserted as mountable by its metadata. + """ + return True # for now - metadata details TBD + + def mount(self, append=False): + pathname = os.path.abspath(os.path.join(self.dirname, self.filename)) + if not self.is_compatible(): + msg = 'Wheel %s not compatible with this Python.' % pathname + raise DistlibException(msg) + if not self.is_mountable(): + msg = 'Wheel %s is marked as not mountable.' % pathname + raise DistlibException(msg) + if pathname in sys.path: + logger.debug('%s already in path', pathname) + else: + if append: + sys.path.append(pathname) + else: + sys.path.insert(0, pathname) + extensions = self._get_extensions() + if extensions: + if _hook not in sys.meta_path: + sys.meta_path.append(_hook) + _hook.add(pathname, extensions) + + def unmount(self): + pathname = os.path.abspath(os.path.join(self.dirname, self.filename)) + if pathname not in sys.path: + logger.debug('%s not in path', pathname) + else: + sys.path.remove(pathname) + if pathname in _hook.impure_wheels: + _hook.remove(pathname) + if not _hook.impure_wheels: + if _hook in sys.meta_path: + sys.meta_path.remove(_hook) + + def verify(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + data_dir = '%s.data' % name_ver + info_dir = '%s.dist-info' % name_ver + + metadata_name = posixpath.join(info_dir, LEGACY_METADATA_FILENAME) + wheel_metadata_name = posixpath.join(info_dir, 'WHEEL') + record_name = posixpath.join(info_dir, 'RECORD') + + wrapper = codecs.getreader('utf-8') + + with ZipFile(pathname, 'r') as zf: + with zf.open(wheel_metadata_name) as bwf: + wf = wrapper(bwf) + message = message_from_file(wf) + wv = message['Wheel-Version'].split('.', 1) + file_version = tuple([int(i) for i in wv]) + # TODO version verification + + records = {} + with zf.open(record_name) as bf: + with CSVReader(stream=bf) as reader: + for row in reader: + p = row[0] + records[p] = row + + for zinfo in zf.infolist(): + arcname = zinfo.filename + if isinstance(arcname, text_type): + u_arcname = arcname + else: + u_arcname = arcname.decode('utf-8') + # See issue #115: some wheels have .. in their entries, but + # in the filename ... e.g. __main__..py ! So the check is + # updated to look for .. in the directory portions + p = u_arcname.split('/') + if '..' in p: + raise DistlibException('invalid entry in ' + 'wheel: %r' % u_arcname) + + if self.skip_entry(u_arcname): + continue + row = records[u_arcname] + if row[2] and str(zinfo.file_size) != row[2]: + raise DistlibException('size mismatch for ' + '%s' % u_arcname) + if row[1]: + kind, value = row[1].split('=', 1) + with zf.open(arcname) as bf: + data = bf.read() + _, digest = self.get_hash(data, kind) + if digest != value: + raise DistlibException('digest mismatch for ' + '%s' % arcname) + + def update(self, modifier, dest_dir=None, **kwargs): + """ + Update the contents of a wheel in a generic way. The modifier should + be a callable which expects a dictionary argument: its keys are + archive-entry paths, and its values are absolute filesystem paths + where the contents the corresponding archive entries can be found. The + modifier is free to change the contents of the files pointed to, add + new entries and remove entries, before returning. This method will + extract the entire contents of the wheel to a temporary location, call + the modifier, and then use the passed (and possibly updated) + dictionary to write a new wheel. If ``dest_dir`` is specified, the new + wheel is written there -- otherwise, the original wheel is overwritten. + + The modifier should return True if it updated the wheel, else False. + This method returns the same value the modifier returns. + """ + + def get_version(path_map, info_dir): + version = path = None + key = '%s/%s' % (info_dir, LEGACY_METADATA_FILENAME) + if key not in path_map: + key = '%s/PKG-INFO' % info_dir + if key in path_map: + path = path_map[key] + version = Metadata(path=path).version + return version, path + + def update_version(version, path): + updated = None + try: + v = NormalizedVersion(version) + i = version.find('-') + if i < 0: + updated = '%s+1' % version + else: + parts = [int(s) for s in version[i + 1:].split('.')] + parts[-1] += 1 + updated = '%s+%s' % (version[:i], + '.'.join(str(i) for i in parts)) + except UnsupportedVersionError: + logger.debug('Cannot update non-compliant (PEP-440) ' + 'version %r', version) + if updated: + md = Metadata(path=path) + md.version = updated + legacy = path.endswith(LEGACY_METADATA_FILENAME) + md.write(path=path, legacy=legacy) + logger.debug('Version updated from %r to %r', version, + updated) + + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + record_name = posixpath.join(info_dir, 'RECORD') + with tempdir() as workdir: + with ZipFile(pathname, 'r') as zf: + path_map = {} + for zinfo in zf.infolist(): + arcname = zinfo.filename + if isinstance(arcname, text_type): + u_arcname = arcname + else: + u_arcname = arcname.decode('utf-8') + if u_arcname == record_name: + continue + if '..' in u_arcname: + raise DistlibException('invalid entry in ' + 'wheel: %r' % u_arcname) + zf.extract(zinfo, workdir) + path = os.path.join(workdir, convert_path(u_arcname)) + path_map[u_arcname] = path + + # Remember the version. + original_version, _ = get_version(path_map, info_dir) + # Files extracted. Call the modifier. + modified = modifier(path_map, **kwargs) + if modified: + # Something changed - need to build a new wheel. + current_version, path = get_version(path_map, info_dir) + if current_version and (current_version == original_version): + # Add or update local version to signify changes. + update_version(current_version, path) + # Decide where the new wheel goes. + if dest_dir is None: + fd, newpath = tempfile.mkstemp(suffix='.whl', + prefix='wheel-update-', + dir=workdir) + os.close(fd) + else: + if not os.path.isdir(dest_dir): + raise DistlibException('Not a directory: %r' % dest_dir) + newpath = os.path.join(dest_dir, self.filename) + archive_paths = list(path_map.items()) + distinfo = os.path.join(workdir, info_dir) + info = distinfo, info_dir + self.write_records(info, workdir, archive_paths) + self.build_zip(newpath, archive_paths) + if dest_dir is None: + shutil.copyfile(newpath, pathname) + return modified + +def _get_glibc_version(): + import platform + ver = platform.libc_ver() + result = [] + if ver[0] == 'glibc': + for s in ver[1].split('.'): + result.append(int(s) if s.isdigit() else 0) + result = tuple(result) + return result + +def compatible_tags(): + """ + Return (pyver, abi, arch) tuples compatible with this Python. + """ + versions = [VER_SUFFIX] + major = VER_SUFFIX[0] + for minor in range(sys.version_info[1] - 1, - 1, -1): + versions.append(''.join([major, str(minor)])) + + abis = [] + for suffix in _get_suffixes(): + if suffix.startswith('.abi'): + abis.append(suffix.split('.', 2)[1]) + abis.sort() + if ABI != 'none': + abis.insert(0, ABI) + abis.append('none') + result = [] + + arches = [ARCH] + if sys.platform == 'darwin': + m = re.match(r'(\w+)_(\d+)_(\d+)_(\w+)$', ARCH) + if m: + name, major, minor, arch = m.groups() + minor = int(minor) + matches = [arch] + if arch in ('i386', 'ppc'): + matches.append('fat') + if arch in ('i386', 'ppc', 'x86_64'): + matches.append('fat3') + if arch in ('ppc64', 'x86_64'): + matches.append('fat64') + if arch in ('i386', 'x86_64'): + matches.append('intel') + if arch in ('i386', 'x86_64', 'intel', 'ppc', 'ppc64'): + matches.append('universal') + while minor >= 0: + for match in matches: + s = '%s_%s_%s_%s' % (name, major, minor, match) + if s != ARCH: # already there + arches.append(s) + minor -= 1 + + # Most specific - our Python version, ABI and arch + for abi in abis: + for arch in arches: + result.append((''.join((IMP_PREFIX, versions[0])), abi, arch)) + # manylinux + if abi != 'none' and sys.platform.startswith('linux'): + arch = arch.replace('linux_', '') + parts = _get_glibc_version() + if len(parts) == 2: + if parts >= (2, 5): + result.append((''.join((IMP_PREFIX, versions[0])), abi, + 'manylinux1_%s' % arch)) + if parts >= (2, 12): + result.append((''.join((IMP_PREFIX, versions[0])), abi, + 'manylinux2010_%s' % arch)) + if parts >= (2, 17): + result.append((''.join((IMP_PREFIX, versions[0])), abi, + 'manylinux2014_%s' % arch)) + result.append((''.join((IMP_PREFIX, versions[0])), abi, + 'manylinux_%s_%s_%s' % (parts[0], parts[1], + arch))) + + # where no ABI / arch dependency, but IMP_PREFIX dependency + for i, version in enumerate(versions): + result.append((''.join((IMP_PREFIX, version)), 'none', 'any')) + if i == 0: + result.append((''.join((IMP_PREFIX, version[0])), 'none', 'any')) + + # no IMP_PREFIX, ABI or arch dependency + for i, version in enumerate(versions): + result.append((''.join(('py', version)), 'none', 'any')) + if i == 0: + result.append((''.join(('py', version[0])), 'none', 'any')) + + return set(result) + + +COMPATIBLE_TAGS = compatible_tags() + +del compatible_tags + + +def is_compatible(wheel, tags=None): + if not isinstance(wheel, Wheel): + wheel = Wheel(wheel) # assume it's a filename + result = False + if tags is None: + tags = COMPATIBLE_TAGS + for ver, abi, arch in tags: + if ver in wheel.pyver and abi in wheel.abi and arch in wheel.arch: + result = True + break + return result diff --git a/virt/lib/python3.9/site-packages/pip/_vendor/distro/distro 3.py b/virt/lib/python3.9/site-packages/pip/_vendor/distro/distro 3.py new file mode 100644 index 00000000..209eff7c --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_vendor/distro/distro 3.py @@ -0,0 +1,1399 @@ +#!/usr/bin/env python +# Copyright 2015,2016,2017 Nir Cohen +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +The ``distro`` package (``distro`` stands for Linux Distribution) provides +information about the Linux distribution it runs on, such as a reliable +machine-readable distro ID, or version information. + +It is the recommended replacement for Python's original +:py:func:`platform.linux_distribution` function, but it provides much more +functionality. An alternative implementation became necessary because Python +3.5 deprecated this function, and Python 3.8 removed it altogether. Its +predecessor function :py:func:`platform.dist` was already deprecated since +Python 2.6 and removed in Python 3.8. Still, there are many cases in which +access to OS distribution information is needed. See `Python issue 1322 +<https://bugs.python.org/issue1322>`_ for more information. +""" + +import argparse +import json +import logging +import os +import re +import shlex +import subprocess +import sys +import warnings +from typing import ( + Any, + Callable, + Dict, + Iterable, + Optional, + Sequence, + TextIO, + Tuple, + Type, +) + +try: + from typing import TypedDict +except ImportError: + # Python 3.7 + TypedDict = dict + +__version__ = "1.8.0" + + +class VersionDict(TypedDict): + major: str + minor: str + build_number: str + + +class InfoDict(TypedDict): + id: str + version: str + version_parts: VersionDict + like: str + codename: str + + +_UNIXCONFDIR = os.environ.get("UNIXCONFDIR", "/etc") +_UNIXUSRLIBDIR = os.environ.get("UNIXUSRLIBDIR", "/usr/lib") +_OS_RELEASE_BASENAME = "os-release" + +#: Translation table for normalizing the "ID" attribute defined in os-release +#: files, for use by the :func:`distro.id` method. +#: +#: * Key: Value as defined in the os-release file, translated to lower case, +#: with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_OS_ID = { + "ol": "oracle", # Oracle Linux + "opensuse-leap": "opensuse", # Newer versions of OpenSuSE report as opensuse-leap +} + +#: Translation table for normalizing the "Distributor ID" attribute returned by +#: the lsb_release command, for use by the :func:`distro.id` method. +#: +#: * Key: Value as returned by the lsb_release command, translated to lower +#: case, with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_LSB_ID = { + "enterpriseenterpriseas": "oracle", # Oracle Enterprise Linux 4 + "enterpriseenterpriseserver": "oracle", # Oracle Linux 5 + "redhatenterpriseworkstation": "rhel", # RHEL 6, 7 Workstation + "redhatenterpriseserver": "rhel", # RHEL 6, 7 Server + "redhatenterprisecomputenode": "rhel", # RHEL 6 ComputeNode +} + +#: Translation table for normalizing the distro ID derived from the file name +#: of distro release files, for use by the :func:`distro.id` method. +#: +#: * Key: Value as derived from the file name of a distro release file, +#: translated to lower case, with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_DISTRO_ID = { + "redhat": "rhel", # RHEL 6.x, 7.x +} + +# Pattern for content of distro release file (reversed) +_DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile( + r"(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)" +) + +# Pattern for base file name of distro release file +_DISTRO_RELEASE_BASENAME_PATTERN = re.compile(r"(\w+)[-_](release|version)$") + +# Base file names to be looked up for if _UNIXCONFDIR is not readable. +_DISTRO_RELEASE_BASENAMES = [ + "SuSE-release", + "arch-release", + "base-release", + "centos-release", + "fedora-release", + "gentoo-release", + "mageia-release", + "mandrake-release", + "mandriva-release", + "mandrivalinux-release", + "manjaro-release", + "oracle-release", + "redhat-release", + "rocky-release", + "sl-release", + "slackware-version", +] + +# Base file names to be ignored when searching for distro release file +_DISTRO_RELEASE_IGNORE_BASENAMES = ( + "debian_version", + "lsb-release", + "oem-release", + _OS_RELEASE_BASENAME, + "system-release", + "plesk-release", + "iredmail-release", +) + + +def linux_distribution(full_distribution_name: bool = True) -> Tuple[str, str, str]: + """ + .. deprecated:: 1.6.0 + + :func:`distro.linux_distribution()` is deprecated. It should only be + used as a compatibility shim with Python's + :py:func:`platform.linux_distribution()`. Please use :func:`distro.id`, + :func:`distro.version` and :func:`distro.name` instead. + + Return information about the current OS distribution as a tuple + ``(id_name, version, codename)`` with items as follows: + + * ``id_name``: If *full_distribution_name* is false, the result of + :func:`distro.id`. Otherwise, the result of :func:`distro.name`. + + * ``version``: The result of :func:`distro.version`. + + * ``codename``: The extra item (usually in parentheses) after the + os-release version number, or the result of :func:`distro.codename`. + + The interface of this function is compatible with the original + :py:func:`platform.linux_distribution` function, supporting a subset of + its parameters. + + The data it returns may not exactly be the same, because it uses more data + sources than the original function, and that may lead to different data if + the OS distribution is not consistent across multiple data sources it + provides (there are indeed such distributions ...). + + Another reason for differences is the fact that the :func:`distro.id` + method normalizes the distro ID string to a reliable machine-readable value + for a number of popular OS distributions. + """ + warnings.warn( + "distro.linux_distribution() is deprecated. It should only be used as a " + "compatibility shim with Python's platform.linux_distribution(). Please use " + "distro.id(), distro.version() and distro.name() instead.", + DeprecationWarning, + stacklevel=2, + ) + return _distro.linux_distribution(full_distribution_name) + + +def id() -> str: + """ + Return the distro ID of the current distribution, as a + machine-readable string. + + For a number of OS distributions, the returned distro ID value is + *reliable*, in the sense that it is documented and that it does not change + across releases of the distribution. + + This package maintains the following reliable distro ID values: + + ============== ========================================= + Distro ID Distribution + ============== ========================================= + "ubuntu" Ubuntu + "debian" Debian + "rhel" RedHat Enterprise Linux + "centos" CentOS + "fedora" Fedora + "sles" SUSE Linux Enterprise Server + "opensuse" openSUSE + "amzn" Amazon Linux + "arch" Arch Linux + "buildroot" Buildroot + "cloudlinux" CloudLinux OS + "exherbo" Exherbo Linux + "gentoo" GenToo Linux + "ibm_powerkvm" IBM PowerKVM + "kvmibm" KVM for IBM z Systems + "linuxmint" Linux Mint + "mageia" Mageia + "mandriva" Mandriva Linux + "parallels" Parallels + "pidora" Pidora + "raspbian" Raspbian + "oracle" Oracle Linux (and Oracle Enterprise Linux) + "scientific" Scientific Linux + "slackware" Slackware + "xenserver" XenServer + "openbsd" OpenBSD + "netbsd" NetBSD + "freebsd" FreeBSD + "midnightbsd" MidnightBSD + "rocky" Rocky Linux + "aix" AIX + "guix" Guix System + ============== ========================================= + + If you have a need to get distros for reliable IDs added into this set, + or if you find that the :func:`distro.id` function returns a different + distro ID for one of the listed distros, please create an issue in the + `distro issue tracker`_. + + **Lookup hierarchy and transformations:** + + First, the ID is obtained from the following sources, in the specified + order. The first available and non-empty value is used: + + * the value of the "ID" attribute of the os-release file, + + * the value of the "Distributor ID" attribute returned by the lsb_release + command, + + * the first part of the file name of the distro release file, + + The so determined ID value then passes the following transformations, + before it is returned by this method: + + * it is translated to lower case, + + * blanks (which should not be there anyway) are translated to underscores, + + * a normalization of the ID is performed, based upon + `normalization tables`_. The purpose of this normalization is to ensure + that the ID is as reliable as possible, even across incompatible changes + in the OS distributions. A common reason for an incompatible change is + the addition of an os-release file, or the addition of the lsb_release + command, with ID values that differ from what was previously determined + from the distro release file name. + """ + return _distro.id() + + +def name(pretty: bool = False) -> str: + """ + Return the name of the current OS distribution, as a human-readable + string. + + If *pretty* is false, the name is returned without version or codename. + (e.g. "CentOS Linux") + + If *pretty* is true, the version and codename are appended. + (e.g. "CentOS Linux 7.1.1503 (Core)") + + **Lookup hierarchy:** + + The name is obtained from the following sources, in the specified order. + The first available and non-empty value is used: + + * If *pretty* is false: + + - the value of the "NAME" attribute of the os-release file, + + - the value of the "Distributor ID" attribute returned by the lsb_release + command, + + - the value of the "<name>" field of the distro release file. + + * If *pretty* is true: + + - the value of the "PRETTY_NAME" attribute of the os-release file, + + - the value of the "Description" attribute returned by the lsb_release + command, + + - the value of the "<name>" field of the distro release file, appended + with the value of the pretty version ("<version_id>" and "<codename>" + fields) of the distro release file, if available. + """ + return _distro.name(pretty) + + +def version(pretty: bool = False, best: bool = False) -> str: + """ + Return the version of the current OS distribution, as a human-readable + string. + + If *pretty* is false, the version is returned without codename (e.g. + "7.0"). + + If *pretty* is true, the codename in parenthesis is appended, if the + codename is non-empty (e.g. "7.0 (Maipo)"). + + Some distributions provide version numbers with different precisions in + the different sources of distribution information. Examining the different + sources in a fixed priority order does not always yield the most precise + version (e.g. for Debian 8.2, or CentOS 7.1). + + Some other distributions may not provide this kind of information. In these + cases, an empty string would be returned. This behavior can be observed + with rolling releases distributions (e.g. Arch Linux). + + The *best* parameter can be used to control the approach for the returned + version: + + If *best* is false, the first non-empty version number in priority order of + the examined sources is returned. + + If *best* is true, the most precise version number out of all examined + sources is returned. + + **Lookup hierarchy:** + + In all cases, the version number is obtained from the following sources. + If *best* is false, this order represents the priority order: + + * the value of the "VERSION_ID" attribute of the os-release file, + * the value of the "Release" attribute returned by the lsb_release + command, + * the version number parsed from the "<version_id>" field of the first line + of the distro release file, + * the version number parsed from the "PRETTY_NAME" attribute of the + os-release file, if it follows the format of the distro release files. + * the version number parsed from the "Description" attribute returned by + the lsb_release command, if it follows the format of the distro release + files. + """ + return _distro.version(pretty, best) + + +def version_parts(best: bool = False) -> Tuple[str, str, str]: + """ + Return the version of the current OS distribution as a tuple + ``(major, minor, build_number)`` with items as follows: + + * ``major``: The result of :func:`distro.major_version`. + + * ``minor``: The result of :func:`distro.minor_version`. + + * ``build_number``: The result of :func:`distro.build_number`. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.version_parts(best) + + +def major_version(best: bool = False) -> str: + """ + Return the major version of the current OS distribution, as a string, + if provided. + Otherwise, the empty string is returned. The major version is the first + part of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.major_version(best) + + +def minor_version(best: bool = False) -> str: + """ + Return the minor version of the current OS distribution, as a string, + if provided. + Otherwise, the empty string is returned. The minor version is the second + part of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.minor_version(best) + + +def build_number(best: bool = False) -> str: + """ + Return the build number of the current OS distribution, as a string, + if provided. + Otherwise, the empty string is returned. The build number is the third part + of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.build_number(best) + + +def like() -> str: + """ + Return a space-separated list of distro IDs of distributions that are + closely related to the current OS distribution in regards to packaging + and programming interfaces, for example distributions the current + distribution is a derivative from. + + **Lookup hierarchy:** + + This information item is only provided by the os-release file. + For details, see the description of the "ID_LIKE" attribute in the + `os-release man page + <http://www.freedesktop.org/software/systemd/man/os-release.html>`_. + """ + return _distro.like() + + +def codename() -> str: + """ + Return the codename for the release of the current OS distribution, + as a string. + + If the distribution does not have a codename, an empty string is returned. + + Note that the returned codename is not always really a codename. For + example, openSUSE returns "x86_64". This function does not handle such + cases in any special way and just returns the string it finds, if any. + + **Lookup hierarchy:** + + * the codename within the "VERSION" attribute of the os-release file, if + provided, + + * the value of the "Codename" attribute returned by the lsb_release + command, + + * the value of the "<codename>" field of the distro release file. + """ + return _distro.codename() + + +def info(pretty: bool = False, best: bool = False) -> InfoDict: + """ + Return certain machine-readable information items about the current OS + distribution in a dictionary, as shown in the following example: + + .. sourcecode:: python + + { + 'id': 'rhel', + 'version': '7.0', + 'version_parts': { + 'major': '7', + 'minor': '0', + 'build_number': '' + }, + 'like': 'fedora', + 'codename': 'Maipo' + } + + The dictionary structure and keys are always the same, regardless of which + information items are available in the underlying data sources. The values + for the various keys are as follows: + + * ``id``: The result of :func:`distro.id`. + + * ``version``: The result of :func:`distro.version`. + + * ``version_parts -> major``: The result of :func:`distro.major_version`. + + * ``version_parts -> minor``: The result of :func:`distro.minor_version`. + + * ``version_parts -> build_number``: The result of + :func:`distro.build_number`. + + * ``like``: The result of :func:`distro.like`. + + * ``codename``: The result of :func:`distro.codename`. + + For a description of the *pretty* and *best* parameters, see the + :func:`distro.version` method. + """ + return _distro.info(pretty, best) + + +def os_release_info() -> Dict[str, str]: + """ + Return a dictionary containing key-value pairs for the information items + from the os-release file data source of the current OS distribution. + + See `os-release file`_ for details about these information items. + """ + return _distro.os_release_info() + + +def lsb_release_info() -> Dict[str, str]: + """ + Return a dictionary containing key-value pairs for the information items + from the lsb_release command data source of the current OS distribution. + + See `lsb_release command output`_ for details about these information + items. + """ + return _distro.lsb_release_info() + + +def distro_release_info() -> Dict[str, str]: + """ + Return a dictionary containing key-value pairs for the information items + from the distro release file data source of the current OS distribution. + + See `distro release file`_ for details about these information items. + """ + return _distro.distro_release_info() + + +def uname_info() -> Dict[str, str]: + """ + Return a dictionary containing key-value pairs for the information items + from the distro release file data source of the current OS distribution. + """ + return _distro.uname_info() + + +def os_release_attr(attribute: str) -> str: + """ + Return a single named information item from the os-release file data source + of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `os-release file`_ for details about these information items. + """ + return _distro.os_release_attr(attribute) + + +def lsb_release_attr(attribute: str) -> str: + """ + Return a single named information item from the lsb_release command output + data source of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `lsb_release command output`_ for details about these information + items. + """ + return _distro.lsb_release_attr(attribute) + + +def distro_release_attr(attribute: str) -> str: + """ + Return a single named information item from the distro release file + data source of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `distro release file`_ for details about these information items. + """ + return _distro.distro_release_attr(attribute) + + +def uname_attr(attribute: str) -> str: + """ + Return a single named information item from the distro release file + data source of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + """ + return _distro.uname_attr(attribute) + + +try: + from functools import cached_property +except ImportError: + # Python < 3.8 + class cached_property: # type: ignore + """A version of @property which caches the value. On access, it calls the + underlying function and sets the value in `__dict__` so future accesses + will not re-call the property. + """ + + def __init__(self, f: Callable[[Any], Any]) -> None: + self._fname = f.__name__ + self._f = f + + def __get__(self, obj: Any, owner: Type[Any]) -> Any: + assert obj is not None, f"call {self._fname} on an instance" + ret = obj.__dict__[self._fname] = self._f(obj) + return ret + + +class LinuxDistribution: + """ + Provides information about a OS distribution. + + This package creates a private module-global instance of this class with + default initialization arguments, that is used by the + `consolidated accessor functions`_ and `single source accessor functions`_. + By using default initialization arguments, that module-global instance + returns data about the current OS distribution (i.e. the distro this + package runs on). + + Normally, it is not necessary to create additional instances of this class. + However, in situations where control is needed over the exact data sources + that are used, instances of this class can be created with a specific + distro release file, or a specific os-release file, or without invoking the + lsb_release command. + """ + + def __init__( + self, + include_lsb: Optional[bool] = None, + os_release_file: str = "", + distro_release_file: str = "", + include_uname: Optional[bool] = None, + root_dir: Optional[str] = None, + include_oslevel: Optional[bool] = None, + ) -> None: + """ + The initialization method of this class gathers information from the + available data sources, and stores that in private instance attributes. + Subsequent access to the information items uses these private instance + attributes, so that the data sources are read only once. + + Parameters: + + * ``include_lsb`` (bool): Controls whether the + `lsb_release command output`_ is included as a data source. + + If the lsb_release command is not available in the program execution + path, the data source for the lsb_release command will be empty. + + * ``os_release_file`` (string): The path name of the + `os-release file`_ that is to be used as a data source. + + An empty string (the default) will cause the default path name to + be used (see `os-release file`_ for details). + + If the specified or defaulted os-release file does not exist, the + data source for the os-release file will be empty. + + * ``distro_release_file`` (string): The path name of the + `distro release file`_ that is to be used as a data source. + + An empty string (the default) will cause a default search algorithm + to be used (see `distro release file`_ for details). + + If the specified distro release file does not exist, or if no default + distro release file can be found, the data source for the distro + release file will be empty. + + * ``include_uname`` (bool): Controls whether uname command output is + included as a data source. If the uname command is not available in + the program execution path the data source for the uname command will + be empty. + + * ``root_dir`` (string): The absolute path to the root directory to use + to find distro-related information files. Note that ``include_*`` + parameters must not be enabled in combination with ``root_dir``. + + * ``include_oslevel`` (bool): Controls whether (AIX) oslevel command + output is included as a data source. If the oslevel command is not + available in the program execution path the data source will be + empty. + + Public instance attributes: + + * ``os_release_file`` (string): The path name of the + `os-release file`_ that is actually used as a data source. The + empty string if no distro release file is used as a data source. + + * ``distro_release_file`` (string): The path name of the + `distro release file`_ that is actually used as a data source. The + empty string if no distro release file is used as a data source. + + * ``include_lsb`` (bool): The result of the ``include_lsb`` parameter. + This controls whether the lsb information will be loaded. + + * ``include_uname`` (bool): The result of the ``include_uname`` + parameter. This controls whether the uname information will + be loaded. + + * ``include_oslevel`` (bool): The result of the ``include_oslevel`` + parameter. This controls whether (AIX) oslevel information will be + loaded. + + * ``root_dir`` (string): The result of the ``root_dir`` parameter. + The absolute path to the root directory to use to find distro-related + information files. + + Raises: + + * :py:exc:`ValueError`: Initialization parameters combination is not + supported. + + * :py:exc:`OSError`: Some I/O issue with an os-release file or distro + release file. + + * :py:exc:`UnicodeError`: A data source has unexpected characters or + uses an unexpected encoding. + """ + self.root_dir = root_dir + self.etc_dir = os.path.join(root_dir, "etc") if root_dir else _UNIXCONFDIR + self.usr_lib_dir = ( + os.path.join(root_dir, "usr/lib") if root_dir else _UNIXUSRLIBDIR + ) + + if os_release_file: + self.os_release_file = os_release_file + else: + etc_dir_os_release_file = os.path.join(self.etc_dir, _OS_RELEASE_BASENAME) + usr_lib_os_release_file = os.path.join( + self.usr_lib_dir, _OS_RELEASE_BASENAME + ) + + # NOTE: The idea is to respect order **and** have it set + # at all times for API backwards compatibility. + if os.path.isfile(etc_dir_os_release_file) or not os.path.isfile( + usr_lib_os_release_file + ): + self.os_release_file = etc_dir_os_release_file + else: + self.os_release_file = usr_lib_os_release_file + + self.distro_release_file = distro_release_file or "" # updated later + + is_root_dir_defined = root_dir is not None + if is_root_dir_defined and (include_lsb or include_uname or include_oslevel): + raise ValueError( + "Including subprocess data sources from specific root_dir is disallowed" + " to prevent false information" + ) + self.include_lsb = ( + include_lsb if include_lsb is not None else not is_root_dir_defined + ) + self.include_uname = ( + include_uname if include_uname is not None else not is_root_dir_defined + ) + self.include_oslevel = ( + include_oslevel if include_oslevel is not None else not is_root_dir_defined + ) + + def __repr__(self) -> str: + """Return repr of all info""" + return ( + "LinuxDistribution(" + "os_release_file={self.os_release_file!r}, " + "distro_release_file={self.distro_release_file!r}, " + "include_lsb={self.include_lsb!r}, " + "include_uname={self.include_uname!r}, " + "include_oslevel={self.include_oslevel!r}, " + "root_dir={self.root_dir!r}, " + "_os_release_info={self._os_release_info!r}, " + "_lsb_release_info={self._lsb_release_info!r}, " + "_distro_release_info={self._distro_release_info!r}, " + "_uname_info={self._uname_info!r}, " + "_oslevel_info={self._oslevel_info!r})".format(self=self) + ) + + def linux_distribution( + self, full_distribution_name: bool = True + ) -> Tuple[str, str, str]: + """ + Return information about the OS distribution that is compatible + with Python's :func:`platform.linux_distribution`, supporting a subset + of its parameters. + + For details, see :func:`distro.linux_distribution`. + """ + return ( + self.name() if full_distribution_name else self.id(), + self.version(), + self._os_release_info.get("release_codename") or self.codename(), + ) + + def id(self) -> str: + """Return the distro ID of the OS distribution, as a string. + + For details, see :func:`distro.id`. + """ + + def normalize(distro_id: str, table: Dict[str, str]) -> str: + distro_id = distro_id.lower().replace(" ", "_") + return table.get(distro_id, distro_id) + + distro_id = self.os_release_attr("id") + if distro_id: + return normalize(distro_id, NORMALIZED_OS_ID) + + distro_id = self.lsb_release_attr("distributor_id") + if distro_id: + return normalize(distro_id, NORMALIZED_LSB_ID) + + distro_id = self.distro_release_attr("id") + if distro_id: + return normalize(distro_id, NORMALIZED_DISTRO_ID) + + distro_id = self.uname_attr("id") + if distro_id: + return normalize(distro_id, NORMALIZED_DISTRO_ID) + + return "" + + def name(self, pretty: bool = False) -> str: + """ + Return the name of the OS distribution, as a string. + + For details, see :func:`distro.name`. + """ + name = ( + self.os_release_attr("name") + or self.lsb_release_attr("distributor_id") + or self.distro_release_attr("name") + or self.uname_attr("name") + ) + if pretty: + name = self.os_release_attr("pretty_name") or self.lsb_release_attr( + "description" + ) + if not name: + name = self.distro_release_attr("name") or self.uname_attr("name") + version = self.version(pretty=True) + if version: + name = f"{name} {version}" + return name or "" + + def version(self, pretty: bool = False, best: bool = False) -> str: + """ + Return the version of the OS distribution, as a string. + + For details, see :func:`distro.version`. + """ + versions = [ + self.os_release_attr("version_id"), + self.lsb_release_attr("release"), + self.distro_release_attr("version_id"), + self._parse_distro_release_content(self.os_release_attr("pretty_name")).get( + "version_id", "" + ), + self._parse_distro_release_content( + self.lsb_release_attr("description") + ).get("version_id", ""), + self.uname_attr("release"), + ] + if self.uname_attr("id").startswith("aix"): + # On AIX platforms, prefer oslevel command output. + versions.insert(0, self.oslevel_info()) + elif self.id() == "debian" or "debian" in self.like().split(): + # On Debian-like, add debian_version file content to candidates list. + versions.append(self._debian_version) + version = "" + if best: + # This algorithm uses the last version in priority order that has + # the best precision. If the versions are not in conflict, that + # does not matter; otherwise, using the last one instead of the + # first one might be considered a surprise. + for v in versions: + if v.count(".") > version.count(".") or version == "": + version = v + else: + for v in versions: + if v != "": + version = v + break + if pretty and version and self.codename(): + version = f"{version} ({self.codename()})" + return version + + def version_parts(self, best: bool = False) -> Tuple[str, str, str]: + """ + Return the version of the OS distribution, as a tuple of version + numbers. + + For details, see :func:`distro.version_parts`. + """ + version_str = self.version(best=best) + if version_str: + version_regex = re.compile(r"(\d+)\.?(\d+)?\.?(\d+)?") + matches = version_regex.match(version_str) + if matches: + major, minor, build_number = matches.groups() + return major, minor or "", build_number or "" + return "", "", "" + + def major_version(self, best: bool = False) -> str: + """ + Return the major version number of the current distribution. + + For details, see :func:`distro.major_version`. + """ + return self.version_parts(best)[0] + + def minor_version(self, best: bool = False) -> str: + """ + Return the minor version number of the current distribution. + + For details, see :func:`distro.minor_version`. + """ + return self.version_parts(best)[1] + + def build_number(self, best: bool = False) -> str: + """ + Return the build number of the current distribution. + + For details, see :func:`distro.build_number`. + """ + return self.version_parts(best)[2] + + def like(self) -> str: + """ + Return the IDs of distributions that are like the OS distribution. + + For details, see :func:`distro.like`. + """ + return self.os_release_attr("id_like") or "" + + def codename(self) -> str: + """ + Return the codename of the OS distribution. + + For details, see :func:`distro.codename`. + """ + try: + # Handle os_release specially since distros might purposefully set + # this to empty string to have no codename + return self._os_release_info["codename"] + except KeyError: + return ( + self.lsb_release_attr("codename") + or self.distro_release_attr("codename") + or "" + ) + + def info(self, pretty: bool = False, best: bool = False) -> InfoDict: + """ + Return certain machine-readable information about the OS + distribution. + + For details, see :func:`distro.info`. + """ + return dict( + id=self.id(), + version=self.version(pretty, best), + version_parts=dict( + major=self.major_version(best), + minor=self.minor_version(best), + build_number=self.build_number(best), + ), + like=self.like(), + codename=self.codename(), + ) + + def os_release_info(self) -> Dict[str, str]: + """ + Return a dictionary containing key-value pairs for the information + items from the os-release file data source of the OS distribution. + + For details, see :func:`distro.os_release_info`. + """ + return self._os_release_info + + def lsb_release_info(self) -> Dict[str, str]: + """ + Return a dictionary containing key-value pairs for the information + items from the lsb_release command data source of the OS + distribution. + + For details, see :func:`distro.lsb_release_info`. + """ + return self._lsb_release_info + + def distro_release_info(self) -> Dict[str, str]: + """ + Return a dictionary containing key-value pairs for the information + items from the distro release file data source of the OS + distribution. + + For details, see :func:`distro.distro_release_info`. + """ + return self._distro_release_info + + def uname_info(self) -> Dict[str, str]: + """ + Return a dictionary containing key-value pairs for the information + items from the uname command data source of the OS distribution. + + For details, see :func:`distro.uname_info`. + """ + return self._uname_info + + def oslevel_info(self) -> str: + """ + Return AIX' oslevel command output. + """ + return self._oslevel_info + + def os_release_attr(self, attribute: str) -> str: + """ + Return a single named information item from the os-release file data + source of the OS distribution. + + For details, see :func:`distro.os_release_attr`. + """ + return self._os_release_info.get(attribute, "") + + def lsb_release_attr(self, attribute: str) -> str: + """ + Return a single named information item from the lsb_release command + output data source of the OS distribution. + + For details, see :func:`distro.lsb_release_attr`. + """ + return self._lsb_release_info.get(attribute, "") + + def distro_release_attr(self, attribute: str) -> str: + """ + Return a single named information item from the distro release file + data source of the OS distribution. + + For details, see :func:`distro.distro_release_attr`. + """ + return self._distro_release_info.get(attribute, "") + + def uname_attr(self, attribute: str) -> str: + """ + Return a single named information item from the uname command + output data source of the OS distribution. + + For details, see :func:`distro.uname_attr`. + """ + return self._uname_info.get(attribute, "") + + @cached_property + def _os_release_info(self) -> Dict[str, str]: + """ + Get the information items from the specified os-release file. + + Returns: + A dictionary containing all information items. + """ + if os.path.isfile(self.os_release_file): + with open(self.os_release_file, encoding="utf-8") as release_file: + return self._parse_os_release_content(release_file) + return {} + + @staticmethod + def _parse_os_release_content(lines: TextIO) -> Dict[str, str]: + """ + Parse the lines of an os-release file. + + Parameters: + + * lines: Iterable through the lines in the os-release file. + Each line must be a unicode string or a UTF-8 encoded byte + string. + + Returns: + A dictionary containing all information items. + """ + props = {} + lexer = shlex.shlex(lines, posix=True) + lexer.whitespace_split = True + + tokens = list(lexer) + for token in tokens: + # At this point, all shell-like parsing has been done (i.e. + # comments processed, quotes and backslash escape sequences + # processed, multi-line values assembled, trailing newlines + # stripped, etc.), so the tokens are now either: + # * variable assignments: var=value + # * commands or their arguments (not allowed in os-release) + # Ignore any tokens that are not variable assignments + if "=" in token: + k, v = token.split("=", 1) + props[k.lower()] = v + + if "version" in props: + # extract release codename (if any) from version attribute + match = re.search(r"\((\D+)\)|,\s*(\D+)", props["version"]) + if match: + release_codename = match.group(1) or match.group(2) + props["codename"] = props["release_codename"] = release_codename + + if "version_codename" in props: + # os-release added a version_codename field. Use that in + # preference to anything else Note that some distros purposefully + # do not have code names. They should be setting + # version_codename="" + props["codename"] = props["version_codename"] + elif "ubuntu_codename" in props: + # Same as above but a non-standard field name used on older Ubuntus + props["codename"] = props["ubuntu_codename"] + + return props + + @cached_property + def _lsb_release_info(self) -> Dict[str, str]: + """ + Get the information items from the lsb_release command output. + + Returns: + A dictionary containing all information items. + """ + if not self.include_lsb: + return {} + try: + cmd = ("lsb_release", "-a") + stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) + # Command not found or lsb_release returned error + except (OSError, subprocess.CalledProcessError): + return {} + content = self._to_str(stdout).splitlines() + return self._parse_lsb_release_content(content) + + @staticmethod + def _parse_lsb_release_content(lines: Iterable[str]) -> Dict[str, str]: + """ + Parse the output of the lsb_release command. + + Parameters: + + * lines: Iterable through the lines of the lsb_release output. + Each line must be a unicode string or a UTF-8 encoded byte + string. + + Returns: + A dictionary containing all information items. + """ + props = {} + for line in lines: + kv = line.strip("\n").split(":", 1) + if len(kv) != 2: + # Ignore lines without colon. + continue + k, v = kv + props.update({k.replace(" ", "_").lower(): v.strip()}) + return props + + @cached_property + def _uname_info(self) -> Dict[str, str]: + if not self.include_uname: + return {} + try: + cmd = ("uname", "-rs") + stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) + except OSError: + return {} + content = self._to_str(stdout).splitlines() + return self._parse_uname_content(content) + + @cached_property + def _oslevel_info(self) -> str: + if not self.include_oslevel: + return "" + try: + stdout = subprocess.check_output("oslevel", stderr=subprocess.DEVNULL) + except (OSError, subprocess.CalledProcessError): + return "" + return self._to_str(stdout).strip() + + @cached_property + def _debian_version(self) -> str: + try: + with open( + os.path.join(self.etc_dir, "debian_version"), encoding="ascii" + ) as fp: + return fp.readline().rstrip() + except FileNotFoundError: + return "" + + @staticmethod + def _parse_uname_content(lines: Sequence[str]) -> Dict[str, str]: + if not lines: + return {} + props = {} + match = re.search(r"^([^\s]+)\s+([\d\.]+)", lines[0].strip()) + if match: + name, version = match.groups() + + # This is to prevent the Linux kernel version from + # appearing as the 'best' version on otherwise + # identifiable distributions. + if name == "Linux": + return {} + props["id"] = name.lower() + props["name"] = name + props["release"] = version + return props + + @staticmethod + def _to_str(bytestring: bytes) -> str: + encoding = sys.getfilesystemencoding() + return bytestring.decode(encoding) + + @cached_property + def _distro_release_info(self) -> Dict[str, str]: + """ + Get the information items from the specified distro release file. + + Returns: + A dictionary containing all information items. + """ + if self.distro_release_file: + # If it was specified, we use it and parse what we can, even if + # its file name or content does not match the expected pattern. + distro_info = self._parse_distro_release_file(self.distro_release_file) + basename = os.path.basename(self.distro_release_file) + # The file name pattern for user-specified distro release files + # is somewhat more tolerant (compared to when searching for the + # file), because we want to use what was specified as best as + # possible. + match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) + else: + try: + basenames = [ + basename + for basename in os.listdir(self.etc_dir) + if basename not in _DISTRO_RELEASE_IGNORE_BASENAMES + and os.path.isfile(os.path.join(self.etc_dir, basename)) + ] + # We sort for repeatability in cases where there are multiple + # distro specific files; e.g. CentOS, Oracle, Enterprise all + # containing `redhat-release` on top of their own. + basenames.sort() + except OSError: + # This may occur when /etc is not readable but we can't be + # sure about the *-release files. Check common entries of + # /etc for information. If they turn out to not be there the + # error is handled in `_parse_distro_release_file()`. + basenames = _DISTRO_RELEASE_BASENAMES + for basename in basenames: + match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) + if match is None: + continue + filepath = os.path.join(self.etc_dir, basename) + distro_info = self._parse_distro_release_file(filepath) + # The name is always present if the pattern matches. + if "name" not in distro_info: + continue + self.distro_release_file = filepath + break + else: # the loop didn't "break": no candidate. + return {} + + if match is not None: + distro_info["id"] = match.group(1) + + # CloudLinux < 7: manually enrich info with proper id. + if "cloudlinux" in distro_info.get("name", "").lower(): + distro_info["id"] = "cloudlinux" + + return distro_info + + def _parse_distro_release_file(self, filepath: str) -> Dict[str, str]: + """ + Parse a distro release file. + + Parameters: + + * filepath: Path name of the distro release file. + + Returns: + A dictionary containing all information items. + """ + try: + with open(filepath, encoding="utf-8") as fp: + # Only parse the first line. For instance, on SLES there + # are multiple lines. We don't want them... + return self._parse_distro_release_content(fp.readline()) + except OSError: + # Ignore not being able to read a specific, seemingly version + # related file. + # See https://github.com/python-distro/distro/issues/162 + return {} + + @staticmethod + def _parse_distro_release_content(line: str) -> Dict[str, str]: + """ + Parse a line from a distro release file. + + Parameters: + * line: Line from the distro release file. Must be a unicode string + or a UTF-8 encoded byte string. + + Returns: + A dictionary containing all information items. + """ + matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match(line.strip()[::-1]) + distro_info = {} + if matches: + # regexp ensures non-None + distro_info["name"] = matches.group(3)[::-1] + if matches.group(2): + distro_info["version_id"] = matches.group(2)[::-1] + if matches.group(1): + distro_info["codename"] = matches.group(1)[::-1] + elif line: + distro_info["name"] = line.strip() + return distro_info + + +_distro = LinuxDistribution() + + +def main() -> None: + logger = logging.getLogger(__name__) + logger.setLevel(logging.DEBUG) + logger.addHandler(logging.StreamHandler(sys.stdout)) + + parser = argparse.ArgumentParser(description="OS distro info tool") + parser.add_argument( + "--json", "-j", help="Output in machine readable format", action="store_true" + ) + + parser.add_argument( + "--root-dir", + "-r", + type=str, + dest="root_dir", + help="Path to the root filesystem directory (defaults to /)", + ) + + args = parser.parse_args() + + if args.root_dir: + dist = LinuxDistribution( + include_lsb=False, + include_uname=False, + include_oslevel=False, + root_dir=args.root_dir, + ) + else: + dist = _distro + + if args.json: + logger.info(json.dumps(dist.info(), indent=4, sort_keys=True)) + else: + logger.info("Name: %s", dist.name(pretty=True)) + distribution_version = dist.version(pretty=True) + logger.info("Version: %s", distribution_version) + distribution_codename = dist.codename() + logger.info("Codename: %s", distribution_codename) + + +if __name__ == "__main__": + main() diff --git a/virt/lib/python3.9/site-packages/pip/_vendor/idna/idnadata 3.py b/virt/lib/python3.9/site-packages/pip/_vendor/idna/idnadata 3.py new file mode 100644 index 00000000..95cd03d3 --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_vendor/idna/idnadata 3.py @@ -0,0 +1,2151 @@ +# This file is automatically generated by tools/idna-data + +__version__ = '15.0.0' +scripts = { + 'Greek': ( + 0x37000000374, + 0x37500000378, + 0x37a0000037e, + 0x37f00000380, + 0x38400000385, + 0x38600000387, + 0x3880000038b, + 0x38c0000038d, + 0x38e000003a2, + 0x3a3000003e2, + 0x3f000000400, + 0x1d2600001d2b, + 0x1d5d00001d62, + 0x1d6600001d6b, + 0x1dbf00001dc0, + 0x1f0000001f16, + 0x1f1800001f1e, + 0x1f2000001f46, + 0x1f4800001f4e, + 0x1f5000001f58, + 0x1f5900001f5a, + 0x1f5b00001f5c, + 0x1f5d00001f5e, + 0x1f5f00001f7e, + 0x1f8000001fb5, + 0x1fb600001fc5, + 0x1fc600001fd4, + 0x1fd600001fdc, + 0x1fdd00001ff0, + 0x1ff200001ff5, + 0x1ff600001fff, + 0x212600002127, + 0xab650000ab66, + 0x101400001018f, + 0x101a0000101a1, + 0x1d2000001d246, + ), + 'Han': ( + 0x2e8000002e9a, + 0x2e9b00002ef4, + 0x2f0000002fd6, + 0x300500003006, + 0x300700003008, + 0x30210000302a, + 0x30380000303c, + 0x340000004dc0, + 0x4e000000a000, + 0xf9000000fa6e, + 0xfa700000fada, + 0x16fe200016fe4, + 0x16ff000016ff2, + 0x200000002a6e0, + 0x2a7000002b73a, + 0x2b7400002b81e, + 0x2b8200002cea2, + 0x2ceb00002ebe1, + 0x2f8000002fa1e, + 0x300000003134b, + 0x31350000323b0, + ), + 'Hebrew': ( + 0x591000005c8, + 0x5d0000005eb, + 0x5ef000005f5, + 0xfb1d0000fb37, + 0xfb380000fb3d, + 0xfb3e0000fb3f, + 0xfb400000fb42, + 0xfb430000fb45, + 0xfb460000fb50, + ), + 'Hiragana': ( + 0x304100003097, + 0x309d000030a0, + 0x1b0010001b120, + 0x1b1320001b133, + 0x1b1500001b153, + 0x1f2000001f201, + ), + 'Katakana': ( + 0x30a1000030fb, + 0x30fd00003100, + 0x31f000003200, + 0x32d0000032ff, + 0x330000003358, + 0xff660000ff70, + 0xff710000ff9e, + 0x1aff00001aff4, + 0x1aff50001affc, + 0x1affd0001afff, + 0x1b0000001b001, + 0x1b1200001b123, + 0x1b1550001b156, + 0x1b1640001b168, + ), +} +joining_types = { + 0x600: 85, + 0x601: 85, + 0x602: 85, + 0x603: 85, + 0x604: 85, + 0x605: 85, + 0x608: 85, + 0x60b: 85, + 0x620: 68, + 0x621: 85, + 0x622: 82, + 0x623: 82, + 0x624: 82, + 0x625: 82, + 0x626: 68, + 0x627: 82, + 0x628: 68, + 0x629: 82, + 0x62a: 68, + 0x62b: 68, + 0x62c: 68, + 0x62d: 68, + 0x62e: 68, + 0x62f: 82, + 0x630: 82, + 0x631: 82, + 0x632: 82, + 0x633: 68, + 0x634: 68, + 0x635: 68, + 0x636: 68, + 0x637: 68, + 0x638: 68, + 0x639: 68, + 0x63a: 68, + 0x63b: 68, + 0x63c: 68, + 0x63d: 68, + 0x63e: 68, + 0x63f: 68, + 0x640: 67, + 0x641: 68, + 0x642: 68, + 0x643: 68, + 0x644: 68, + 0x645: 68, + 0x646: 68, + 0x647: 68, + 0x648: 82, + 0x649: 68, + 0x64a: 68, + 0x66e: 68, + 0x66f: 68, + 0x671: 82, + 0x672: 82, + 0x673: 82, + 0x674: 85, + 0x675: 82, + 0x676: 82, + 0x677: 82, + 0x678: 68, + 0x679: 68, + 0x67a: 68, + 0x67b: 68, + 0x67c: 68, + 0x67d: 68, + 0x67e: 68, + 0x67f: 68, + 0x680: 68, + 0x681: 68, + 0x682: 68, + 0x683: 68, + 0x684: 68, + 0x685: 68, + 0x686: 68, + 0x687: 68, + 0x688: 82, + 0x689: 82, + 0x68a: 82, + 0x68b: 82, + 0x68c: 82, + 0x68d: 82, + 0x68e: 82, + 0x68f: 82, + 0x690: 82, + 0x691: 82, + 0x692: 82, + 0x693: 82, + 0x694: 82, + 0x695: 82, + 0x696: 82, + 0x697: 82, + 0x698: 82, + 0x699: 82, + 0x69a: 68, + 0x69b: 68, + 0x69c: 68, + 0x69d: 68, + 0x69e: 68, + 0x69f: 68, + 0x6a0: 68, + 0x6a1: 68, + 0x6a2: 68, + 0x6a3: 68, + 0x6a4: 68, + 0x6a5: 68, + 0x6a6: 68, + 0x6a7: 68, + 0x6a8: 68, + 0x6a9: 68, + 0x6aa: 68, + 0x6ab: 68, + 0x6ac: 68, + 0x6ad: 68, + 0x6ae: 68, + 0x6af: 68, + 0x6b0: 68, + 0x6b1: 68, + 0x6b2: 68, + 0x6b3: 68, + 0x6b4: 68, + 0x6b5: 68, + 0x6b6: 68, + 0x6b7: 68, + 0x6b8: 68, + 0x6b9: 68, + 0x6ba: 68, + 0x6bb: 68, + 0x6bc: 68, + 0x6bd: 68, + 0x6be: 68, + 0x6bf: 68, + 0x6c0: 82, + 0x6c1: 68, + 0x6c2: 68, + 0x6c3: 82, + 0x6c4: 82, + 0x6c5: 82, + 0x6c6: 82, + 0x6c7: 82, + 0x6c8: 82, + 0x6c9: 82, + 0x6ca: 82, + 0x6cb: 82, + 0x6cc: 68, + 0x6cd: 82, + 0x6ce: 68, + 0x6cf: 82, + 0x6d0: 68, + 0x6d1: 68, + 0x6d2: 82, + 0x6d3: 82, + 0x6d5: 82, + 0x6dd: 85, + 0x6ee: 82, + 0x6ef: 82, + 0x6fa: 68, + 0x6fb: 68, + 0x6fc: 68, + 0x6ff: 68, + 0x70f: 84, + 0x710: 82, + 0x712: 68, + 0x713: 68, + 0x714: 68, + 0x715: 82, + 0x716: 82, + 0x717: 82, + 0x718: 82, + 0x719: 82, + 0x71a: 68, + 0x71b: 68, + 0x71c: 68, + 0x71d: 68, + 0x71e: 82, + 0x71f: 68, + 0x720: 68, + 0x721: 68, + 0x722: 68, + 0x723: 68, + 0x724: 68, + 0x725: 68, + 0x726: 68, + 0x727: 68, + 0x728: 82, + 0x729: 68, + 0x72a: 82, + 0x72b: 68, + 0x72c: 82, + 0x72d: 68, + 0x72e: 68, + 0x72f: 82, + 0x74d: 82, + 0x74e: 68, + 0x74f: 68, + 0x750: 68, + 0x751: 68, + 0x752: 68, + 0x753: 68, + 0x754: 68, + 0x755: 68, + 0x756: 68, + 0x757: 68, + 0x758: 68, + 0x759: 82, + 0x75a: 82, + 0x75b: 82, + 0x75c: 68, + 0x75d: 68, + 0x75e: 68, + 0x75f: 68, + 0x760: 68, + 0x761: 68, + 0x762: 68, + 0x763: 68, + 0x764: 68, + 0x765: 68, + 0x766: 68, + 0x767: 68, + 0x768: 68, + 0x769: 68, + 0x76a: 68, + 0x76b: 82, + 0x76c: 82, + 0x76d: 68, + 0x76e: 68, + 0x76f: 68, + 0x770: 68, + 0x771: 82, + 0x772: 68, + 0x773: 82, + 0x774: 82, + 0x775: 68, + 0x776: 68, + 0x777: 68, + 0x778: 82, + 0x779: 82, + 0x77a: 68, + 0x77b: 68, + 0x77c: 68, + 0x77d: 68, + 0x77e: 68, + 0x77f: 68, + 0x7ca: 68, + 0x7cb: 68, + 0x7cc: 68, + 0x7cd: 68, + 0x7ce: 68, + 0x7cf: 68, + 0x7d0: 68, + 0x7d1: 68, + 0x7d2: 68, + 0x7d3: 68, + 0x7d4: 68, + 0x7d5: 68, + 0x7d6: 68, + 0x7d7: 68, + 0x7d8: 68, + 0x7d9: 68, + 0x7da: 68, + 0x7db: 68, + 0x7dc: 68, + 0x7dd: 68, + 0x7de: 68, + 0x7df: 68, + 0x7e0: 68, + 0x7e1: 68, + 0x7e2: 68, + 0x7e3: 68, + 0x7e4: 68, + 0x7e5: 68, + 0x7e6: 68, + 0x7e7: 68, + 0x7e8: 68, + 0x7e9: 68, + 0x7ea: 68, + 0x7fa: 67, + 0x840: 82, + 0x841: 68, + 0x842: 68, + 0x843: 68, + 0x844: 68, + 0x845: 68, + 0x846: 82, + 0x847: 82, + 0x848: 68, + 0x849: 82, + 0x84a: 68, + 0x84b: 68, + 0x84c: 68, + 0x84d: 68, + 0x84e: 68, + 0x84f: 68, + 0x850: 68, + 0x851: 68, + 0x852: 68, + 0x853: 68, + 0x854: 82, + 0x855: 68, + 0x856: 82, + 0x857: 82, + 0x858: 82, + 0x860: 68, + 0x861: 85, + 0x862: 68, + 0x863: 68, + 0x864: 68, + 0x865: 68, + 0x866: 85, + 0x867: 82, + 0x868: 68, + 0x869: 82, + 0x86a: 82, + 0x870: 82, + 0x871: 82, + 0x872: 82, + 0x873: 82, + 0x874: 82, + 0x875: 82, + 0x876: 82, + 0x877: 82, + 0x878: 82, + 0x879: 82, + 0x87a: 82, + 0x87b: 82, + 0x87c: 82, + 0x87d: 82, + 0x87e: 82, + 0x87f: 82, + 0x880: 82, + 0x881: 82, + 0x882: 82, + 0x883: 67, + 0x884: 67, + 0x885: 67, + 0x886: 68, + 0x887: 85, + 0x888: 85, + 0x889: 68, + 0x88a: 68, + 0x88b: 68, + 0x88c: 68, + 0x88d: 68, + 0x88e: 82, + 0x890: 85, + 0x891: 85, + 0x8a0: 68, + 0x8a1: 68, + 0x8a2: 68, + 0x8a3: 68, + 0x8a4: 68, + 0x8a5: 68, + 0x8a6: 68, + 0x8a7: 68, + 0x8a8: 68, + 0x8a9: 68, + 0x8aa: 82, + 0x8ab: 82, + 0x8ac: 82, + 0x8ad: 85, + 0x8ae: 82, + 0x8af: 68, + 0x8b0: 68, + 0x8b1: 82, + 0x8b2: 82, + 0x8b3: 68, + 0x8b4: 68, + 0x8b5: 68, + 0x8b6: 68, + 0x8b7: 68, + 0x8b8: 68, + 0x8b9: 82, + 0x8ba: 68, + 0x8bb: 68, + 0x8bc: 68, + 0x8bd: 68, + 0x8be: 68, + 0x8bf: 68, + 0x8c0: 68, + 0x8c1: 68, + 0x8c2: 68, + 0x8c3: 68, + 0x8c4: 68, + 0x8c5: 68, + 0x8c6: 68, + 0x8c7: 68, + 0x8c8: 68, + 0x8e2: 85, + 0x1806: 85, + 0x1807: 68, + 0x180a: 67, + 0x180e: 85, + 0x1820: 68, + 0x1821: 68, + 0x1822: 68, + 0x1823: 68, + 0x1824: 68, + 0x1825: 68, + 0x1826: 68, + 0x1827: 68, + 0x1828: 68, + 0x1829: 68, + 0x182a: 68, + 0x182b: 68, + 0x182c: 68, + 0x182d: 68, + 0x182e: 68, + 0x182f: 68, + 0x1830: 68, + 0x1831: 68, + 0x1832: 68, + 0x1833: 68, + 0x1834: 68, + 0x1835: 68, + 0x1836: 68, + 0x1837: 68, + 0x1838: 68, + 0x1839: 68, + 0x183a: 68, + 0x183b: 68, + 0x183c: 68, + 0x183d: 68, + 0x183e: 68, + 0x183f: 68, + 0x1840: 68, + 0x1841: 68, + 0x1842: 68, + 0x1843: 68, + 0x1844: 68, + 0x1845: 68, + 0x1846: 68, + 0x1847: 68, + 0x1848: 68, + 0x1849: 68, + 0x184a: 68, + 0x184b: 68, + 0x184c: 68, + 0x184d: 68, + 0x184e: 68, + 0x184f: 68, + 0x1850: 68, + 0x1851: 68, + 0x1852: 68, + 0x1853: 68, + 0x1854: 68, + 0x1855: 68, + 0x1856: 68, + 0x1857: 68, + 0x1858: 68, + 0x1859: 68, + 0x185a: 68, + 0x185b: 68, + 0x185c: 68, + 0x185d: 68, + 0x185e: 68, + 0x185f: 68, + 0x1860: 68, + 0x1861: 68, + 0x1862: 68, + 0x1863: 68, + 0x1864: 68, + 0x1865: 68, + 0x1866: 68, + 0x1867: 68, + 0x1868: 68, + 0x1869: 68, + 0x186a: 68, + 0x186b: 68, + 0x186c: 68, + 0x186d: 68, + 0x186e: 68, + 0x186f: 68, + 0x1870: 68, + 0x1871: 68, + 0x1872: 68, + 0x1873: 68, + 0x1874: 68, + 0x1875: 68, + 0x1876: 68, + 0x1877: 68, + 0x1878: 68, + 0x1880: 85, + 0x1881: 85, + 0x1882: 85, + 0x1883: 85, + 0x1884: 85, + 0x1885: 84, + 0x1886: 84, + 0x1887: 68, + 0x1888: 68, + 0x1889: 68, + 0x188a: 68, + 0x188b: 68, + 0x188c: 68, + 0x188d: 68, + 0x188e: 68, + 0x188f: 68, + 0x1890: 68, + 0x1891: 68, + 0x1892: 68, + 0x1893: 68, + 0x1894: 68, + 0x1895: 68, + 0x1896: 68, + 0x1897: 68, + 0x1898: 68, + 0x1899: 68, + 0x189a: 68, + 0x189b: 68, + 0x189c: 68, + 0x189d: 68, + 0x189e: 68, + 0x189f: 68, + 0x18a0: 68, + 0x18a1: 68, + 0x18a2: 68, + 0x18a3: 68, + 0x18a4: 68, + 0x18a5: 68, + 0x18a6: 68, + 0x18a7: 68, + 0x18a8: 68, + 0x18aa: 68, + 0x200c: 85, + 0x200d: 67, + 0x202f: 85, + 0x2066: 85, + 0x2067: 85, + 0x2068: 85, + 0x2069: 85, + 0xa840: 68, + 0xa841: 68, + 0xa842: 68, + 0xa843: 68, + 0xa844: 68, + 0xa845: 68, + 0xa846: 68, + 0xa847: 68, + 0xa848: 68, + 0xa849: 68, + 0xa84a: 68, + 0xa84b: 68, + 0xa84c: 68, + 0xa84d: 68, + 0xa84e: 68, + 0xa84f: 68, + 0xa850: 68, + 0xa851: 68, + 0xa852: 68, + 0xa853: 68, + 0xa854: 68, + 0xa855: 68, + 0xa856: 68, + 0xa857: 68, + 0xa858: 68, + 0xa859: 68, + 0xa85a: 68, + 0xa85b: 68, + 0xa85c: 68, + 0xa85d: 68, + 0xa85e: 68, + 0xa85f: 68, + 0xa860: 68, + 0xa861: 68, + 0xa862: 68, + 0xa863: 68, + 0xa864: 68, + 0xa865: 68, + 0xa866: 68, + 0xa867: 68, + 0xa868: 68, + 0xa869: 68, + 0xa86a: 68, + 0xa86b: 68, + 0xa86c: 68, + 0xa86d: 68, + 0xa86e: 68, + 0xa86f: 68, + 0xa870: 68, + 0xa871: 68, + 0xa872: 76, + 0xa873: 85, + 0x10ac0: 68, + 0x10ac1: 68, + 0x10ac2: 68, + 0x10ac3: 68, + 0x10ac4: 68, + 0x10ac5: 82, + 0x10ac6: 85, + 0x10ac7: 82, + 0x10ac8: 85, + 0x10ac9: 82, + 0x10aca: 82, + 0x10acb: 85, + 0x10acc: 85, + 0x10acd: 76, + 0x10ace: 82, + 0x10acf: 82, + 0x10ad0: 82, + 0x10ad1: 82, + 0x10ad2: 82, + 0x10ad3: 68, + 0x10ad4: 68, + 0x10ad5: 68, + 0x10ad6: 68, + 0x10ad7: 76, + 0x10ad8: 68, + 0x10ad9: 68, + 0x10ada: 68, + 0x10adb: 68, + 0x10adc: 68, + 0x10add: 82, + 0x10ade: 68, + 0x10adf: 68, + 0x10ae0: 68, + 0x10ae1: 82, + 0x10ae2: 85, + 0x10ae3: 85, + 0x10ae4: 82, + 0x10aeb: 68, + 0x10aec: 68, + 0x10aed: 68, + 0x10aee: 68, + 0x10aef: 82, + 0x10b80: 68, + 0x10b81: 82, + 0x10b82: 68, + 0x10b83: 82, + 0x10b84: 82, + 0x10b85: 82, + 0x10b86: 68, + 0x10b87: 68, + 0x10b88: 68, + 0x10b89: 82, + 0x10b8a: 68, + 0x10b8b: 68, + 0x10b8c: 82, + 0x10b8d: 68, + 0x10b8e: 82, + 0x10b8f: 82, + 0x10b90: 68, + 0x10b91: 82, + 0x10ba9: 82, + 0x10baa: 82, + 0x10bab: 82, + 0x10bac: 82, + 0x10bad: 68, + 0x10bae: 68, + 0x10baf: 85, + 0x10d00: 76, + 0x10d01: 68, + 0x10d02: 68, + 0x10d03: 68, + 0x10d04: 68, + 0x10d05: 68, + 0x10d06: 68, + 0x10d07: 68, + 0x10d08: 68, + 0x10d09: 68, + 0x10d0a: 68, + 0x10d0b: 68, + 0x10d0c: 68, + 0x10d0d: 68, + 0x10d0e: 68, + 0x10d0f: 68, + 0x10d10: 68, + 0x10d11: 68, + 0x10d12: 68, + 0x10d13: 68, + 0x10d14: 68, + 0x10d15: 68, + 0x10d16: 68, + 0x10d17: 68, + 0x10d18: 68, + 0x10d19: 68, + 0x10d1a: 68, + 0x10d1b: 68, + 0x10d1c: 68, + 0x10d1d: 68, + 0x10d1e: 68, + 0x10d1f: 68, + 0x10d20: 68, + 0x10d21: 68, + 0x10d22: 82, + 0x10d23: 68, + 0x10f30: 68, + 0x10f31: 68, + 0x10f32: 68, + 0x10f33: 82, + 0x10f34: 68, + 0x10f35: 68, + 0x10f36: 68, + 0x10f37: 68, + 0x10f38: 68, + 0x10f39: 68, + 0x10f3a: 68, + 0x10f3b: 68, + 0x10f3c: 68, + 0x10f3d: 68, + 0x10f3e: 68, + 0x10f3f: 68, + 0x10f40: 68, + 0x10f41: 68, + 0x10f42: 68, + 0x10f43: 68, + 0x10f44: 68, + 0x10f45: 85, + 0x10f51: 68, + 0x10f52: 68, + 0x10f53: 68, + 0x10f54: 82, + 0x10f70: 68, + 0x10f71: 68, + 0x10f72: 68, + 0x10f73: 68, + 0x10f74: 82, + 0x10f75: 82, + 0x10f76: 68, + 0x10f77: 68, + 0x10f78: 68, + 0x10f79: 68, + 0x10f7a: 68, + 0x10f7b: 68, + 0x10f7c: 68, + 0x10f7d: 68, + 0x10f7e: 68, + 0x10f7f: 68, + 0x10f80: 68, + 0x10f81: 68, + 0x10fb0: 68, + 0x10fb1: 85, + 0x10fb2: 68, + 0x10fb3: 68, + 0x10fb4: 82, + 0x10fb5: 82, + 0x10fb6: 82, + 0x10fb7: 85, + 0x10fb8: 68, + 0x10fb9: 82, + 0x10fba: 82, + 0x10fbb: 68, + 0x10fbc: 68, + 0x10fbd: 82, + 0x10fbe: 68, + 0x10fbf: 68, + 0x10fc0: 85, + 0x10fc1: 68, + 0x10fc2: 82, + 0x10fc3: 82, + 0x10fc4: 68, + 0x10fc5: 85, + 0x10fc6: 85, + 0x10fc7: 85, + 0x10fc8: 85, + 0x10fc9: 82, + 0x10fca: 68, + 0x10fcb: 76, + 0x110bd: 85, + 0x110cd: 85, + 0x1e900: 68, + 0x1e901: 68, + 0x1e902: 68, + 0x1e903: 68, + 0x1e904: 68, + 0x1e905: 68, + 0x1e906: 68, + 0x1e907: 68, + 0x1e908: 68, + 0x1e909: 68, + 0x1e90a: 68, + 0x1e90b: 68, + 0x1e90c: 68, + 0x1e90d: 68, + 0x1e90e: 68, + 0x1e90f: 68, + 0x1e910: 68, + 0x1e911: 68, + 0x1e912: 68, + 0x1e913: 68, + 0x1e914: 68, + 0x1e915: 68, + 0x1e916: 68, + 0x1e917: 68, + 0x1e918: 68, + 0x1e919: 68, + 0x1e91a: 68, + 0x1e91b: 68, + 0x1e91c: 68, + 0x1e91d: 68, + 0x1e91e: 68, + 0x1e91f: 68, + 0x1e920: 68, + 0x1e921: 68, + 0x1e922: 68, + 0x1e923: 68, + 0x1e924: 68, + 0x1e925: 68, + 0x1e926: 68, + 0x1e927: 68, + 0x1e928: 68, + 0x1e929: 68, + 0x1e92a: 68, + 0x1e92b: 68, + 0x1e92c: 68, + 0x1e92d: 68, + 0x1e92e: 68, + 0x1e92f: 68, + 0x1e930: 68, + 0x1e931: 68, + 0x1e932: 68, + 0x1e933: 68, + 0x1e934: 68, + 0x1e935: 68, + 0x1e936: 68, + 0x1e937: 68, + 0x1e938: 68, + 0x1e939: 68, + 0x1e93a: 68, + 0x1e93b: 68, + 0x1e93c: 68, + 0x1e93d: 68, + 0x1e93e: 68, + 0x1e93f: 68, + 0x1e940: 68, + 0x1e941: 68, + 0x1e942: 68, + 0x1e943: 68, + 0x1e94b: 84, +} +codepoint_classes = { + 'PVALID': ( + 0x2d0000002e, + 0x300000003a, + 0x610000007b, + 0xdf000000f7, + 0xf800000100, + 0x10100000102, + 0x10300000104, + 0x10500000106, + 0x10700000108, + 0x1090000010a, + 0x10b0000010c, + 0x10d0000010e, + 0x10f00000110, + 0x11100000112, + 0x11300000114, + 0x11500000116, + 0x11700000118, + 0x1190000011a, + 0x11b0000011c, + 0x11d0000011e, + 0x11f00000120, + 0x12100000122, + 0x12300000124, + 0x12500000126, + 0x12700000128, + 0x1290000012a, + 0x12b0000012c, + 0x12d0000012e, + 0x12f00000130, + 0x13100000132, + 0x13500000136, + 0x13700000139, + 0x13a0000013b, + 0x13c0000013d, + 0x13e0000013f, + 0x14200000143, + 0x14400000145, + 0x14600000147, + 0x14800000149, + 0x14b0000014c, + 0x14d0000014e, + 0x14f00000150, + 0x15100000152, + 0x15300000154, + 0x15500000156, + 0x15700000158, + 0x1590000015a, + 0x15b0000015c, + 0x15d0000015e, + 0x15f00000160, + 0x16100000162, + 0x16300000164, + 0x16500000166, + 0x16700000168, + 0x1690000016a, + 0x16b0000016c, + 0x16d0000016e, + 0x16f00000170, + 0x17100000172, + 0x17300000174, + 0x17500000176, + 0x17700000178, + 0x17a0000017b, + 0x17c0000017d, + 0x17e0000017f, + 0x18000000181, + 0x18300000184, + 0x18500000186, + 0x18800000189, + 0x18c0000018e, + 0x19200000193, + 0x19500000196, + 0x1990000019c, + 0x19e0000019f, + 0x1a1000001a2, + 0x1a3000001a4, + 0x1a5000001a6, + 0x1a8000001a9, + 0x1aa000001ac, + 0x1ad000001ae, + 0x1b0000001b1, + 0x1b4000001b5, + 0x1b6000001b7, + 0x1b9000001bc, + 0x1bd000001c4, + 0x1ce000001cf, + 0x1d0000001d1, + 0x1d2000001d3, + 0x1d4000001d5, + 0x1d6000001d7, + 0x1d8000001d9, + 0x1da000001db, + 0x1dc000001de, + 0x1df000001e0, + 0x1e1000001e2, + 0x1e3000001e4, + 0x1e5000001e6, + 0x1e7000001e8, + 0x1e9000001ea, + 0x1eb000001ec, + 0x1ed000001ee, + 0x1ef000001f1, + 0x1f5000001f6, + 0x1f9000001fa, + 0x1fb000001fc, + 0x1fd000001fe, + 0x1ff00000200, + 0x20100000202, + 0x20300000204, + 0x20500000206, + 0x20700000208, + 0x2090000020a, + 0x20b0000020c, + 0x20d0000020e, + 0x20f00000210, + 0x21100000212, + 0x21300000214, + 0x21500000216, + 0x21700000218, + 0x2190000021a, + 0x21b0000021c, + 0x21d0000021e, + 0x21f00000220, + 0x22100000222, + 0x22300000224, + 0x22500000226, + 0x22700000228, + 0x2290000022a, + 0x22b0000022c, + 0x22d0000022e, + 0x22f00000230, + 0x23100000232, + 0x2330000023a, + 0x23c0000023d, + 0x23f00000241, + 0x24200000243, + 0x24700000248, + 0x2490000024a, + 0x24b0000024c, + 0x24d0000024e, + 0x24f000002b0, + 0x2b9000002c2, + 0x2c6000002d2, + 0x2ec000002ed, + 0x2ee000002ef, + 0x30000000340, + 0x34200000343, + 0x3460000034f, + 0x35000000370, + 0x37100000372, + 0x37300000374, + 0x37700000378, + 0x37b0000037e, + 0x39000000391, + 0x3ac000003cf, + 0x3d7000003d8, + 0x3d9000003da, + 0x3db000003dc, + 0x3dd000003de, + 0x3df000003e0, + 0x3e1000003e2, + 0x3e3000003e4, + 0x3e5000003e6, + 0x3e7000003e8, + 0x3e9000003ea, + 0x3eb000003ec, + 0x3ed000003ee, + 0x3ef000003f0, + 0x3f3000003f4, + 0x3f8000003f9, + 0x3fb000003fd, + 0x43000000460, + 0x46100000462, + 0x46300000464, + 0x46500000466, + 0x46700000468, + 0x4690000046a, + 0x46b0000046c, + 0x46d0000046e, + 0x46f00000470, + 0x47100000472, + 0x47300000474, + 0x47500000476, + 0x47700000478, + 0x4790000047a, + 0x47b0000047c, + 0x47d0000047e, + 0x47f00000480, + 0x48100000482, + 0x48300000488, + 0x48b0000048c, + 0x48d0000048e, + 0x48f00000490, + 0x49100000492, + 0x49300000494, + 0x49500000496, + 0x49700000498, + 0x4990000049a, + 0x49b0000049c, + 0x49d0000049e, + 0x49f000004a0, + 0x4a1000004a2, + 0x4a3000004a4, + 0x4a5000004a6, + 0x4a7000004a8, + 0x4a9000004aa, + 0x4ab000004ac, + 0x4ad000004ae, + 0x4af000004b0, + 0x4b1000004b2, + 0x4b3000004b4, + 0x4b5000004b6, + 0x4b7000004b8, + 0x4b9000004ba, + 0x4bb000004bc, + 0x4bd000004be, + 0x4bf000004c0, + 0x4c2000004c3, + 0x4c4000004c5, + 0x4c6000004c7, + 0x4c8000004c9, + 0x4ca000004cb, + 0x4cc000004cd, + 0x4ce000004d0, + 0x4d1000004d2, + 0x4d3000004d4, + 0x4d5000004d6, + 0x4d7000004d8, + 0x4d9000004da, + 0x4db000004dc, + 0x4dd000004de, + 0x4df000004e0, + 0x4e1000004e2, + 0x4e3000004e4, + 0x4e5000004e6, + 0x4e7000004e8, + 0x4e9000004ea, + 0x4eb000004ec, + 0x4ed000004ee, + 0x4ef000004f0, + 0x4f1000004f2, + 0x4f3000004f4, + 0x4f5000004f6, + 0x4f7000004f8, + 0x4f9000004fa, + 0x4fb000004fc, + 0x4fd000004fe, + 0x4ff00000500, + 0x50100000502, + 0x50300000504, + 0x50500000506, + 0x50700000508, + 0x5090000050a, + 0x50b0000050c, + 0x50d0000050e, + 0x50f00000510, + 0x51100000512, + 0x51300000514, + 0x51500000516, + 0x51700000518, + 0x5190000051a, + 0x51b0000051c, + 0x51d0000051e, + 0x51f00000520, + 0x52100000522, + 0x52300000524, + 0x52500000526, + 0x52700000528, + 0x5290000052a, + 0x52b0000052c, + 0x52d0000052e, + 0x52f00000530, + 0x5590000055a, + 0x56000000587, + 0x58800000589, + 0x591000005be, + 0x5bf000005c0, + 0x5c1000005c3, + 0x5c4000005c6, + 0x5c7000005c8, + 0x5d0000005eb, + 0x5ef000005f3, + 0x6100000061b, + 0x62000000640, + 0x64100000660, + 0x66e00000675, + 0x679000006d4, + 0x6d5000006dd, + 0x6df000006e9, + 0x6ea000006f0, + 0x6fa00000700, + 0x7100000074b, + 0x74d000007b2, + 0x7c0000007f6, + 0x7fd000007fe, + 0x8000000082e, + 0x8400000085c, + 0x8600000086b, + 0x87000000888, + 0x8890000088f, + 0x898000008e2, + 0x8e300000958, + 0x96000000964, + 0x96600000970, + 0x97100000984, + 0x9850000098d, + 0x98f00000991, + 0x993000009a9, + 0x9aa000009b1, + 0x9b2000009b3, + 0x9b6000009ba, + 0x9bc000009c5, + 0x9c7000009c9, + 0x9cb000009cf, + 0x9d7000009d8, + 0x9e0000009e4, + 0x9e6000009f2, + 0x9fc000009fd, + 0x9fe000009ff, + 0xa0100000a04, + 0xa0500000a0b, + 0xa0f00000a11, + 0xa1300000a29, + 0xa2a00000a31, + 0xa3200000a33, + 0xa3500000a36, + 0xa3800000a3a, + 0xa3c00000a3d, + 0xa3e00000a43, + 0xa4700000a49, + 0xa4b00000a4e, + 0xa5100000a52, + 0xa5c00000a5d, + 0xa6600000a76, + 0xa8100000a84, + 0xa8500000a8e, + 0xa8f00000a92, + 0xa9300000aa9, + 0xaaa00000ab1, + 0xab200000ab4, + 0xab500000aba, + 0xabc00000ac6, + 0xac700000aca, + 0xacb00000ace, + 0xad000000ad1, + 0xae000000ae4, + 0xae600000af0, + 0xaf900000b00, + 0xb0100000b04, + 0xb0500000b0d, + 0xb0f00000b11, + 0xb1300000b29, + 0xb2a00000b31, + 0xb3200000b34, + 0xb3500000b3a, + 0xb3c00000b45, + 0xb4700000b49, + 0xb4b00000b4e, + 0xb5500000b58, + 0xb5f00000b64, + 0xb6600000b70, + 0xb7100000b72, + 0xb8200000b84, + 0xb8500000b8b, + 0xb8e00000b91, + 0xb9200000b96, + 0xb9900000b9b, + 0xb9c00000b9d, + 0xb9e00000ba0, + 0xba300000ba5, + 0xba800000bab, + 0xbae00000bba, + 0xbbe00000bc3, + 0xbc600000bc9, + 0xbca00000bce, + 0xbd000000bd1, + 0xbd700000bd8, + 0xbe600000bf0, + 0xc0000000c0d, + 0xc0e00000c11, + 0xc1200000c29, + 0xc2a00000c3a, + 0xc3c00000c45, + 0xc4600000c49, + 0xc4a00000c4e, + 0xc5500000c57, + 0xc5800000c5b, + 0xc5d00000c5e, + 0xc6000000c64, + 0xc6600000c70, + 0xc8000000c84, + 0xc8500000c8d, + 0xc8e00000c91, + 0xc9200000ca9, + 0xcaa00000cb4, + 0xcb500000cba, + 0xcbc00000cc5, + 0xcc600000cc9, + 0xcca00000cce, + 0xcd500000cd7, + 0xcdd00000cdf, + 0xce000000ce4, + 0xce600000cf0, + 0xcf100000cf4, + 0xd0000000d0d, + 0xd0e00000d11, + 0xd1200000d45, + 0xd4600000d49, + 0xd4a00000d4f, + 0xd5400000d58, + 0xd5f00000d64, + 0xd6600000d70, + 0xd7a00000d80, + 0xd8100000d84, + 0xd8500000d97, + 0xd9a00000db2, + 0xdb300000dbc, + 0xdbd00000dbe, + 0xdc000000dc7, + 0xdca00000dcb, + 0xdcf00000dd5, + 0xdd600000dd7, + 0xdd800000de0, + 0xde600000df0, + 0xdf200000df4, + 0xe0100000e33, + 0xe3400000e3b, + 0xe4000000e4f, + 0xe5000000e5a, + 0xe8100000e83, + 0xe8400000e85, + 0xe8600000e8b, + 0xe8c00000ea4, + 0xea500000ea6, + 0xea700000eb3, + 0xeb400000ebe, + 0xec000000ec5, + 0xec600000ec7, + 0xec800000ecf, + 0xed000000eda, + 0xede00000ee0, + 0xf0000000f01, + 0xf0b00000f0c, + 0xf1800000f1a, + 0xf2000000f2a, + 0xf3500000f36, + 0xf3700000f38, + 0xf3900000f3a, + 0xf3e00000f43, + 0xf4400000f48, + 0xf4900000f4d, + 0xf4e00000f52, + 0xf5300000f57, + 0xf5800000f5c, + 0xf5d00000f69, + 0xf6a00000f6d, + 0xf7100000f73, + 0xf7400000f75, + 0xf7a00000f81, + 0xf8200000f85, + 0xf8600000f93, + 0xf9400000f98, + 0xf9900000f9d, + 0xf9e00000fa2, + 0xfa300000fa7, + 0xfa800000fac, + 0xfad00000fb9, + 0xfba00000fbd, + 0xfc600000fc7, + 0x10000000104a, + 0x10500000109e, + 0x10d0000010fb, + 0x10fd00001100, + 0x120000001249, + 0x124a0000124e, + 0x125000001257, + 0x125800001259, + 0x125a0000125e, + 0x126000001289, + 0x128a0000128e, + 0x1290000012b1, + 0x12b2000012b6, + 0x12b8000012bf, + 0x12c0000012c1, + 0x12c2000012c6, + 0x12c8000012d7, + 0x12d800001311, + 0x131200001316, + 0x13180000135b, + 0x135d00001360, + 0x138000001390, + 0x13a0000013f6, + 0x14010000166d, + 0x166f00001680, + 0x16810000169b, + 0x16a0000016eb, + 0x16f1000016f9, + 0x170000001716, + 0x171f00001735, + 0x174000001754, + 0x17600000176d, + 0x176e00001771, + 0x177200001774, + 0x1780000017b4, + 0x17b6000017d4, + 0x17d7000017d8, + 0x17dc000017de, + 0x17e0000017ea, + 0x18100000181a, + 0x182000001879, + 0x1880000018ab, + 0x18b0000018f6, + 0x19000000191f, + 0x19200000192c, + 0x19300000193c, + 0x19460000196e, + 0x197000001975, + 0x1980000019ac, + 0x19b0000019ca, + 0x19d0000019da, + 0x1a0000001a1c, + 0x1a2000001a5f, + 0x1a6000001a7d, + 0x1a7f00001a8a, + 0x1a9000001a9a, + 0x1aa700001aa8, + 0x1ab000001abe, + 0x1abf00001acf, + 0x1b0000001b4d, + 0x1b5000001b5a, + 0x1b6b00001b74, + 0x1b8000001bf4, + 0x1c0000001c38, + 0x1c4000001c4a, + 0x1c4d00001c7e, + 0x1cd000001cd3, + 0x1cd400001cfb, + 0x1d0000001d2c, + 0x1d2f00001d30, + 0x1d3b00001d3c, + 0x1d4e00001d4f, + 0x1d6b00001d78, + 0x1d7900001d9b, + 0x1dc000001e00, + 0x1e0100001e02, + 0x1e0300001e04, + 0x1e0500001e06, + 0x1e0700001e08, + 0x1e0900001e0a, + 0x1e0b00001e0c, + 0x1e0d00001e0e, + 0x1e0f00001e10, + 0x1e1100001e12, + 0x1e1300001e14, + 0x1e1500001e16, + 0x1e1700001e18, + 0x1e1900001e1a, + 0x1e1b00001e1c, + 0x1e1d00001e1e, + 0x1e1f00001e20, + 0x1e2100001e22, + 0x1e2300001e24, + 0x1e2500001e26, + 0x1e2700001e28, + 0x1e2900001e2a, + 0x1e2b00001e2c, + 0x1e2d00001e2e, + 0x1e2f00001e30, + 0x1e3100001e32, + 0x1e3300001e34, + 0x1e3500001e36, + 0x1e3700001e38, + 0x1e3900001e3a, + 0x1e3b00001e3c, + 0x1e3d00001e3e, + 0x1e3f00001e40, + 0x1e4100001e42, + 0x1e4300001e44, + 0x1e4500001e46, + 0x1e4700001e48, + 0x1e4900001e4a, + 0x1e4b00001e4c, + 0x1e4d00001e4e, + 0x1e4f00001e50, + 0x1e5100001e52, + 0x1e5300001e54, + 0x1e5500001e56, + 0x1e5700001e58, + 0x1e5900001e5a, + 0x1e5b00001e5c, + 0x1e5d00001e5e, + 0x1e5f00001e60, + 0x1e6100001e62, + 0x1e6300001e64, + 0x1e6500001e66, + 0x1e6700001e68, + 0x1e6900001e6a, + 0x1e6b00001e6c, + 0x1e6d00001e6e, + 0x1e6f00001e70, + 0x1e7100001e72, + 0x1e7300001e74, + 0x1e7500001e76, + 0x1e7700001e78, + 0x1e7900001e7a, + 0x1e7b00001e7c, + 0x1e7d00001e7e, + 0x1e7f00001e80, + 0x1e8100001e82, + 0x1e8300001e84, + 0x1e8500001e86, + 0x1e8700001e88, + 0x1e8900001e8a, + 0x1e8b00001e8c, + 0x1e8d00001e8e, + 0x1e8f00001e90, + 0x1e9100001e92, + 0x1e9300001e94, + 0x1e9500001e9a, + 0x1e9c00001e9e, + 0x1e9f00001ea0, + 0x1ea100001ea2, + 0x1ea300001ea4, + 0x1ea500001ea6, + 0x1ea700001ea8, + 0x1ea900001eaa, + 0x1eab00001eac, + 0x1ead00001eae, + 0x1eaf00001eb0, + 0x1eb100001eb2, + 0x1eb300001eb4, + 0x1eb500001eb6, + 0x1eb700001eb8, + 0x1eb900001eba, + 0x1ebb00001ebc, + 0x1ebd00001ebe, + 0x1ebf00001ec0, + 0x1ec100001ec2, + 0x1ec300001ec4, + 0x1ec500001ec6, + 0x1ec700001ec8, + 0x1ec900001eca, + 0x1ecb00001ecc, + 0x1ecd00001ece, + 0x1ecf00001ed0, + 0x1ed100001ed2, + 0x1ed300001ed4, + 0x1ed500001ed6, + 0x1ed700001ed8, + 0x1ed900001eda, + 0x1edb00001edc, + 0x1edd00001ede, + 0x1edf00001ee0, + 0x1ee100001ee2, + 0x1ee300001ee4, + 0x1ee500001ee6, + 0x1ee700001ee8, + 0x1ee900001eea, + 0x1eeb00001eec, + 0x1eed00001eee, + 0x1eef00001ef0, + 0x1ef100001ef2, + 0x1ef300001ef4, + 0x1ef500001ef6, + 0x1ef700001ef8, + 0x1ef900001efa, + 0x1efb00001efc, + 0x1efd00001efe, + 0x1eff00001f08, + 0x1f1000001f16, + 0x1f2000001f28, + 0x1f3000001f38, + 0x1f4000001f46, + 0x1f5000001f58, + 0x1f6000001f68, + 0x1f7000001f71, + 0x1f7200001f73, + 0x1f7400001f75, + 0x1f7600001f77, + 0x1f7800001f79, + 0x1f7a00001f7b, + 0x1f7c00001f7d, + 0x1fb000001fb2, + 0x1fb600001fb7, + 0x1fc600001fc7, + 0x1fd000001fd3, + 0x1fd600001fd8, + 0x1fe000001fe3, + 0x1fe400001fe8, + 0x1ff600001ff7, + 0x214e0000214f, + 0x218400002185, + 0x2c3000002c60, + 0x2c6100002c62, + 0x2c6500002c67, + 0x2c6800002c69, + 0x2c6a00002c6b, + 0x2c6c00002c6d, + 0x2c7100002c72, + 0x2c7300002c75, + 0x2c7600002c7c, + 0x2c8100002c82, + 0x2c8300002c84, + 0x2c8500002c86, + 0x2c8700002c88, + 0x2c8900002c8a, + 0x2c8b00002c8c, + 0x2c8d00002c8e, + 0x2c8f00002c90, + 0x2c9100002c92, + 0x2c9300002c94, + 0x2c9500002c96, + 0x2c9700002c98, + 0x2c9900002c9a, + 0x2c9b00002c9c, + 0x2c9d00002c9e, + 0x2c9f00002ca0, + 0x2ca100002ca2, + 0x2ca300002ca4, + 0x2ca500002ca6, + 0x2ca700002ca8, + 0x2ca900002caa, + 0x2cab00002cac, + 0x2cad00002cae, + 0x2caf00002cb0, + 0x2cb100002cb2, + 0x2cb300002cb4, + 0x2cb500002cb6, + 0x2cb700002cb8, + 0x2cb900002cba, + 0x2cbb00002cbc, + 0x2cbd00002cbe, + 0x2cbf00002cc0, + 0x2cc100002cc2, + 0x2cc300002cc4, + 0x2cc500002cc6, + 0x2cc700002cc8, + 0x2cc900002cca, + 0x2ccb00002ccc, + 0x2ccd00002cce, + 0x2ccf00002cd0, + 0x2cd100002cd2, + 0x2cd300002cd4, + 0x2cd500002cd6, + 0x2cd700002cd8, + 0x2cd900002cda, + 0x2cdb00002cdc, + 0x2cdd00002cde, + 0x2cdf00002ce0, + 0x2ce100002ce2, + 0x2ce300002ce5, + 0x2cec00002ced, + 0x2cee00002cf2, + 0x2cf300002cf4, + 0x2d0000002d26, + 0x2d2700002d28, + 0x2d2d00002d2e, + 0x2d3000002d68, + 0x2d7f00002d97, + 0x2da000002da7, + 0x2da800002daf, + 0x2db000002db7, + 0x2db800002dbf, + 0x2dc000002dc7, + 0x2dc800002dcf, + 0x2dd000002dd7, + 0x2dd800002ddf, + 0x2de000002e00, + 0x2e2f00002e30, + 0x300500003008, + 0x302a0000302e, + 0x303c0000303d, + 0x304100003097, + 0x30990000309b, + 0x309d0000309f, + 0x30a1000030fb, + 0x30fc000030ff, + 0x310500003130, + 0x31a0000031c0, + 0x31f000003200, + 0x340000004dc0, + 0x4e000000a48d, + 0xa4d00000a4fe, + 0xa5000000a60d, + 0xa6100000a62c, + 0xa6410000a642, + 0xa6430000a644, + 0xa6450000a646, + 0xa6470000a648, + 0xa6490000a64a, + 0xa64b0000a64c, + 0xa64d0000a64e, + 0xa64f0000a650, + 0xa6510000a652, + 0xa6530000a654, + 0xa6550000a656, + 0xa6570000a658, + 0xa6590000a65a, + 0xa65b0000a65c, + 0xa65d0000a65e, + 0xa65f0000a660, + 0xa6610000a662, + 0xa6630000a664, + 0xa6650000a666, + 0xa6670000a668, + 0xa6690000a66a, + 0xa66b0000a66c, + 0xa66d0000a670, + 0xa6740000a67e, + 0xa67f0000a680, + 0xa6810000a682, + 0xa6830000a684, + 0xa6850000a686, + 0xa6870000a688, + 0xa6890000a68a, + 0xa68b0000a68c, + 0xa68d0000a68e, + 0xa68f0000a690, + 0xa6910000a692, + 0xa6930000a694, + 0xa6950000a696, + 0xa6970000a698, + 0xa6990000a69a, + 0xa69b0000a69c, + 0xa69e0000a6e6, + 0xa6f00000a6f2, + 0xa7170000a720, + 0xa7230000a724, + 0xa7250000a726, + 0xa7270000a728, + 0xa7290000a72a, + 0xa72b0000a72c, + 0xa72d0000a72e, + 0xa72f0000a732, + 0xa7330000a734, + 0xa7350000a736, + 0xa7370000a738, + 0xa7390000a73a, + 0xa73b0000a73c, + 0xa73d0000a73e, + 0xa73f0000a740, + 0xa7410000a742, + 0xa7430000a744, + 0xa7450000a746, + 0xa7470000a748, + 0xa7490000a74a, + 0xa74b0000a74c, + 0xa74d0000a74e, + 0xa74f0000a750, + 0xa7510000a752, + 0xa7530000a754, + 0xa7550000a756, + 0xa7570000a758, + 0xa7590000a75a, + 0xa75b0000a75c, + 0xa75d0000a75e, + 0xa75f0000a760, + 0xa7610000a762, + 0xa7630000a764, + 0xa7650000a766, + 0xa7670000a768, + 0xa7690000a76a, + 0xa76b0000a76c, + 0xa76d0000a76e, + 0xa76f0000a770, + 0xa7710000a779, + 0xa77a0000a77b, + 0xa77c0000a77d, + 0xa77f0000a780, + 0xa7810000a782, + 0xa7830000a784, + 0xa7850000a786, + 0xa7870000a789, + 0xa78c0000a78d, + 0xa78e0000a790, + 0xa7910000a792, + 0xa7930000a796, + 0xa7970000a798, + 0xa7990000a79a, + 0xa79b0000a79c, + 0xa79d0000a79e, + 0xa79f0000a7a0, + 0xa7a10000a7a2, + 0xa7a30000a7a4, + 0xa7a50000a7a6, + 0xa7a70000a7a8, + 0xa7a90000a7aa, + 0xa7af0000a7b0, + 0xa7b50000a7b6, + 0xa7b70000a7b8, + 0xa7b90000a7ba, + 0xa7bb0000a7bc, + 0xa7bd0000a7be, + 0xa7bf0000a7c0, + 0xa7c10000a7c2, + 0xa7c30000a7c4, + 0xa7c80000a7c9, + 0xa7ca0000a7cb, + 0xa7d10000a7d2, + 0xa7d30000a7d4, + 0xa7d50000a7d6, + 0xa7d70000a7d8, + 0xa7d90000a7da, + 0xa7f20000a7f5, + 0xa7f60000a7f8, + 0xa7fa0000a828, + 0xa82c0000a82d, + 0xa8400000a874, + 0xa8800000a8c6, + 0xa8d00000a8da, + 0xa8e00000a8f8, + 0xa8fb0000a8fc, + 0xa8fd0000a92e, + 0xa9300000a954, + 0xa9800000a9c1, + 0xa9cf0000a9da, + 0xa9e00000a9ff, + 0xaa000000aa37, + 0xaa400000aa4e, + 0xaa500000aa5a, + 0xaa600000aa77, + 0xaa7a0000aac3, + 0xaadb0000aade, + 0xaae00000aaf0, + 0xaaf20000aaf7, + 0xab010000ab07, + 0xab090000ab0f, + 0xab110000ab17, + 0xab200000ab27, + 0xab280000ab2f, + 0xab300000ab5b, + 0xab600000ab69, + 0xabc00000abeb, + 0xabec0000abee, + 0xabf00000abfa, + 0xac000000d7a4, + 0xfa0e0000fa10, + 0xfa110000fa12, + 0xfa130000fa15, + 0xfa1f0000fa20, + 0xfa210000fa22, + 0xfa230000fa25, + 0xfa270000fa2a, + 0xfb1e0000fb1f, + 0xfe200000fe30, + 0xfe730000fe74, + 0x100000001000c, + 0x1000d00010027, + 0x100280001003b, + 0x1003c0001003e, + 0x1003f0001004e, + 0x100500001005e, + 0x10080000100fb, + 0x101fd000101fe, + 0x102800001029d, + 0x102a0000102d1, + 0x102e0000102e1, + 0x1030000010320, + 0x1032d00010341, + 0x103420001034a, + 0x103500001037b, + 0x103800001039e, + 0x103a0000103c4, + 0x103c8000103d0, + 0x104280001049e, + 0x104a0000104aa, + 0x104d8000104fc, + 0x1050000010528, + 0x1053000010564, + 0x10597000105a2, + 0x105a3000105b2, + 0x105b3000105ba, + 0x105bb000105bd, + 0x1060000010737, + 0x1074000010756, + 0x1076000010768, + 0x1078000010786, + 0x10787000107b1, + 0x107b2000107bb, + 0x1080000010806, + 0x1080800010809, + 0x1080a00010836, + 0x1083700010839, + 0x1083c0001083d, + 0x1083f00010856, + 0x1086000010877, + 0x108800001089f, + 0x108e0000108f3, + 0x108f4000108f6, + 0x1090000010916, + 0x109200001093a, + 0x10980000109b8, + 0x109be000109c0, + 0x10a0000010a04, + 0x10a0500010a07, + 0x10a0c00010a14, + 0x10a1500010a18, + 0x10a1900010a36, + 0x10a3800010a3b, + 0x10a3f00010a40, + 0x10a6000010a7d, + 0x10a8000010a9d, + 0x10ac000010ac8, + 0x10ac900010ae7, + 0x10b0000010b36, + 0x10b4000010b56, + 0x10b6000010b73, + 0x10b8000010b92, + 0x10c0000010c49, + 0x10cc000010cf3, + 0x10d0000010d28, + 0x10d3000010d3a, + 0x10e8000010eaa, + 0x10eab00010ead, + 0x10eb000010eb2, + 0x10efd00010f1d, + 0x10f2700010f28, + 0x10f3000010f51, + 0x10f7000010f86, + 0x10fb000010fc5, + 0x10fe000010ff7, + 0x1100000011047, + 0x1106600011076, + 0x1107f000110bb, + 0x110c2000110c3, + 0x110d0000110e9, + 0x110f0000110fa, + 0x1110000011135, + 0x1113600011140, + 0x1114400011148, + 0x1115000011174, + 0x1117600011177, + 0x11180000111c5, + 0x111c9000111cd, + 0x111ce000111db, + 0x111dc000111dd, + 0x1120000011212, + 0x1121300011238, + 0x1123e00011242, + 0x1128000011287, + 0x1128800011289, + 0x1128a0001128e, + 0x1128f0001129e, + 0x1129f000112a9, + 0x112b0000112eb, + 0x112f0000112fa, + 0x1130000011304, + 0x113050001130d, + 0x1130f00011311, + 0x1131300011329, + 0x1132a00011331, + 0x1133200011334, + 0x113350001133a, + 0x1133b00011345, + 0x1134700011349, + 0x1134b0001134e, + 0x1135000011351, + 0x1135700011358, + 0x1135d00011364, + 0x113660001136d, + 0x1137000011375, + 0x114000001144b, + 0x114500001145a, + 0x1145e00011462, + 0x11480000114c6, + 0x114c7000114c8, + 0x114d0000114da, + 0x11580000115b6, + 0x115b8000115c1, + 0x115d8000115de, + 0x1160000011641, + 0x1164400011645, + 0x116500001165a, + 0x11680000116b9, + 0x116c0000116ca, + 0x117000001171b, + 0x1171d0001172c, + 0x117300001173a, + 0x1174000011747, + 0x118000001183b, + 0x118c0000118ea, + 0x118ff00011907, + 0x119090001190a, + 0x1190c00011914, + 0x1191500011917, + 0x1191800011936, + 0x1193700011939, + 0x1193b00011944, + 0x119500001195a, + 0x119a0000119a8, + 0x119aa000119d8, + 0x119da000119e2, + 0x119e3000119e5, + 0x11a0000011a3f, + 0x11a4700011a48, + 0x11a5000011a9a, + 0x11a9d00011a9e, + 0x11ab000011af9, + 0x11c0000011c09, + 0x11c0a00011c37, + 0x11c3800011c41, + 0x11c5000011c5a, + 0x11c7200011c90, + 0x11c9200011ca8, + 0x11ca900011cb7, + 0x11d0000011d07, + 0x11d0800011d0a, + 0x11d0b00011d37, + 0x11d3a00011d3b, + 0x11d3c00011d3e, + 0x11d3f00011d48, + 0x11d5000011d5a, + 0x11d6000011d66, + 0x11d6700011d69, + 0x11d6a00011d8f, + 0x11d9000011d92, + 0x11d9300011d99, + 0x11da000011daa, + 0x11ee000011ef7, + 0x11f0000011f11, + 0x11f1200011f3b, + 0x11f3e00011f43, + 0x11f5000011f5a, + 0x11fb000011fb1, + 0x120000001239a, + 0x1248000012544, + 0x12f9000012ff1, + 0x1300000013430, + 0x1344000013456, + 0x1440000014647, + 0x1680000016a39, + 0x16a4000016a5f, + 0x16a6000016a6a, + 0x16a7000016abf, + 0x16ac000016aca, + 0x16ad000016aee, + 0x16af000016af5, + 0x16b0000016b37, + 0x16b4000016b44, + 0x16b5000016b5a, + 0x16b6300016b78, + 0x16b7d00016b90, + 0x16e6000016e80, + 0x16f0000016f4b, + 0x16f4f00016f88, + 0x16f8f00016fa0, + 0x16fe000016fe2, + 0x16fe300016fe5, + 0x16ff000016ff2, + 0x17000000187f8, + 0x1880000018cd6, + 0x18d0000018d09, + 0x1aff00001aff4, + 0x1aff50001affc, + 0x1affd0001afff, + 0x1b0000001b123, + 0x1b1320001b133, + 0x1b1500001b153, + 0x1b1550001b156, + 0x1b1640001b168, + 0x1b1700001b2fc, + 0x1bc000001bc6b, + 0x1bc700001bc7d, + 0x1bc800001bc89, + 0x1bc900001bc9a, + 0x1bc9d0001bc9f, + 0x1cf000001cf2e, + 0x1cf300001cf47, + 0x1da000001da37, + 0x1da3b0001da6d, + 0x1da750001da76, + 0x1da840001da85, + 0x1da9b0001daa0, + 0x1daa10001dab0, + 0x1df000001df1f, + 0x1df250001df2b, + 0x1e0000001e007, + 0x1e0080001e019, + 0x1e01b0001e022, + 0x1e0230001e025, + 0x1e0260001e02b, + 0x1e0300001e06e, + 0x1e08f0001e090, + 0x1e1000001e12d, + 0x1e1300001e13e, + 0x1e1400001e14a, + 0x1e14e0001e14f, + 0x1e2900001e2af, + 0x1e2c00001e2fa, + 0x1e4d00001e4fa, + 0x1e7e00001e7e7, + 0x1e7e80001e7ec, + 0x1e7ed0001e7ef, + 0x1e7f00001e7ff, + 0x1e8000001e8c5, + 0x1e8d00001e8d7, + 0x1e9220001e94c, + 0x1e9500001e95a, + 0x200000002a6e0, + 0x2a7000002b73a, + 0x2b7400002b81e, + 0x2b8200002cea2, + 0x2ceb00002ebe1, + 0x300000003134b, + 0x31350000323b0, + ), + 'CONTEXTJ': ( + 0x200c0000200e, + ), + 'CONTEXTO': ( + 0xb7000000b8, + 0x37500000376, + 0x5f3000005f5, + 0x6600000066a, + 0x6f0000006fa, + 0x30fb000030fc, + ), +} diff --git a/virt/lib/python3.9/site-packages/pip/_vendor/idna/uts46data 3.py b/virt/lib/python3.9/site-packages/pip/_vendor/idna/uts46data 3.py new file mode 100644 index 00000000..261bed13 --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_vendor/idna/uts46data 3.py @@ -0,0 +1,8600 @@ +# This file is automatically generated by tools/idna-data +# vim: set fileencoding=utf-8 : + +from typing import List, Tuple, Union + + +"""IDNA Mapping Table from UTS46.""" + + +__version__ = '15.0.0' +def _seg_0() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x0, '3'), + (0x1, '3'), + (0x2, '3'), + (0x3, '3'), + (0x4, '3'), + (0x5, '3'), + (0x6, '3'), + (0x7, '3'), + (0x8, '3'), + (0x9, '3'), + (0xA, '3'), + (0xB, '3'), + (0xC, '3'), + (0xD, '3'), + (0xE, '3'), + (0xF, '3'), + (0x10, '3'), + (0x11, '3'), + (0x12, '3'), + (0x13, '3'), + (0x14, '3'), + (0x15, '3'), + (0x16, '3'), + (0x17, '3'), + (0x18, '3'), + (0x19, '3'), + (0x1A, '3'), + (0x1B, '3'), + (0x1C, '3'), + (0x1D, '3'), + (0x1E, '3'), + (0x1F, '3'), + (0x20, '3'), + (0x21, '3'), + (0x22, '3'), + (0x23, '3'), + (0x24, '3'), + (0x25, '3'), + (0x26, '3'), + (0x27, '3'), + (0x28, '3'), + (0x29, '3'), + (0x2A, '3'), + (0x2B, '3'), + (0x2C, '3'), + (0x2D, 'V'), + (0x2E, 'V'), + (0x2F, '3'), + (0x30, 'V'), + (0x31, 'V'), + (0x32, 'V'), + (0x33, 'V'), + (0x34, 'V'), + (0x35, 'V'), + (0x36, 'V'), + (0x37, 'V'), + (0x38, 'V'), + (0x39, 'V'), + (0x3A, '3'), + (0x3B, '3'), + (0x3C, '3'), + (0x3D, '3'), + (0x3E, '3'), + (0x3F, '3'), + (0x40, '3'), + (0x41, 'M', 'a'), + (0x42, 'M', 'b'), + (0x43, 'M', 'c'), + (0x44, 'M', 'd'), + (0x45, 'M', 'e'), + (0x46, 'M', 'f'), + (0x47, 'M', 'g'), + (0x48, 'M', 'h'), + (0x49, 'M', 'i'), + (0x4A, 'M', 'j'), + (0x4B, 'M', 'k'), + (0x4C, 'M', 'l'), + (0x4D, 'M', 'm'), + (0x4E, 'M', 'n'), + (0x4F, 'M', 'o'), + (0x50, 'M', 'p'), + (0x51, 'M', 'q'), + (0x52, 'M', 'r'), + (0x53, 'M', 's'), + (0x54, 'M', 't'), + (0x55, 'M', 'u'), + (0x56, 'M', 'v'), + (0x57, 'M', 'w'), + (0x58, 'M', 'x'), + (0x59, 'M', 'y'), + (0x5A, 'M', 'z'), + (0x5B, '3'), + (0x5C, '3'), + (0x5D, '3'), + (0x5E, '3'), + (0x5F, '3'), + (0x60, '3'), + (0x61, 'V'), + (0x62, 'V'), + (0x63, 'V'), + ] + +def _seg_1() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x64, 'V'), + (0x65, 'V'), + (0x66, 'V'), + (0x67, 'V'), + (0x68, 'V'), + (0x69, 'V'), + (0x6A, 'V'), + (0x6B, 'V'), + (0x6C, 'V'), + (0x6D, 'V'), + (0x6E, 'V'), + (0x6F, 'V'), + (0x70, 'V'), + (0x71, 'V'), + (0x72, 'V'), + (0x73, 'V'), + (0x74, 'V'), + (0x75, 'V'), + (0x76, 'V'), + (0x77, 'V'), + (0x78, 'V'), + (0x79, 'V'), + (0x7A, 'V'), + (0x7B, '3'), + (0x7C, '3'), + (0x7D, '3'), + (0x7E, '3'), + (0x7F, '3'), + (0x80, 'X'), + (0x81, 'X'), + (0x82, 'X'), + (0x83, 'X'), + (0x84, 'X'), + (0x85, 'X'), + (0x86, 'X'), + (0x87, 'X'), + (0x88, 'X'), + (0x89, 'X'), + (0x8A, 'X'), + (0x8B, 'X'), + (0x8C, 'X'), + (0x8D, 'X'), + (0x8E, 'X'), + (0x8F, 'X'), + (0x90, 'X'), + (0x91, 'X'), + (0x92, 'X'), + (0x93, 'X'), + (0x94, 'X'), + (0x95, 'X'), + (0x96, 'X'), + (0x97, 'X'), + (0x98, 'X'), + (0x99, 'X'), + (0x9A, 'X'), + (0x9B, 'X'), + (0x9C, 'X'), + (0x9D, 'X'), + (0x9E, 'X'), + (0x9F, 'X'), + (0xA0, '3', ' '), + (0xA1, 'V'), + (0xA2, 'V'), + (0xA3, 'V'), + (0xA4, 'V'), + (0xA5, 'V'), + (0xA6, 'V'), + (0xA7, 'V'), + (0xA8, '3', ' ̈'), + (0xA9, 'V'), + (0xAA, 'M', 'a'), + (0xAB, 'V'), + (0xAC, 'V'), + (0xAD, 'I'), + (0xAE, 'V'), + (0xAF, '3', ' ̄'), + (0xB0, 'V'), + (0xB1, 'V'), + (0xB2, 'M', '2'), + (0xB3, 'M', '3'), + (0xB4, '3', ' ́'), + (0xB5, 'M', 'μ'), + (0xB6, 'V'), + (0xB7, 'V'), + (0xB8, '3', ' ̧'), + (0xB9, 'M', '1'), + (0xBA, 'M', 'o'), + (0xBB, 'V'), + (0xBC, 'M', '1⁄4'), + (0xBD, 'M', '1⁄2'), + (0xBE, 'M', '3⁄4'), + (0xBF, 'V'), + (0xC0, 'M', 'à'), + (0xC1, 'M', 'á'), + (0xC2, 'M', 'â'), + (0xC3, 'M', 'ã'), + (0xC4, 'M', 'ä'), + (0xC5, 'M', 'å'), + (0xC6, 'M', 'æ'), + (0xC7, 'M', 'ç'), + ] + +def _seg_2() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xC8, 'M', 'è'), + (0xC9, 'M', 'é'), + (0xCA, 'M', 'ê'), + (0xCB, 'M', 'ë'), + (0xCC, 'M', 'ì'), + (0xCD, 'M', 'í'), + (0xCE, 'M', 'î'), + (0xCF, 'M', 'ï'), + (0xD0, 'M', 'ð'), + (0xD1, 'M', 'ñ'), + (0xD2, 'M', 'ò'), + (0xD3, 'M', 'ó'), + (0xD4, 'M', 'ô'), + (0xD5, 'M', 'õ'), + (0xD6, 'M', 'ö'), + (0xD7, 'V'), + (0xD8, 'M', 'ø'), + (0xD9, 'M', 'ù'), + (0xDA, 'M', 'ú'), + (0xDB, 'M', 'û'), + (0xDC, 'M', 'ü'), + (0xDD, 'M', 'ý'), + (0xDE, 'M', 'þ'), + (0xDF, 'D', 'ss'), + (0xE0, 'V'), + (0xE1, 'V'), + (0xE2, 'V'), + (0xE3, 'V'), + (0xE4, 'V'), + (0xE5, 'V'), + (0xE6, 'V'), + (0xE7, 'V'), + (0xE8, 'V'), + (0xE9, 'V'), + (0xEA, 'V'), + (0xEB, 'V'), + (0xEC, 'V'), + (0xED, 'V'), + (0xEE, 'V'), + (0xEF, 'V'), + (0xF0, 'V'), + (0xF1, 'V'), + (0xF2, 'V'), + (0xF3, 'V'), + (0xF4, 'V'), + (0xF5, 'V'), + (0xF6, 'V'), + (0xF7, 'V'), + (0xF8, 'V'), + (0xF9, 'V'), + (0xFA, 'V'), + (0xFB, 'V'), + (0xFC, 'V'), + (0xFD, 'V'), + (0xFE, 'V'), + (0xFF, 'V'), + (0x100, 'M', 'ā'), + (0x101, 'V'), + (0x102, 'M', 'ă'), + (0x103, 'V'), + (0x104, 'M', 'ą'), + (0x105, 'V'), + (0x106, 'M', 'ć'), + (0x107, 'V'), + (0x108, 'M', 'ĉ'), + (0x109, 'V'), + (0x10A, 'M', 'ċ'), + (0x10B, 'V'), + (0x10C, 'M', 'č'), + (0x10D, 'V'), + (0x10E, 'M', 'ď'), + (0x10F, 'V'), + (0x110, 'M', 'đ'), + (0x111, 'V'), + (0x112, 'M', 'ē'), + (0x113, 'V'), + (0x114, 'M', 'ĕ'), + (0x115, 'V'), + (0x116, 'M', 'ė'), + (0x117, 'V'), + (0x118, 'M', 'ę'), + (0x119, 'V'), + (0x11A, 'M', 'ě'), + (0x11B, 'V'), + (0x11C, 'M', 'ĝ'), + (0x11D, 'V'), + (0x11E, 'M', 'ğ'), + (0x11F, 'V'), + (0x120, 'M', 'ġ'), + (0x121, 'V'), + (0x122, 'M', 'ģ'), + (0x123, 'V'), + (0x124, 'M', 'ĥ'), + (0x125, 'V'), + (0x126, 'M', 'ħ'), + (0x127, 'V'), + (0x128, 'M', 'ĩ'), + (0x129, 'V'), + (0x12A, 'M', 'ī'), + (0x12B, 'V'), + ] + +def _seg_3() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x12C, 'M', 'ĭ'), + (0x12D, 'V'), + (0x12E, 'M', 'į'), + (0x12F, 'V'), + (0x130, 'M', 'i̇'), + (0x131, 'V'), + (0x132, 'M', 'ij'), + (0x134, 'M', 'ĵ'), + (0x135, 'V'), + (0x136, 'M', 'ķ'), + (0x137, 'V'), + (0x139, 'M', 'ĺ'), + (0x13A, 'V'), + (0x13B, 'M', 'ļ'), + (0x13C, 'V'), + (0x13D, 'M', 'ľ'), + (0x13E, 'V'), + (0x13F, 'M', 'l·'), + (0x141, 'M', 'ł'), + (0x142, 'V'), + (0x143, 'M', 'ń'), + (0x144, 'V'), + (0x145, 'M', 'ņ'), + (0x146, 'V'), + (0x147, 'M', 'ň'), + (0x148, 'V'), + (0x149, 'M', 'ʼn'), + (0x14A, 'M', 'ŋ'), + (0x14B, 'V'), + (0x14C, 'M', 'ō'), + (0x14D, 'V'), + (0x14E, 'M', 'ŏ'), + (0x14F, 'V'), + (0x150, 'M', 'ő'), + (0x151, 'V'), + (0x152, 'M', 'œ'), + (0x153, 'V'), + (0x154, 'M', 'ŕ'), + (0x155, 'V'), + (0x156, 'M', 'ŗ'), + (0x157, 'V'), + (0x158, 'M', 'ř'), + (0x159, 'V'), + (0x15A, 'M', 'ś'), + (0x15B, 'V'), + (0x15C, 'M', 'ŝ'), + (0x15D, 'V'), + (0x15E, 'M', 'ş'), + (0x15F, 'V'), + (0x160, 'M', 'š'), + (0x161, 'V'), + (0x162, 'M', 'ţ'), + (0x163, 'V'), + (0x164, 'M', 'ť'), + (0x165, 'V'), + (0x166, 'M', 'ŧ'), + (0x167, 'V'), + (0x168, 'M', 'ũ'), + (0x169, 'V'), + (0x16A, 'M', 'ū'), + (0x16B, 'V'), + (0x16C, 'M', 'ŭ'), + (0x16D, 'V'), + (0x16E, 'M', 'ů'), + (0x16F, 'V'), + (0x170, 'M', 'ű'), + (0x171, 'V'), + (0x172, 'M', 'ų'), + (0x173, 'V'), + (0x174, 'M', 'ŵ'), + (0x175, 'V'), + (0x176, 'M', 'ŷ'), + (0x177, 'V'), + (0x178, 'M', 'ÿ'), + (0x179, 'M', 'ź'), + (0x17A, 'V'), + (0x17B, 'M', 'ż'), + (0x17C, 'V'), + (0x17D, 'M', 'ž'), + (0x17E, 'V'), + (0x17F, 'M', 's'), + (0x180, 'V'), + (0x181, 'M', 'ɓ'), + (0x182, 'M', 'ƃ'), + (0x183, 'V'), + (0x184, 'M', 'ƅ'), + (0x185, 'V'), + (0x186, 'M', 'ɔ'), + (0x187, 'M', 'ƈ'), + (0x188, 'V'), + (0x189, 'M', 'ɖ'), + (0x18A, 'M', 'ɗ'), + (0x18B, 'M', 'ƌ'), + (0x18C, 'V'), + (0x18E, 'M', 'ǝ'), + (0x18F, 'M', 'ə'), + (0x190, 'M', 'ɛ'), + (0x191, 'M', 'ƒ'), + (0x192, 'V'), + (0x193, 'M', 'ɠ'), + ] + +def _seg_4() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x194, 'M', 'ɣ'), + (0x195, 'V'), + (0x196, 'M', 'ɩ'), + (0x197, 'M', 'ɨ'), + (0x198, 'M', 'ƙ'), + (0x199, 'V'), + (0x19C, 'M', 'ɯ'), + (0x19D, 'M', 'ɲ'), + (0x19E, 'V'), + (0x19F, 'M', 'ɵ'), + (0x1A0, 'M', 'ơ'), + (0x1A1, 'V'), + (0x1A2, 'M', 'ƣ'), + (0x1A3, 'V'), + (0x1A4, 'M', 'ƥ'), + (0x1A5, 'V'), + (0x1A6, 'M', 'ʀ'), + (0x1A7, 'M', 'ƨ'), + (0x1A8, 'V'), + (0x1A9, 'M', 'ʃ'), + (0x1AA, 'V'), + (0x1AC, 'M', 'ƭ'), + (0x1AD, 'V'), + (0x1AE, 'M', 'ʈ'), + (0x1AF, 'M', 'ư'), + (0x1B0, 'V'), + (0x1B1, 'M', 'ʊ'), + (0x1B2, 'M', 'ʋ'), + (0x1B3, 'M', 'ƴ'), + (0x1B4, 'V'), + (0x1B5, 'M', 'ƶ'), + (0x1B6, 'V'), + (0x1B7, 'M', 'ʒ'), + (0x1B8, 'M', 'ƹ'), + (0x1B9, 'V'), + (0x1BC, 'M', 'ƽ'), + (0x1BD, 'V'), + (0x1C4, 'M', 'dž'), + (0x1C7, 'M', 'lj'), + (0x1CA, 'M', 'nj'), + (0x1CD, 'M', 'ǎ'), + (0x1CE, 'V'), + (0x1CF, 'M', 'ǐ'), + (0x1D0, 'V'), + (0x1D1, 'M', 'ǒ'), + (0x1D2, 'V'), + (0x1D3, 'M', 'ǔ'), + (0x1D4, 'V'), + (0x1D5, 'M', 'ǖ'), + (0x1D6, 'V'), + (0x1D7, 'M', 'ǘ'), + (0x1D8, 'V'), + (0x1D9, 'M', 'ǚ'), + (0x1DA, 'V'), + (0x1DB, 'M', 'ǜ'), + (0x1DC, 'V'), + (0x1DE, 'M', 'ǟ'), + (0x1DF, 'V'), + (0x1E0, 'M', 'ǡ'), + (0x1E1, 'V'), + (0x1E2, 'M', 'ǣ'), + (0x1E3, 'V'), + (0x1E4, 'M', 'ǥ'), + (0x1E5, 'V'), + (0x1E6, 'M', 'ǧ'), + (0x1E7, 'V'), + (0x1E8, 'M', 'ǩ'), + (0x1E9, 'V'), + (0x1EA, 'M', 'ǫ'), + (0x1EB, 'V'), + (0x1EC, 'M', 'ǭ'), + (0x1ED, 'V'), + (0x1EE, 'M', 'ǯ'), + (0x1EF, 'V'), + (0x1F1, 'M', 'dz'), + (0x1F4, 'M', 'ǵ'), + (0x1F5, 'V'), + (0x1F6, 'M', 'ƕ'), + (0x1F7, 'M', 'ƿ'), + (0x1F8, 'M', 'ǹ'), + (0x1F9, 'V'), + (0x1FA, 'M', 'ǻ'), + (0x1FB, 'V'), + (0x1FC, 'M', 'ǽ'), + (0x1FD, 'V'), + (0x1FE, 'M', 'ǿ'), + (0x1FF, 'V'), + (0x200, 'M', 'ȁ'), + (0x201, 'V'), + (0x202, 'M', 'ȃ'), + (0x203, 'V'), + (0x204, 'M', 'ȅ'), + (0x205, 'V'), + (0x206, 'M', 'ȇ'), + (0x207, 'V'), + (0x208, 'M', 'ȉ'), + (0x209, 'V'), + (0x20A, 'M', 'ȋ'), + (0x20B, 'V'), + (0x20C, 'M', 'ȍ'), + ] + +def _seg_5() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x20D, 'V'), + (0x20E, 'M', 'ȏ'), + (0x20F, 'V'), + (0x210, 'M', 'ȑ'), + (0x211, 'V'), + (0x212, 'M', 'ȓ'), + (0x213, 'V'), + (0x214, 'M', 'ȕ'), + (0x215, 'V'), + (0x216, 'M', 'ȗ'), + (0x217, 'V'), + (0x218, 'M', 'ș'), + (0x219, 'V'), + (0x21A, 'M', 'ț'), + (0x21B, 'V'), + (0x21C, 'M', 'ȝ'), + (0x21D, 'V'), + (0x21E, 'M', 'ȟ'), + (0x21F, 'V'), + (0x220, 'M', 'ƞ'), + (0x221, 'V'), + (0x222, 'M', 'ȣ'), + (0x223, 'V'), + (0x224, 'M', 'ȥ'), + (0x225, 'V'), + (0x226, 'M', 'ȧ'), + (0x227, 'V'), + (0x228, 'M', 'ȩ'), + (0x229, 'V'), + (0x22A, 'M', 'ȫ'), + (0x22B, 'V'), + (0x22C, 'M', 'ȭ'), + (0x22D, 'V'), + (0x22E, 'M', 'ȯ'), + (0x22F, 'V'), + (0x230, 'M', 'ȱ'), + (0x231, 'V'), + (0x232, 'M', 'ȳ'), + (0x233, 'V'), + (0x23A, 'M', 'ⱥ'), + (0x23B, 'M', 'ȼ'), + (0x23C, 'V'), + (0x23D, 'M', 'ƚ'), + (0x23E, 'M', 'ⱦ'), + (0x23F, 'V'), + (0x241, 'M', 'ɂ'), + (0x242, 'V'), + (0x243, 'M', 'ƀ'), + (0x244, 'M', 'ʉ'), + (0x245, 'M', 'ʌ'), + (0x246, 'M', 'ɇ'), + (0x247, 'V'), + (0x248, 'M', 'ɉ'), + (0x249, 'V'), + (0x24A, 'M', 'ɋ'), + (0x24B, 'V'), + (0x24C, 'M', 'ɍ'), + (0x24D, 'V'), + (0x24E, 'M', 'ɏ'), + (0x24F, 'V'), + (0x2B0, 'M', 'h'), + (0x2B1, 'M', 'ɦ'), + (0x2B2, 'M', 'j'), + (0x2B3, 'M', 'r'), + (0x2B4, 'M', 'ɹ'), + (0x2B5, 'M', 'ɻ'), + (0x2B6, 'M', 'ʁ'), + (0x2B7, 'M', 'w'), + (0x2B8, 'M', 'y'), + (0x2B9, 'V'), + (0x2D8, '3', ' ̆'), + (0x2D9, '3', ' ̇'), + (0x2DA, '3', ' ̊'), + (0x2DB, '3', ' ̨'), + (0x2DC, '3', ' ̃'), + (0x2DD, '3', ' ̋'), + (0x2DE, 'V'), + (0x2E0, 'M', 'ɣ'), + (0x2E1, 'M', 'l'), + (0x2E2, 'M', 's'), + (0x2E3, 'M', 'x'), + (0x2E4, 'M', 'ʕ'), + (0x2E5, 'V'), + (0x340, 'M', '̀'), + (0x341, 'M', '́'), + (0x342, 'V'), + (0x343, 'M', '̓'), + (0x344, 'M', '̈́'), + (0x345, 'M', 'ι'), + (0x346, 'V'), + (0x34F, 'I'), + (0x350, 'V'), + (0x370, 'M', 'ͱ'), + (0x371, 'V'), + (0x372, 'M', 'ͳ'), + (0x373, 'V'), + (0x374, 'M', 'ʹ'), + (0x375, 'V'), + (0x376, 'M', 'ͷ'), + (0x377, 'V'), + ] + +def _seg_6() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x378, 'X'), + (0x37A, '3', ' ι'), + (0x37B, 'V'), + (0x37E, '3', ';'), + (0x37F, 'M', 'ϳ'), + (0x380, 'X'), + (0x384, '3', ' ́'), + (0x385, '3', ' ̈́'), + (0x386, 'M', 'ά'), + (0x387, 'M', '·'), + (0x388, 'M', 'έ'), + (0x389, 'M', 'ή'), + (0x38A, 'M', 'ί'), + (0x38B, 'X'), + (0x38C, 'M', 'ό'), + (0x38D, 'X'), + (0x38E, 'M', 'ύ'), + (0x38F, 'M', 'ώ'), + (0x390, 'V'), + (0x391, 'M', 'α'), + (0x392, 'M', 'β'), + (0x393, 'M', 'γ'), + (0x394, 'M', 'δ'), + (0x395, 'M', 'ε'), + (0x396, 'M', 'ζ'), + (0x397, 'M', 'η'), + (0x398, 'M', 'θ'), + (0x399, 'M', 'ι'), + (0x39A, 'M', 'κ'), + (0x39B, 'M', 'λ'), + (0x39C, 'M', 'μ'), + (0x39D, 'M', 'ν'), + (0x39E, 'M', 'ξ'), + (0x39F, 'M', 'ο'), + (0x3A0, 'M', 'π'), + (0x3A1, 'M', 'ρ'), + (0x3A2, 'X'), + (0x3A3, 'M', 'σ'), + (0x3A4, 'M', 'τ'), + (0x3A5, 'M', 'υ'), + (0x3A6, 'M', 'φ'), + (0x3A7, 'M', 'χ'), + (0x3A8, 'M', 'ψ'), + (0x3A9, 'M', 'ω'), + (0x3AA, 'M', 'ϊ'), + (0x3AB, 'M', 'ϋ'), + (0x3AC, 'V'), + (0x3C2, 'D', 'σ'), + (0x3C3, 'V'), + (0x3CF, 'M', 'ϗ'), + (0x3D0, 'M', 'β'), + (0x3D1, 'M', 'θ'), + (0x3D2, 'M', 'υ'), + (0x3D3, 'M', 'ύ'), + (0x3D4, 'M', 'ϋ'), + (0x3D5, 'M', 'φ'), + (0x3D6, 'M', 'π'), + (0x3D7, 'V'), + (0x3D8, 'M', 'ϙ'), + (0x3D9, 'V'), + (0x3DA, 'M', 'ϛ'), + (0x3DB, 'V'), + (0x3DC, 'M', 'ϝ'), + (0x3DD, 'V'), + (0x3DE, 'M', 'ϟ'), + (0x3DF, 'V'), + (0x3E0, 'M', 'ϡ'), + (0x3E1, 'V'), + (0x3E2, 'M', 'ϣ'), + (0x3E3, 'V'), + (0x3E4, 'M', 'ϥ'), + (0x3E5, 'V'), + (0x3E6, 'M', 'ϧ'), + (0x3E7, 'V'), + (0x3E8, 'M', 'ϩ'), + (0x3E9, 'V'), + (0x3EA, 'M', 'ϫ'), + (0x3EB, 'V'), + (0x3EC, 'M', 'ϭ'), + (0x3ED, 'V'), + (0x3EE, 'M', 'ϯ'), + (0x3EF, 'V'), + (0x3F0, 'M', 'κ'), + (0x3F1, 'M', 'ρ'), + (0x3F2, 'M', 'σ'), + (0x3F3, 'V'), + (0x3F4, 'M', 'θ'), + (0x3F5, 'M', 'ε'), + (0x3F6, 'V'), + (0x3F7, 'M', 'ϸ'), + (0x3F8, 'V'), + (0x3F9, 'M', 'σ'), + (0x3FA, 'M', 'ϻ'), + (0x3FB, 'V'), + (0x3FD, 'M', 'ͻ'), + (0x3FE, 'M', 'ͼ'), + (0x3FF, 'M', 'ͽ'), + (0x400, 'M', 'ѐ'), + (0x401, 'M', 'ё'), + (0x402, 'M', 'ђ'), + ] + +def _seg_7() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x403, 'M', 'ѓ'), + (0x404, 'M', 'є'), + (0x405, 'M', 'ѕ'), + (0x406, 'M', 'і'), + (0x407, 'M', 'ї'), + (0x408, 'M', 'ј'), + (0x409, 'M', 'љ'), + (0x40A, 'M', 'њ'), + (0x40B, 'M', 'ћ'), + (0x40C, 'M', 'ќ'), + (0x40D, 'M', 'ѝ'), + (0x40E, 'M', 'ў'), + (0x40F, 'M', 'џ'), + (0x410, 'M', 'а'), + (0x411, 'M', 'б'), + (0x412, 'M', 'в'), + (0x413, 'M', 'г'), + (0x414, 'M', 'д'), + (0x415, 'M', 'е'), + (0x416, 'M', 'ж'), + (0x417, 'M', 'з'), + (0x418, 'M', 'и'), + (0x419, 'M', 'й'), + (0x41A, 'M', 'к'), + (0x41B, 'M', 'л'), + (0x41C, 'M', 'м'), + (0x41D, 'M', 'н'), + (0x41E, 'M', 'о'), + (0x41F, 'M', 'п'), + (0x420, 'M', 'р'), + (0x421, 'M', 'с'), + (0x422, 'M', 'т'), + (0x423, 'M', 'у'), + (0x424, 'M', 'ф'), + (0x425, 'M', 'х'), + (0x426, 'M', 'ц'), + (0x427, 'M', 'ч'), + (0x428, 'M', 'ш'), + (0x429, 'M', 'щ'), + (0x42A, 'M', 'ъ'), + (0x42B, 'M', 'ы'), + (0x42C, 'M', 'ь'), + (0x42D, 'M', 'э'), + (0x42E, 'M', 'ю'), + (0x42F, 'M', 'я'), + (0x430, 'V'), + (0x460, 'M', 'ѡ'), + (0x461, 'V'), + (0x462, 'M', 'ѣ'), + (0x463, 'V'), + (0x464, 'M', 'ѥ'), + (0x465, 'V'), + (0x466, 'M', 'ѧ'), + (0x467, 'V'), + (0x468, 'M', 'ѩ'), + (0x469, 'V'), + (0x46A, 'M', 'ѫ'), + (0x46B, 'V'), + (0x46C, 'M', 'ѭ'), + (0x46D, 'V'), + (0x46E, 'M', 'ѯ'), + (0x46F, 'V'), + (0x470, 'M', 'ѱ'), + (0x471, 'V'), + (0x472, 'M', 'ѳ'), + (0x473, 'V'), + (0x474, 'M', 'ѵ'), + (0x475, 'V'), + (0x476, 'M', 'ѷ'), + (0x477, 'V'), + (0x478, 'M', 'ѹ'), + (0x479, 'V'), + (0x47A, 'M', 'ѻ'), + (0x47B, 'V'), + (0x47C, 'M', 'ѽ'), + (0x47D, 'V'), + (0x47E, 'M', 'ѿ'), + (0x47F, 'V'), + (0x480, 'M', 'ҁ'), + (0x481, 'V'), + (0x48A, 'M', 'ҋ'), + (0x48B, 'V'), + (0x48C, 'M', 'ҍ'), + (0x48D, 'V'), + (0x48E, 'M', 'ҏ'), + (0x48F, 'V'), + (0x490, 'M', 'ґ'), + (0x491, 'V'), + (0x492, 'M', 'ғ'), + (0x493, 'V'), + (0x494, 'M', 'ҕ'), + (0x495, 'V'), + (0x496, 'M', 'җ'), + (0x497, 'V'), + (0x498, 'M', 'ҙ'), + (0x499, 'V'), + (0x49A, 'M', 'қ'), + (0x49B, 'V'), + (0x49C, 'M', 'ҝ'), + (0x49D, 'V'), + ] + +def _seg_8() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x49E, 'M', 'ҟ'), + (0x49F, 'V'), + (0x4A0, 'M', 'ҡ'), + (0x4A1, 'V'), + (0x4A2, 'M', 'ң'), + (0x4A3, 'V'), + (0x4A4, 'M', 'ҥ'), + (0x4A5, 'V'), + (0x4A6, 'M', 'ҧ'), + (0x4A7, 'V'), + (0x4A8, 'M', 'ҩ'), + (0x4A9, 'V'), + (0x4AA, 'M', 'ҫ'), + (0x4AB, 'V'), + (0x4AC, 'M', 'ҭ'), + (0x4AD, 'V'), + (0x4AE, 'M', 'ү'), + (0x4AF, 'V'), + (0x4B0, 'M', 'ұ'), + (0x4B1, 'V'), + (0x4B2, 'M', 'ҳ'), + (0x4B3, 'V'), + (0x4B4, 'M', 'ҵ'), + (0x4B5, 'V'), + (0x4B6, 'M', 'ҷ'), + (0x4B7, 'V'), + (0x4B8, 'M', 'ҹ'), + (0x4B9, 'V'), + (0x4BA, 'M', 'һ'), + (0x4BB, 'V'), + (0x4BC, 'M', 'ҽ'), + (0x4BD, 'V'), + (0x4BE, 'M', 'ҿ'), + (0x4BF, 'V'), + (0x4C0, 'X'), + (0x4C1, 'M', 'ӂ'), + (0x4C2, 'V'), + (0x4C3, 'M', 'ӄ'), + (0x4C4, 'V'), + (0x4C5, 'M', 'ӆ'), + (0x4C6, 'V'), + (0x4C7, 'M', 'ӈ'), + (0x4C8, 'V'), + (0x4C9, 'M', 'ӊ'), + (0x4CA, 'V'), + (0x4CB, 'M', 'ӌ'), + (0x4CC, 'V'), + (0x4CD, 'M', 'ӎ'), + (0x4CE, 'V'), + (0x4D0, 'M', 'ӑ'), + (0x4D1, 'V'), + (0x4D2, 'M', 'ӓ'), + (0x4D3, 'V'), + (0x4D4, 'M', 'ӕ'), + (0x4D5, 'V'), + (0x4D6, 'M', 'ӗ'), + (0x4D7, 'V'), + (0x4D8, 'M', 'ә'), + (0x4D9, 'V'), + (0x4DA, 'M', 'ӛ'), + (0x4DB, 'V'), + (0x4DC, 'M', 'ӝ'), + (0x4DD, 'V'), + (0x4DE, 'M', 'ӟ'), + (0x4DF, 'V'), + (0x4E0, 'M', 'ӡ'), + (0x4E1, 'V'), + (0x4E2, 'M', 'ӣ'), + (0x4E3, 'V'), + (0x4E4, 'M', 'ӥ'), + (0x4E5, 'V'), + (0x4E6, 'M', 'ӧ'), + (0x4E7, 'V'), + (0x4E8, 'M', 'ө'), + (0x4E9, 'V'), + (0x4EA, 'M', 'ӫ'), + (0x4EB, 'V'), + (0x4EC, 'M', 'ӭ'), + (0x4ED, 'V'), + (0x4EE, 'M', 'ӯ'), + (0x4EF, 'V'), + (0x4F0, 'M', 'ӱ'), + (0x4F1, 'V'), + (0x4F2, 'M', 'ӳ'), + (0x4F3, 'V'), + (0x4F4, 'M', 'ӵ'), + (0x4F5, 'V'), + (0x4F6, 'M', 'ӷ'), + (0x4F7, 'V'), + (0x4F8, 'M', 'ӹ'), + (0x4F9, 'V'), + (0x4FA, 'M', 'ӻ'), + (0x4FB, 'V'), + (0x4FC, 'M', 'ӽ'), + (0x4FD, 'V'), + (0x4FE, 'M', 'ӿ'), + (0x4FF, 'V'), + (0x500, 'M', 'ԁ'), + (0x501, 'V'), + (0x502, 'M', 'ԃ'), + ] + +def _seg_9() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x503, 'V'), + (0x504, 'M', 'ԅ'), + (0x505, 'V'), + (0x506, 'M', 'ԇ'), + (0x507, 'V'), + (0x508, 'M', 'ԉ'), + (0x509, 'V'), + (0x50A, 'M', 'ԋ'), + (0x50B, 'V'), + (0x50C, 'M', 'ԍ'), + (0x50D, 'V'), + (0x50E, 'M', 'ԏ'), + (0x50F, 'V'), + (0x510, 'M', 'ԑ'), + (0x511, 'V'), + (0x512, 'M', 'ԓ'), + (0x513, 'V'), + (0x514, 'M', 'ԕ'), + (0x515, 'V'), + (0x516, 'M', 'ԗ'), + (0x517, 'V'), + (0x518, 'M', 'ԙ'), + (0x519, 'V'), + (0x51A, 'M', 'ԛ'), + (0x51B, 'V'), + (0x51C, 'M', 'ԝ'), + (0x51D, 'V'), + (0x51E, 'M', 'ԟ'), + (0x51F, 'V'), + (0x520, 'M', 'ԡ'), + (0x521, 'V'), + (0x522, 'M', 'ԣ'), + (0x523, 'V'), + (0x524, 'M', 'ԥ'), + (0x525, 'V'), + (0x526, 'M', 'ԧ'), + (0x527, 'V'), + (0x528, 'M', 'ԩ'), + (0x529, 'V'), + (0x52A, 'M', 'ԫ'), + (0x52B, 'V'), + (0x52C, 'M', 'ԭ'), + (0x52D, 'V'), + (0x52E, 'M', 'ԯ'), + (0x52F, 'V'), + (0x530, 'X'), + (0x531, 'M', 'ա'), + (0x532, 'M', 'բ'), + (0x533, 'M', 'գ'), + (0x534, 'M', 'դ'), + (0x535, 'M', 'ե'), + (0x536, 'M', 'զ'), + (0x537, 'M', 'է'), + (0x538, 'M', 'ը'), + (0x539, 'M', 'թ'), + (0x53A, 'M', 'ժ'), + (0x53B, 'M', 'ի'), + (0x53C, 'M', 'լ'), + (0x53D, 'M', 'խ'), + (0x53E, 'M', 'ծ'), + (0x53F, 'M', 'կ'), + (0x540, 'M', 'հ'), + (0x541, 'M', 'ձ'), + (0x542, 'M', 'ղ'), + (0x543, 'M', 'ճ'), + (0x544, 'M', 'մ'), + (0x545, 'M', 'յ'), + (0x546, 'M', 'ն'), + (0x547, 'M', 'շ'), + (0x548, 'M', 'ո'), + (0x549, 'M', 'չ'), + (0x54A, 'M', 'պ'), + (0x54B, 'M', 'ջ'), + (0x54C, 'M', 'ռ'), + (0x54D, 'M', 'ս'), + (0x54E, 'M', 'վ'), + (0x54F, 'M', 'տ'), + (0x550, 'M', 'ր'), + (0x551, 'M', 'ց'), + (0x552, 'M', 'ւ'), + (0x553, 'M', 'փ'), + (0x554, 'M', 'ք'), + (0x555, 'M', 'օ'), + (0x556, 'M', 'ֆ'), + (0x557, 'X'), + (0x559, 'V'), + (0x587, 'M', 'եւ'), + (0x588, 'V'), + (0x58B, 'X'), + (0x58D, 'V'), + (0x590, 'X'), + (0x591, 'V'), + (0x5C8, 'X'), + (0x5D0, 'V'), + (0x5EB, 'X'), + (0x5EF, 'V'), + (0x5F5, 'X'), + (0x606, 'V'), + (0x61C, 'X'), + (0x61D, 'V'), + ] + +def _seg_10() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x675, 'M', 'اٴ'), + (0x676, 'M', 'وٴ'), + (0x677, 'M', 'ۇٴ'), + (0x678, 'M', 'يٴ'), + (0x679, 'V'), + (0x6DD, 'X'), + (0x6DE, 'V'), + (0x70E, 'X'), + (0x710, 'V'), + (0x74B, 'X'), + (0x74D, 'V'), + (0x7B2, 'X'), + (0x7C0, 'V'), + (0x7FB, 'X'), + (0x7FD, 'V'), + (0x82E, 'X'), + (0x830, 'V'), + (0x83F, 'X'), + (0x840, 'V'), + (0x85C, 'X'), + (0x85E, 'V'), + (0x85F, 'X'), + (0x860, 'V'), + (0x86B, 'X'), + (0x870, 'V'), + (0x88F, 'X'), + (0x898, 'V'), + (0x8E2, 'X'), + (0x8E3, 'V'), + (0x958, 'M', 'क़'), + (0x959, 'M', 'ख़'), + (0x95A, 'M', 'ग़'), + (0x95B, 'M', 'ज़'), + (0x95C, 'M', 'ड़'), + (0x95D, 'M', 'ढ़'), + (0x95E, 'M', 'फ़'), + (0x95F, 'M', 'य़'), + (0x960, 'V'), + (0x984, 'X'), + (0x985, 'V'), + (0x98D, 'X'), + (0x98F, 'V'), + (0x991, 'X'), + (0x993, 'V'), + (0x9A9, 'X'), + (0x9AA, 'V'), + (0x9B1, 'X'), + (0x9B2, 'V'), + (0x9B3, 'X'), + (0x9B6, 'V'), + (0x9BA, 'X'), + (0x9BC, 'V'), + (0x9C5, 'X'), + (0x9C7, 'V'), + (0x9C9, 'X'), + (0x9CB, 'V'), + (0x9CF, 'X'), + (0x9D7, 'V'), + (0x9D8, 'X'), + (0x9DC, 'M', 'ড়'), + (0x9DD, 'M', 'ঢ়'), + (0x9DE, 'X'), + (0x9DF, 'M', 'য়'), + (0x9E0, 'V'), + (0x9E4, 'X'), + (0x9E6, 'V'), + (0x9FF, 'X'), + (0xA01, 'V'), + (0xA04, 'X'), + (0xA05, 'V'), + (0xA0B, 'X'), + (0xA0F, 'V'), + (0xA11, 'X'), + (0xA13, 'V'), + (0xA29, 'X'), + (0xA2A, 'V'), + (0xA31, 'X'), + (0xA32, 'V'), + (0xA33, 'M', 'ਲ਼'), + (0xA34, 'X'), + (0xA35, 'V'), + (0xA36, 'M', 'ਸ਼'), + (0xA37, 'X'), + (0xA38, 'V'), + (0xA3A, 'X'), + (0xA3C, 'V'), + (0xA3D, 'X'), + (0xA3E, 'V'), + (0xA43, 'X'), + (0xA47, 'V'), + (0xA49, 'X'), + (0xA4B, 'V'), + (0xA4E, 'X'), + (0xA51, 'V'), + (0xA52, 'X'), + (0xA59, 'M', 'ਖ਼'), + (0xA5A, 'M', 'ਗ਼'), + (0xA5B, 'M', 'ਜ਼'), + (0xA5C, 'V'), + (0xA5D, 'X'), + ] + +def _seg_11() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xA5E, 'M', 'ਫ਼'), + (0xA5F, 'X'), + (0xA66, 'V'), + (0xA77, 'X'), + (0xA81, 'V'), + (0xA84, 'X'), + (0xA85, 'V'), + (0xA8E, 'X'), + (0xA8F, 'V'), + (0xA92, 'X'), + (0xA93, 'V'), + (0xAA9, 'X'), + (0xAAA, 'V'), + (0xAB1, 'X'), + (0xAB2, 'V'), + (0xAB4, 'X'), + (0xAB5, 'V'), + (0xABA, 'X'), + (0xABC, 'V'), + (0xAC6, 'X'), + (0xAC7, 'V'), + (0xACA, 'X'), + (0xACB, 'V'), + (0xACE, 'X'), + (0xAD0, 'V'), + (0xAD1, 'X'), + (0xAE0, 'V'), + (0xAE4, 'X'), + (0xAE6, 'V'), + (0xAF2, 'X'), + (0xAF9, 'V'), + (0xB00, 'X'), + (0xB01, 'V'), + (0xB04, 'X'), + (0xB05, 'V'), + (0xB0D, 'X'), + (0xB0F, 'V'), + (0xB11, 'X'), + (0xB13, 'V'), + (0xB29, 'X'), + (0xB2A, 'V'), + (0xB31, 'X'), + (0xB32, 'V'), + (0xB34, 'X'), + (0xB35, 'V'), + (0xB3A, 'X'), + (0xB3C, 'V'), + (0xB45, 'X'), + (0xB47, 'V'), + (0xB49, 'X'), + (0xB4B, 'V'), + (0xB4E, 'X'), + (0xB55, 'V'), + (0xB58, 'X'), + (0xB5C, 'M', 'ଡ଼'), + (0xB5D, 'M', 'ଢ଼'), + (0xB5E, 'X'), + (0xB5F, 'V'), + (0xB64, 'X'), + (0xB66, 'V'), + (0xB78, 'X'), + (0xB82, 'V'), + (0xB84, 'X'), + (0xB85, 'V'), + (0xB8B, 'X'), + (0xB8E, 'V'), + (0xB91, 'X'), + (0xB92, 'V'), + (0xB96, 'X'), + (0xB99, 'V'), + (0xB9B, 'X'), + (0xB9C, 'V'), + (0xB9D, 'X'), + (0xB9E, 'V'), + (0xBA0, 'X'), + (0xBA3, 'V'), + (0xBA5, 'X'), + (0xBA8, 'V'), + (0xBAB, 'X'), + (0xBAE, 'V'), + (0xBBA, 'X'), + (0xBBE, 'V'), + (0xBC3, 'X'), + (0xBC6, 'V'), + (0xBC9, 'X'), + (0xBCA, 'V'), + (0xBCE, 'X'), + (0xBD0, 'V'), + (0xBD1, 'X'), + (0xBD7, 'V'), + (0xBD8, 'X'), + (0xBE6, 'V'), + (0xBFB, 'X'), + (0xC00, 'V'), + (0xC0D, 'X'), + (0xC0E, 'V'), + (0xC11, 'X'), + (0xC12, 'V'), + (0xC29, 'X'), + (0xC2A, 'V'), + ] + +def _seg_12() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xC3A, 'X'), + (0xC3C, 'V'), + (0xC45, 'X'), + (0xC46, 'V'), + (0xC49, 'X'), + (0xC4A, 'V'), + (0xC4E, 'X'), + (0xC55, 'V'), + (0xC57, 'X'), + (0xC58, 'V'), + (0xC5B, 'X'), + (0xC5D, 'V'), + (0xC5E, 'X'), + (0xC60, 'V'), + (0xC64, 'X'), + (0xC66, 'V'), + (0xC70, 'X'), + (0xC77, 'V'), + (0xC8D, 'X'), + (0xC8E, 'V'), + (0xC91, 'X'), + (0xC92, 'V'), + (0xCA9, 'X'), + (0xCAA, 'V'), + (0xCB4, 'X'), + (0xCB5, 'V'), + (0xCBA, 'X'), + (0xCBC, 'V'), + (0xCC5, 'X'), + (0xCC6, 'V'), + (0xCC9, 'X'), + (0xCCA, 'V'), + (0xCCE, 'X'), + (0xCD5, 'V'), + (0xCD7, 'X'), + (0xCDD, 'V'), + (0xCDF, 'X'), + (0xCE0, 'V'), + (0xCE4, 'X'), + (0xCE6, 'V'), + (0xCF0, 'X'), + (0xCF1, 'V'), + (0xCF4, 'X'), + (0xD00, 'V'), + (0xD0D, 'X'), + (0xD0E, 'V'), + (0xD11, 'X'), + (0xD12, 'V'), + (0xD45, 'X'), + (0xD46, 'V'), + (0xD49, 'X'), + (0xD4A, 'V'), + (0xD50, 'X'), + (0xD54, 'V'), + (0xD64, 'X'), + (0xD66, 'V'), + (0xD80, 'X'), + (0xD81, 'V'), + (0xD84, 'X'), + (0xD85, 'V'), + (0xD97, 'X'), + (0xD9A, 'V'), + (0xDB2, 'X'), + (0xDB3, 'V'), + (0xDBC, 'X'), + (0xDBD, 'V'), + (0xDBE, 'X'), + (0xDC0, 'V'), + (0xDC7, 'X'), + (0xDCA, 'V'), + (0xDCB, 'X'), + (0xDCF, 'V'), + (0xDD5, 'X'), + (0xDD6, 'V'), + (0xDD7, 'X'), + (0xDD8, 'V'), + (0xDE0, 'X'), + (0xDE6, 'V'), + (0xDF0, 'X'), + (0xDF2, 'V'), + (0xDF5, 'X'), + (0xE01, 'V'), + (0xE33, 'M', 'ํา'), + (0xE34, 'V'), + (0xE3B, 'X'), + (0xE3F, 'V'), + (0xE5C, 'X'), + (0xE81, 'V'), + (0xE83, 'X'), + (0xE84, 'V'), + (0xE85, 'X'), + (0xE86, 'V'), + (0xE8B, 'X'), + (0xE8C, 'V'), + (0xEA4, 'X'), + (0xEA5, 'V'), + (0xEA6, 'X'), + (0xEA7, 'V'), + (0xEB3, 'M', 'ໍາ'), + (0xEB4, 'V'), + ] + +def _seg_13() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xEBE, 'X'), + (0xEC0, 'V'), + (0xEC5, 'X'), + (0xEC6, 'V'), + (0xEC7, 'X'), + (0xEC8, 'V'), + (0xECF, 'X'), + (0xED0, 'V'), + (0xEDA, 'X'), + (0xEDC, 'M', 'ຫນ'), + (0xEDD, 'M', 'ຫມ'), + (0xEDE, 'V'), + (0xEE0, 'X'), + (0xF00, 'V'), + (0xF0C, 'M', '་'), + (0xF0D, 'V'), + (0xF43, 'M', 'གྷ'), + (0xF44, 'V'), + (0xF48, 'X'), + (0xF49, 'V'), + (0xF4D, 'M', 'ཌྷ'), + (0xF4E, 'V'), + (0xF52, 'M', 'དྷ'), + (0xF53, 'V'), + (0xF57, 'M', 'བྷ'), + (0xF58, 'V'), + (0xF5C, 'M', 'ཛྷ'), + (0xF5D, 'V'), + (0xF69, 'M', 'ཀྵ'), + (0xF6A, 'V'), + (0xF6D, 'X'), + (0xF71, 'V'), + (0xF73, 'M', 'ཱི'), + (0xF74, 'V'), + (0xF75, 'M', 'ཱུ'), + (0xF76, 'M', 'ྲྀ'), + (0xF77, 'M', 'ྲཱྀ'), + (0xF78, 'M', 'ླྀ'), + (0xF79, 'M', 'ླཱྀ'), + (0xF7A, 'V'), + (0xF81, 'M', 'ཱྀ'), + (0xF82, 'V'), + (0xF93, 'M', 'ྒྷ'), + (0xF94, 'V'), + (0xF98, 'X'), + (0xF99, 'V'), + (0xF9D, 'M', 'ྜྷ'), + (0xF9E, 'V'), + (0xFA2, 'M', 'ྡྷ'), + (0xFA3, 'V'), + (0xFA7, 'M', 'ྦྷ'), + (0xFA8, 'V'), + (0xFAC, 'M', 'ྫྷ'), + (0xFAD, 'V'), + (0xFB9, 'M', 'ྐྵ'), + (0xFBA, 'V'), + (0xFBD, 'X'), + (0xFBE, 'V'), + (0xFCD, 'X'), + (0xFCE, 'V'), + (0xFDB, 'X'), + (0x1000, 'V'), + (0x10A0, 'X'), + (0x10C7, 'M', 'ⴧ'), + (0x10C8, 'X'), + (0x10CD, 'M', 'ⴭ'), + (0x10CE, 'X'), + (0x10D0, 'V'), + (0x10FC, 'M', 'ნ'), + (0x10FD, 'V'), + (0x115F, 'X'), + (0x1161, 'V'), + (0x1249, 'X'), + (0x124A, 'V'), + (0x124E, 'X'), + (0x1250, 'V'), + (0x1257, 'X'), + (0x1258, 'V'), + (0x1259, 'X'), + (0x125A, 'V'), + (0x125E, 'X'), + (0x1260, 'V'), + (0x1289, 'X'), + (0x128A, 'V'), + (0x128E, 'X'), + (0x1290, 'V'), + (0x12B1, 'X'), + (0x12B2, 'V'), + (0x12B6, 'X'), + (0x12B8, 'V'), + (0x12BF, 'X'), + (0x12C0, 'V'), + (0x12C1, 'X'), + (0x12C2, 'V'), + (0x12C6, 'X'), + (0x12C8, 'V'), + (0x12D7, 'X'), + (0x12D8, 'V'), + (0x1311, 'X'), + (0x1312, 'V'), + ] + +def _seg_14() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1316, 'X'), + (0x1318, 'V'), + (0x135B, 'X'), + (0x135D, 'V'), + (0x137D, 'X'), + (0x1380, 'V'), + (0x139A, 'X'), + (0x13A0, 'V'), + (0x13F6, 'X'), + (0x13F8, 'M', 'Ᏸ'), + (0x13F9, 'M', 'Ᏹ'), + (0x13FA, 'M', 'Ᏺ'), + (0x13FB, 'M', 'Ᏻ'), + (0x13FC, 'M', 'Ᏼ'), + (0x13FD, 'M', 'Ᏽ'), + (0x13FE, 'X'), + (0x1400, 'V'), + (0x1680, 'X'), + (0x1681, 'V'), + (0x169D, 'X'), + (0x16A0, 'V'), + (0x16F9, 'X'), + (0x1700, 'V'), + (0x1716, 'X'), + (0x171F, 'V'), + (0x1737, 'X'), + (0x1740, 'V'), + (0x1754, 'X'), + (0x1760, 'V'), + (0x176D, 'X'), + (0x176E, 'V'), + (0x1771, 'X'), + (0x1772, 'V'), + (0x1774, 'X'), + (0x1780, 'V'), + (0x17B4, 'X'), + (0x17B6, 'V'), + (0x17DE, 'X'), + (0x17E0, 'V'), + (0x17EA, 'X'), + (0x17F0, 'V'), + (0x17FA, 'X'), + (0x1800, 'V'), + (0x1806, 'X'), + (0x1807, 'V'), + (0x180B, 'I'), + (0x180E, 'X'), + (0x180F, 'I'), + (0x1810, 'V'), + (0x181A, 'X'), + (0x1820, 'V'), + (0x1879, 'X'), + (0x1880, 'V'), + (0x18AB, 'X'), + (0x18B0, 'V'), + (0x18F6, 'X'), + (0x1900, 'V'), + (0x191F, 'X'), + (0x1920, 'V'), + (0x192C, 'X'), + (0x1930, 'V'), + (0x193C, 'X'), + (0x1940, 'V'), + (0x1941, 'X'), + (0x1944, 'V'), + (0x196E, 'X'), + (0x1970, 'V'), + (0x1975, 'X'), + (0x1980, 'V'), + (0x19AC, 'X'), + (0x19B0, 'V'), + (0x19CA, 'X'), + (0x19D0, 'V'), + (0x19DB, 'X'), + (0x19DE, 'V'), + (0x1A1C, 'X'), + (0x1A1E, 'V'), + (0x1A5F, 'X'), + (0x1A60, 'V'), + (0x1A7D, 'X'), + (0x1A7F, 'V'), + (0x1A8A, 'X'), + (0x1A90, 'V'), + (0x1A9A, 'X'), + (0x1AA0, 'V'), + (0x1AAE, 'X'), + (0x1AB0, 'V'), + (0x1ACF, 'X'), + (0x1B00, 'V'), + (0x1B4D, 'X'), + (0x1B50, 'V'), + (0x1B7F, 'X'), + (0x1B80, 'V'), + (0x1BF4, 'X'), + (0x1BFC, 'V'), + (0x1C38, 'X'), + (0x1C3B, 'V'), + (0x1C4A, 'X'), + (0x1C4D, 'V'), + (0x1C80, 'M', 'в'), + ] + +def _seg_15() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1C81, 'M', 'д'), + (0x1C82, 'M', 'о'), + (0x1C83, 'M', 'с'), + (0x1C84, 'M', 'т'), + (0x1C86, 'M', 'ъ'), + (0x1C87, 'M', 'ѣ'), + (0x1C88, 'M', 'ꙋ'), + (0x1C89, 'X'), + (0x1C90, 'M', 'ა'), + (0x1C91, 'M', 'ბ'), + (0x1C92, 'M', 'გ'), + (0x1C93, 'M', 'დ'), + (0x1C94, 'M', 'ე'), + (0x1C95, 'M', 'ვ'), + (0x1C96, 'M', 'ზ'), + (0x1C97, 'M', 'თ'), + (0x1C98, 'M', 'ი'), + (0x1C99, 'M', 'კ'), + (0x1C9A, 'M', 'ლ'), + (0x1C9B, 'M', 'მ'), + (0x1C9C, 'M', 'ნ'), + (0x1C9D, 'M', 'ო'), + (0x1C9E, 'M', 'პ'), + (0x1C9F, 'M', 'ჟ'), + (0x1CA0, 'M', 'რ'), + (0x1CA1, 'M', 'ს'), + (0x1CA2, 'M', 'ტ'), + (0x1CA3, 'M', 'უ'), + (0x1CA4, 'M', 'ფ'), + (0x1CA5, 'M', 'ქ'), + (0x1CA6, 'M', 'ღ'), + (0x1CA7, 'M', 'ყ'), + (0x1CA8, 'M', 'შ'), + (0x1CA9, 'M', 'ჩ'), + (0x1CAA, 'M', 'ც'), + (0x1CAB, 'M', 'ძ'), + (0x1CAC, 'M', 'წ'), + (0x1CAD, 'M', 'ჭ'), + (0x1CAE, 'M', 'ხ'), + (0x1CAF, 'M', 'ჯ'), + (0x1CB0, 'M', 'ჰ'), + (0x1CB1, 'M', 'ჱ'), + (0x1CB2, 'M', 'ჲ'), + (0x1CB3, 'M', 'ჳ'), + (0x1CB4, 'M', 'ჴ'), + (0x1CB5, 'M', 'ჵ'), + (0x1CB6, 'M', 'ჶ'), + (0x1CB7, 'M', 'ჷ'), + (0x1CB8, 'M', 'ჸ'), + (0x1CB9, 'M', 'ჹ'), + (0x1CBA, 'M', 'ჺ'), + (0x1CBB, 'X'), + (0x1CBD, 'M', 'ჽ'), + (0x1CBE, 'M', 'ჾ'), + (0x1CBF, 'M', 'ჿ'), + (0x1CC0, 'V'), + (0x1CC8, 'X'), + (0x1CD0, 'V'), + (0x1CFB, 'X'), + (0x1D00, 'V'), + (0x1D2C, 'M', 'a'), + (0x1D2D, 'M', 'æ'), + (0x1D2E, 'M', 'b'), + (0x1D2F, 'V'), + (0x1D30, 'M', 'd'), + (0x1D31, 'M', 'e'), + (0x1D32, 'M', 'ǝ'), + (0x1D33, 'M', 'g'), + (0x1D34, 'M', 'h'), + (0x1D35, 'M', 'i'), + (0x1D36, 'M', 'j'), + (0x1D37, 'M', 'k'), + (0x1D38, 'M', 'l'), + (0x1D39, 'M', 'm'), + (0x1D3A, 'M', 'n'), + (0x1D3B, 'V'), + (0x1D3C, 'M', 'o'), + (0x1D3D, 'M', 'ȣ'), + (0x1D3E, 'M', 'p'), + (0x1D3F, 'M', 'r'), + (0x1D40, 'M', 't'), + (0x1D41, 'M', 'u'), + (0x1D42, 'M', 'w'), + (0x1D43, 'M', 'a'), + (0x1D44, 'M', 'ɐ'), + (0x1D45, 'M', 'ɑ'), + (0x1D46, 'M', 'ᴂ'), + (0x1D47, 'M', 'b'), + (0x1D48, 'M', 'd'), + (0x1D49, 'M', 'e'), + (0x1D4A, 'M', 'ə'), + (0x1D4B, 'M', 'ɛ'), + (0x1D4C, 'M', 'ɜ'), + (0x1D4D, 'M', 'g'), + (0x1D4E, 'V'), + (0x1D4F, 'M', 'k'), + (0x1D50, 'M', 'm'), + (0x1D51, 'M', 'ŋ'), + (0x1D52, 'M', 'o'), + (0x1D53, 'M', 'ɔ'), + ] + +def _seg_16() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D54, 'M', 'ᴖ'), + (0x1D55, 'M', 'ᴗ'), + (0x1D56, 'M', 'p'), + (0x1D57, 'M', 't'), + (0x1D58, 'M', 'u'), + (0x1D59, 'M', 'ᴝ'), + (0x1D5A, 'M', 'ɯ'), + (0x1D5B, 'M', 'v'), + (0x1D5C, 'M', 'ᴥ'), + (0x1D5D, 'M', 'β'), + (0x1D5E, 'M', 'γ'), + (0x1D5F, 'M', 'δ'), + (0x1D60, 'M', 'φ'), + (0x1D61, 'M', 'χ'), + (0x1D62, 'M', 'i'), + (0x1D63, 'M', 'r'), + (0x1D64, 'M', 'u'), + (0x1D65, 'M', 'v'), + (0x1D66, 'M', 'β'), + (0x1D67, 'M', 'γ'), + (0x1D68, 'M', 'ρ'), + (0x1D69, 'M', 'φ'), + (0x1D6A, 'M', 'χ'), + (0x1D6B, 'V'), + (0x1D78, 'M', 'н'), + (0x1D79, 'V'), + (0x1D9B, 'M', 'ɒ'), + (0x1D9C, 'M', 'c'), + (0x1D9D, 'M', 'ɕ'), + (0x1D9E, 'M', 'ð'), + (0x1D9F, 'M', 'ɜ'), + (0x1DA0, 'M', 'f'), + (0x1DA1, 'M', 'ɟ'), + (0x1DA2, 'M', 'ɡ'), + (0x1DA3, 'M', 'ɥ'), + (0x1DA4, 'M', 'ɨ'), + (0x1DA5, 'M', 'ɩ'), + (0x1DA6, 'M', 'ɪ'), + (0x1DA7, 'M', 'ᵻ'), + (0x1DA8, 'M', 'ʝ'), + (0x1DA9, 'M', 'ɭ'), + (0x1DAA, 'M', 'ᶅ'), + (0x1DAB, 'M', 'ʟ'), + (0x1DAC, 'M', 'ɱ'), + (0x1DAD, 'M', 'ɰ'), + (0x1DAE, 'M', 'ɲ'), + (0x1DAF, 'M', 'ɳ'), + (0x1DB0, 'M', 'ɴ'), + (0x1DB1, 'M', 'ɵ'), + (0x1DB2, 'M', 'ɸ'), + (0x1DB3, 'M', 'ʂ'), + (0x1DB4, 'M', 'ʃ'), + (0x1DB5, 'M', 'ƫ'), + (0x1DB6, 'M', 'ʉ'), + (0x1DB7, 'M', 'ʊ'), + (0x1DB8, 'M', 'ᴜ'), + (0x1DB9, 'M', 'ʋ'), + (0x1DBA, 'M', 'ʌ'), + (0x1DBB, 'M', 'z'), + (0x1DBC, 'M', 'ʐ'), + (0x1DBD, 'M', 'ʑ'), + (0x1DBE, 'M', 'ʒ'), + (0x1DBF, 'M', 'θ'), + (0x1DC0, 'V'), + (0x1E00, 'M', 'ḁ'), + (0x1E01, 'V'), + (0x1E02, 'M', 'ḃ'), + (0x1E03, 'V'), + (0x1E04, 'M', 'ḅ'), + (0x1E05, 'V'), + (0x1E06, 'M', 'ḇ'), + (0x1E07, 'V'), + (0x1E08, 'M', 'ḉ'), + (0x1E09, 'V'), + (0x1E0A, 'M', 'ḋ'), + (0x1E0B, 'V'), + (0x1E0C, 'M', 'ḍ'), + (0x1E0D, 'V'), + (0x1E0E, 'M', 'ḏ'), + (0x1E0F, 'V'), + (0x1E10, 'M', 'ḑ'), + (0x1E11, 'V'), + (0x1E12, 'M', 'ḓ'), + (0x1E13, 'V'), + (0x1E14, 'M', 'ḕ'), + (0x1E15, 'V'), + (0x1E16, 'M', 'ḗ'), + (0x1E17, 'V'), + (0x1E18, 'M', 'ḙ'), + (0x1E19, 'V'), + (0x1E1A, 'M', 'ḛ'), + (0x1E1B, 'V'), + (0x1E1C, 'M', 'ḝ'), + (0x1E1D, 'V'), + (0x1E1E, 'M', 'ḟ'), + (0x1E1F, 'V'), + (0x1E20, 'M', 'ḡ'), + (0x1E21, 'V'), + (0x1E22, 'M', 'ḣ'), + (0x1E23, 'V'), + ] + +def _seg_17() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1E24, 'M', 'ḥ'), + (0x1E25, 'V'), + (0x1E26, 'M', 'ḧ'), + (0x1E27, 'V'), + (0x1E28, 'M', 'ḩ'), + (0x1E29, 'V'), + (0x1E2A, 'M', 'ḫ'), + (0x1E2B, 'V'), + (0x1E2C, 'M', 'ḭ'), + (0x1E2D, 'V'), + (0x1E2E, 'M', 'ḯ'), + (0x1E2F, 'V'), + (0x1E30, 'M', 'ḱ'), + (0x1E31, 'V'), + (0x1E32, 'M', 'ḳ'), + (0x1E33, 'V'), + (0x1E34, 'M', 'ḵ'), + (0x1E35, 'V'), + (0x1E36, 'M', 'ḷ'), + (0x1E37, 'V'), + (0x1E38, 'M', 'ḹ'), + (0x1E39, 'V'), + (0x1E3A, 'M', 'ḻ'), + (0x1E3B, 'V'), + (0x1E3C, 'M', 'ḽ'), + (0x1E3D, 'V'), + (0x1E3E, 'M', 'ḿ'), + (0x1E3F, 'V'), + (0x1E40, 'M', 'ṁ'), + (0x1E41, 'V'), + (0x1E42, 'M', 'ṃ'), + (0x1E43, 'V'), + (0x1E44, 'M', 'ṅ'), + (0x1E45, 'V'), + (0x1E46, 'M', 'ṇ'), + (0x1E47, 'V'), + (0x1E48, 'M', 'ṉ'), + (0x1E49, 'V'), + (0x1E4A, 'M', 'ṋ'), + (0x1E4B, 'V'), + (0x1E4C, 'M', 'ṍ'), + (0x1E4D, 'V'), + (0x1E4E, 'M', 'ṏ'), + (0x1E4F, 'V'), + (0x1E50, 'M', 'ṑ'), + (0x1E51, 'V'), + (0x1E52, 'M', 'ṓ'), + (0x1E53, 'V'), + (0x1E54, 'M', 'ṕ'), + (0x1E55, 'V'), + (0x1E56, 'M', 'ṗ'), + (0x1E57, 'V'), + (0x1E58, 'M', 'ṙ'), + (0x1E59, 'V'), + (0x1E5A, 'M', 'ṛ'), + (0x1E5B, 'V'), + (0x1E5C, 'M', 'ṝ'), + (0x1E5D, 'V'), + (0x1E5E, 'M', 'ṟ'), + (0x1E5F, 'V'), + (0x1E60, 'M', 'ṡ'), + (0x1E61, 'V'), + (0x1E62, 'M', 'ṣ'), + (0x1E63, 'V'), + (0x1E64, 'M', 'ṥ'), + (0x1E65, 'V'), + (0x1E66, 'M', 'ṧ'), + (0x1E67, 'V'), + (0x1E68, 'M', 'ṩ'), + (0x1E69, 'V'), + (0x1E6A, 'M', 'ṫ'), + (0x1E6B, 'V'), + (0x1E6C, 'M', 'ṭ'), + (0x1E6D, 'V'), + (0x1E6E, 'M', 'ṯ'), + (0x1E6F, 'V'), + (0x1E70, 'M', 'ṱ'), + (0x1E71, 'V'), + (0x1E72, 'M', 'ṳ'), + (0x1E73, 'V'), + (0x1E74, 'M', 'ṵ'), + (0x1E75, 'V'), + (0x1E76, 'M', 'ṷ'), + (0x1E77, 'V'), + (0x1E78, 'M', 'ṹ'), + (0x1E79, 'V'), + (0x1E7A, 'M', 'ṻ'), + (0x1E7B, 'V'), + (0x1E7C, 'M', 'ṽ'), + (0x1E7D, 'V'), + (0x1E7E, 'M', 'ṿ'), + (0x1E7F, 'V'), + (0x1E80, 'M', 'ẁ'), + (0x1E81, 'V'), + (0x1E82, 'M', 'ẃ'), + (0x1E83, 'V'), + (0x1E84, 'M', 'ẅ'), + (0x1E85, 'V'), + (0x1E86, 'M', 'ẇ'), + (0x1E87, 'V'), + ] + +def _seg_18() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1E88, 'M', 'ẉ'), + (0x1E89, 'V'), + (0x1E8A, 'M', 'ẋ'), + (0x1E8B, 'V'), + (0x1E8C, 'M', 'ẍ'), + (0x1E8D, 'V'), + (0x1E8E, 'M', 'ẏ'), + (0x1E8F, 'V'), + (0x1E90, 'M', 'ẑ'), + (0x1E91, 'V'), + (0x1E92, 'M', 'ẓ'), + (0x1E93, 'V'), + (0x1E94, 'M', 'ẕ'), + (0x1E95, 'V'), + (0x1E9A, 'M', 'aʾ'), + (0x1E9B, 'M', 'ṡ'), + (0x1E9C, 'V'), + (0x1E9E, 'M', 'ss'), + (0x1E9F, 'V'), + (0x1EA0, 'M', 'ạ'), + (0x1EA1, 'V'), + (0x1EA2, 'M', 'ả'), + (0x1EA3, 'V'), + (0x1EA4, 'M', 'ấ'), + (0x1EA5, 'V'), + (0x1EA6, 'M', 'ầ'), + (0x1EA7, 'V'), + (0x1EA8, 'M', 'ẩ'), + (0x1EA9, 'V'), + (0x1EAA, 'M', 'ẫ'), + (0x1EAB, 'V'), + (0x1EAC, 'M', 'ậ'), + (0x1EAD, 'V'), + (0x1EAE, 'M', 'ắ'), + (0x1EAF, 'V'), + (0x1EB0, 'M', 'ằ'), + (0x1EB1, 'V'), + (0x1EB2, 'M', 'ẳ'), + (0x1EB3, 'V'), + (0x1EB4, 'M', 'ẵ'), + (0x1EB5, 'V'), + (0x1EB6, 'M', 'ặ'), + (0x1EB7, 'V'), + (0x1EB8, 'M', 'ẹ'), + (0x1EB9, 'V'), + (0x1EBA, 'M', 'ẻ'), + (0x1EBB, 'V'), + (0x1EBC, 'M', 'ẽ'), + (0x1EBD, 'V'), + (0x1EBE, 'M', 'ế'), + (0x1EBF, 'V'), + (0x1EC0, 'M', 'ề'), + (0x1EC1, 'V'), + (0x1EC2, 'M', 'ể'), + (0x1EC3, 'V'), + (0x1EC4, 'M', 'ễ'), + (0x1EC5, 'V'), + (0x1EC6, 'M', 'ệ'), + (0x1EC7, 'V'), + (0x1EC8, 'M', 'ỉ'), + (0x1EC9, 'V'), + (0x1ECA, 'M', 'ị'), + (0x1ECB, 'V'), + (0x1ECC, 'M', 'ọ'), + (0x1ECD, 'V'), + (0x1ECE, 'M', 'ỏ'), + (0x1ECF, 'V'), + (0x1ED0, 'M', 'ố'), + (0x1ED1, 'V'), + (0x1ED2, 'M', 'ồ'), + (0x1ED3, 'V'), + (0x1ED4, 'M', 'ổ'), + (0x1ED5, 'V'), + (0x1ED6, 'M', 'ỗ'), + (0x1ED7, 'V'), + (0x1ED8, 'M', 'ộ'), + (0x1ED9, 'V'), + (0x1EDA, 'M', 'ớ'), + (0x1EDB, 'V'), + (0x1EDC, 'M', 'ờ'), + (0x1EDD, 'V'), + (0x1EDE, 'M', 'ở'), + (0x1EDF, 'V'), + (0x1EE0, 'M', 'ỡ'), + (0x1EE1, 'V'), + (0x1EE2, 'M', 'ợ'), + (0x1EE3, 'V'), + (0x1EE4, 'M', 'ụ'), + (0x1EE5, 'V'), + (0x1EE6, 'M', 'ủ'), + (0x1EE7, 'V'), + (0x1EE8, 'M', 'ứ'), + (0x1EE9, 'V'), + (0x1EEA, 'M', 'ừ'), + (0x1EEB, 'V'), + (0x1EEC, 'M', 'ử'), + (0x1EED, 'V'), + (0x1EEE, 'M', 'ữ'), + (0x1EEF, 'V'), + (0x1EF0, 'M', 'ự'), + ] + +def _seg_19() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1EF1, 'V'), + (0x1EF2, 'M', 'ỳ'), + (0x1EF3, 'V'), + (0x1EF4, 'M', 'ỵ'), + (0x1EF5, 'V'), + (0x1EF6, 'M', 'ỷ'), + (0x1EF7, 'V'), + (0x1EF8, 'M', 'ỹ'), + (0x1EF9, 'V'), + (0x1EFA, 'M', 'ỻ'), + (0x1EFB, 'V'), + (0x1EFC, 'M', 'ỽ'), + (0x1EFD, 'V'), + (0x1EFE, 'M', 'ỿ'), + (0x1EFF, 'V'), + (0x1F08, 'M', 'ἀ'), + (0x1F09, 'M', 'ἁ'), + (0x1F0A, 'M', 'ἂ'), + (0x1F0B, 'M', 'ἃ'), + (0x1F0C, 'M', 'ἄ'), + (0x1F0D, 'M', 'ἅ'), + (0x1F0E, 'M', 'ἆ'), + (0x1F0F, 'M', 'ἇ'), + (0x1F10, 'V'), + (0x1F16, 'X'), + (0x1F18, 'M', 'ἐ'), + (0x1F19, 'M', 'ἑ'), + (0x1F1A, 'M', 'ἒ'), + (0x1F1B, 'M', 'ἓ'), + (0x1F1C, 'M', 'ἔ'), + (0x1F1D, 'M', 'ἕ'), + (0x1F1E, 'X'), + (0x1F20, 'V'), + (0x1F28, 'M', 'ἠ'), + (0x1F29, 'M', 'ἡ'), + (0x1F2A, 'M', 'ἢ'), + (0x1F2B, 'M', 'ἣ'), + (0x1F2C, 'M', 'ἤ'), + (0x1F2D, 'M', 'ἥ'), + (0x1F2E, 'M', 'ἦ'), + (0x1F2F, 'M', 'ἧ'), + (0x1F30, 'V'), + (0x1F38, 'M', 'ἰ'), + (0x1F39, 'M', 'ἱ'), + (0x1F3A, 'M', 'ἲ'), + (0x1F3B, 'M', 'ἳ'), + (0x1F3C, 'M', 'ἴ'), + (0x1F3D, 'M', 'ἵ'), + (0x1F3E, 'M', 'ἶ'), + (0x1F3F, 'M', 'ἷ'), + (0x1F40, 'V'), + (0x1F46, 'X'), + (0x1F48, 'M', 'ὀ'), + (0x1F49, 'M', 'ὁ'), + (0x1F4A, 'M', 'ὂ'), + (0x1F4B, 'M', 'ὃ'), + (0x1F4C, 'M', 'ὄ'), + (0x1F4D, 'M', 'ὅ'), + (0x1F4E, 'X'), + (0x1F50, 'V'), + (0x1F58, 'X'), + (0x1F59, 'M', 'ὑ'), + (0x1F5A, 'X'), + (0x1F5B, 'M', 'ὓ'), + (0x1F5C, 'X'), + (0x1F5D, 'M', 'ὕ'), + (0x1F5E, 'X'), + (0x1F5F, 'M', 'ὗ'), + (0x1F60, 'V'), + (0x1F68, 'M', 'ὠ'), + (0x1F69, 'M', 'ὡ'), + (0x1F6A, 'M', 'ὢ'), + (0x1F6B, 'M', 'ὣ'), + (0x1F6C, 'M', 'ὤ'), + (0x1F6D, 'M', 'ὥ'), + (0x1F6E, 'M', 'ὦ'), + (0x1F6F, 'M', 'ὧ'), + (0x1F70, 'V'), + (0x1F71, 'M', 'ά'), + (0x1F72, 'V'), + (0x1F73, 'M', 'έ'), + (0x1F74, 'V'), + (0x1F75, 'M', 'ή'), + (0x1F76, 'V'), + (0x1F77, 'M', 'ί'), + (0x1F78, 'V'), + (0x1F79, 'M', 'ό'), + (0x1F7A, 'V'), + (0x1F7B, 'M', 'ύ'), + (0x1F7C, 'V'), + (0x1F7D, 'M', 'ώ'), + (0x1F7E, 'X'), + (0x1F80, 'M', 'ἀι'), + (0x1F81, 'M', 'ἁι'), + (0x1F82, 'M', 'ἂι'), + (0x1F83, 'M', 'ἃι'), + (0x1F84, 'M', 'ἄι'), + (0x1F85, 'M', 'ἅι'), + (0x1F86, 'M', 'ἆι'), + (0x1F87, 'M', 'ἇι'), + ] + +def _seg_20() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1F88, 'M', 'ἀι'), + (0x1F89, 'M', 'ἁι'), + (0x1F8A, 'M', 'ἂι'), + (0x1F8B, 'M', 'ἃι'), + (0x1F8C, 'M', 'ἄι'), + (0x1F8D, 'M', 'ἅι'), + (0x1F8E, 'M', 'ἆι'), + (0x1F8F, 'M', 'ἇι'), + (0x1F90, 'M', 'ἠι'), + (0x1F91, 'M', 'ἡι'), + (0x1F92, 'M', 'ἢι'), + (0x1F93, 'M', 'ἣι'), + (0x1F94, 'M', 'ἤι'), + (0x1F95, 'M', 'ἥι'), + (0x1F96, 'M', 'ἦι'), + (0x1F97, 'M', 'ἧι'), + (0x1F98, 'M', 'ἠι'), + (0x1F99, 'M', 'ἡι'), + (0x1F9A, 'M', 'ἢι'), + (0x1F9B, 'M', 'ἣι'), + (0x1F9C, 'M', 'ἤι'), + (0x1F9D, 'M', 'ἥι'), + (0x1F9E, 'M', 'ἦι'), + (0x1F9F, 'M', 'ἧι'), + (0x1FA0, 'M', 'ὠι'), + (0x1FA1, 'M', 'ὡι'), + (0x1FA2, 'M', 'ὢι'), + (0x1FA3, 'M', 'ὣι'), + (0x1FA4, 'M', 'ὤι'), + (0x1FA5, 'M', 'ὥι'), + (0x1FA6, 'M', 'ὦι'), + (0x1FA7, 'M', 'ὧι'), + (0x1FA8, 'M', 'ὠι'), + (0x1FA9, 'M', 'ὡι'), + (0x1FAA, 'M', 'ὢι'), + (0x1FAB, 'M', 'ὣι'), + (0x1FAC, 'M', 'ὤι'), + (0x1FAD, 'M', 'ὥι'), + (0x1FAE, 'M', 'ὦι'), + (0x1FAF, 'M', 'ὧι'), + (0x1FB0, 'V'), + (0x1FB2, 'M', 'ὰι'), + (0x1FB3, 'M', 'αι'), + (0x1FB4, 'M', 'άι'), + (0x1FB5, 'X'), + (0x1FB6, 'V'), + (0x1FB7, 'M', 'ᾶι'), + (0x1FB8, 'M', 'ᾰ'), + (0x1FB9, 'M', 'ᾱ'), + (0x1FBA, 'M', 'ὰ'), + (0x1FBB, 'M', 'ά'), + (0x1FBC, 'M', 'αι'), + (0x1FBD, '3', ' ̓'), + (0x1FBE, 'M', 'ι'), + (0x1FBF, '3', ' ̓'), + (0x1FC0, '3', ' ͂'), + (0x1FC1, '3', ' ̈͂'), + (0x1FC2, 'M', 'ὴι'), + (0x1FC3, 'M', 'ηι'), + (0x1FC4, 'M', 'ήι'), + (0x1FC5, 'X'), + (0x1FC6, 'V'), + (0x1FC7, 'M', 'ῆι'), + (0x1FC8, 'M', 'ὲ'), + (0x1FC9, 'M', 'έ'), + (0x1FCA, 'M', 'ὴ'), + (0x1FCB, 'M', 'ή'), + (0x1FCC, 'M', 'ηι'), + (0x1FCD, '3', ' ̓̀'), + (0x1FCE, '3', ' ̓́'), + (0x1FCF, '3', ' ̓͂'), + (0x1FD0, 'V'), + (0x1FD3, 'M', 'ΐ'), + (0x1FD4, 'X'), + (0x1FD6, 'V'), + (0x1FD8, 'M', 'ῐ'), + (0x1FD9, 'M', 'ῑ'), + (0x1FDA, 'M', 'ὶ'), + (0x1FDB, 'M', 'ί'), + (0x1FDC, 'X'), + (0x1FDD, '3', ' ̔̀'), + (0x1FDE, '3', ' ̔́'), + (0x1FDF, '3', ' ̔͂'), + (0x1FE0, 'V'), + (0x1FE3, 'M', 'ΰ'), + (0x1FE4, 'V'), + (0x1FE8, 'M', 'ῠ'), + (0x1FE9, 'M', 'ῡ'), + (0x1FEA, 'M', 'ὺ'), + (0x1FEB, 'M', 'ύ'), + (0x1FEC, 'M', 'ῥ'), + (0x1FED, '3', ' ̈̀'), + (0x1FEE, '3', ' ̈́'), + (0x1FEF, '3', '`'), + (0x1FF0, 'X'), + (0x1FF2, 'M', 'ὼι'), + (0x1FF3, 'M', 'ωι'), + (0x1FF4, 'M', 'ώι'), + (0x1FF5, 'X'), + (0x1FF6, 'V'), + ] + +def _seg_21() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1FF7, 'M', 'ῶι'), + (0x1FF8, 'M', 'ὸ'), + (0x1FF9, 'M', 'ό'), + (0x1FFA, 'M', 'ὼ'), + (0x1FFB, 'M', 'ώ'), + (0x1FFC, 'M', 'ωι'), + (0x1FFD, '3', ' ́'), + (0x1FFE, '3', ' ̔'), + (0x1FFF, 'X'), + (0x2000, '3', ' '), + (0x200B, 'I'), + (0x200C, 'D', ''), + (0x200E, 'X'), + (0x2010, 'V'), + (0x2011, 'M', '‐'), + (0x2012, 'V'), + (0x2017, '3', ' ̳'), + (0x2018, 'V'), + (0x2024, 'X'), + (0x2027, 'V'), + (0x2028, 'X'), + (0x202F, '3', ' '), + (0x2030, 'V'), + (0x2033, 'M', '′′'), + (0x2034, 'M', '′′′'), + (0x2035, 'V'), + (0x2036, 'M', '‵‵'), + (0x2037, 'M', '‵‵‵'), + (0x2038, 'V'), + (0x203C, '3', '!!'), + (0x203D, 'V'), + (0x203E, '3', ' ̅'), + (0x203F, 'V'), + (0x2047, '3', '??'), + (0x2048, '3', '?!'), + (0x2049, '3', '!?'), + (0x204A, 'V'), + (0x2057, 'M', '′′′′'), + (0x2058, 'V'), + (0x205F, '3', ' '), + (0x2060, 'I'), + (0x2061, 'X'), + (0x2064, 'I'), + (0x2065, 'X'), + (0x2070, 'M', '0'), + (0x2071, 'M', 'i'), + (0x2072, 'X'), + (0x2074, 'M', '4'), + (0x2075, 'M', '5'), + (0x2076, 'M', '6'), + (0x2077, 'M', '7'), + (0x2078, 'M', '8'), + (0x2079, 'M', '9'), + (0x207A, '3', '+'), + (0x207B, 'M', '−'), + (0x207C, '3', '='), + (0x207D, '3', '('), + (0x207E, '3', ')'), + (0x207F, 'M', 'n'), + (0x2080, 'M', '0'), + (0x2081, 'M', '1'), + (0x2082, 'M', '2'), + (0x2083, 'M', '3'), + (0x2084, 'M', '4'), + (0x2085, 'M', '5'), + (0x2086, 'M', '6'), + (0x2087, 'M', '7'), + (0x2088, 'M', '8'), + (0x2089, 'M', '9'), + (0x208A, '3', '+'), + (0x208B, 'M', '−'), + (0x208C, '3', '='), + (0x208D, '3', '('), + (0x208E, '3', ')'), + (0x208F, 'X'), + (0x2090, 'M', 'a'), + (0x2091, 'M', 'e'), + (0x2092, 'M', 'o'), + (0x2093, 'M', 'x'), + (0x2094, 'M', 'ə'), + (0x2095, 'M', 'h'), + (0x2096, 'M', 'k'), + (0x2097, 'M', 'l'), + (0x2098, 'M', 'm'), + (0x2099, 'M', 'n'), + (0x209A, 'M', 'p'), + (0x209B, 'M', 's'), + (0x209C, 'M', 't'), + (0x209D, 'X'), + (0x20A0, 'V'), + (0x20A8, 'M', 'rs'), + (0x20A9, 'V'), + (0x20C1, 'X'), + (0x20D0, 'V'), + (0x20F1, 'X'), + (0x2100, '3', 'a/c'), + (0x2101, '3', 'a/s'), + (0x2102, 'M', 'c'), + (0x2103, 'M', '°c'), + (0x2104, 'V'), + ] + +def _seg_22() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2105, '3', 'c/o'), + (0x2106, '3', 'c/u'), + (0x2107, 'M', 'ɛ'), + (0x2108, 'V'), + (0x2109, 'M', '°f'), + (0x210A, 'M', 'g'), + (0x210B, 'M', 'h'), + (0x210F, 'M', 'ħ'), + (0x2110, 'M', 'i'), + (0x2112, 'M', 'l'), + (0x2114, 'V'), + (0x2115, 'M', 'n'), + (0x2116, 'M', 'no'), + (0x2117, 'V'), + (0x2119, 'M', 'p'), + (0x211A, 'M', 'q'), + (0x211B, 'M', 'r'), + (0x211E, 'V'), + (0x2120, 'M', 'sm'), + (0x2121, 'M', 'tel'), + (0x2122, 'M', 'tm'), + (0x2123, 'V'), + (0x2124, 'M', 'z'), + (0x2125, 'V'), + (0x2126, 'M', 'ω'), + (0x2127, 'V'), + (0x2128, 'M', 'z'), + (0x2129, 'V'), + (0x212A, 'M', 'k'), + (0x212B, 'M', 'å'), + (0x212C, 'M', 'b'), + (0x212D, 'M', 'c'), + (0x212E, 'V'), + (0x212F, 'M', 'e'), + (0x2131, 'M', 'f'), + (0x2132, 'X'), + (0x2133, 'M', 'm'), + (0x2134, 'M', 'o'), + (0x2135, 'M', 'א'), + (0x2136, 'M', 'ב'), + (0x2137, 'M', 'ג'), + (0x2138, 'M', 'ד'), + (0x2139, 'M', 'i'), + (0x213A, 'V'), + (0x213B, 'M', 'fax'), + (0x213C, 'M', 'π'), + (0x213D, 'M', 'γ'), + (0x213F, 'M', 'π'), + (0x2140, 'M', '∑'), + (0x2141, 'V'), + (0x2145, 'M', 'd'), + (0x2147, 'M', 'e'), + (0x2148, 'M', 'i'), + (0x2149, 'M', 'j'), + (0x214A, 'V'), + (0x2150, 'M', '1⁄7'), + (0x2151, 'M', '1⁄9'), + (0x2152, 'M', '1⁄10'), + (0x2153, 'M', '1⁄3'), + (0x2154, 'M', '2⁄3'), + (0x2155, 'M', '1⁄5'), + (0x2156, 'M', '2⁄5'), + (0x2157, 'M', '3⁄5'), + (0x2158, 'M', '4⁄5'), + (0x2159, 'M', '1⁄6'), + (0x215A, 'M', '5⁄6'), + (0x215B, 'M', '1⁄8'), + (0x215C, 'M', '3⁄8'), + (0x215D, 'M', '5⁄8'), + (0x215E, 'M', '7⁄8'), + (0x215F, 'M', '1⁄'), + (0x2160, 'M', 'i'), + (0x2161, 'M', 'ii'), + (0x2162, 'M', 'iii'), + (0x2163, 'M', 'iv'), + (0x2164, 'M', 'v'), + (0x2165, 'M', 'vi'), + (0x2166, 'M', 'vii'), + (0x2167, 'M', 'viii'), + (0x2168, 'M', 'ix'), + (0x2169, 'M', 'x'), + (0x216A, 'M', 'xi'), + (0x216B, 'M', 'xii'), + (0x216C, 'M', 'l'), + (0x216D, 'M', 'c'), + (0x216E, 'M', 'd'), + (0x216F, 'M', 'm'), + (0x2170, 'M', 'i'), + (0x2171, 'M', 'ii'), + (0x2172, 'M', 'iii'), + (0x2173, 'M', 'iv'), + (0x2174, 'M', 'v'), + (0x2175, 'M', 'vi'), + (0x2176, 'M', 'vii'), + (0x2177, 'M', 'viii'), + (0x2178, 'M', 'ix'), + (0x2179, 'M', 'x'), + (0x217A, 'M', 'xi'), + (0x217B, 'M', 'xii'), + (0x217C, 'M', 'l'), + ] + +def _seg_23() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x217D, 'M', 'c'), + (0x217E, 'M', 'd'), + (0x217F, 'M', 'm'), + (0x2180, 'V'), + (0x2183, 'X'), + (0x2184, 'V'), + (0x2189, 'M', '0⁄3'), + (0x218A, 'V'), + (0x218C, 'X'), + (0x2190, 'V'), + (0x222C, 'M', '∫∫'), + (0x222D, 'M', '∫∫∫'), + (0x222E, 'V'), + (0x222F, 'M', '∮∮'), + (0x2230, 'M', '∮∮∮'), + (0x2231, 'V'), + (0x2260, '3'), + (0x2261, 'V'), + (0x226E, '3'), + (0x2270, 'V'), + (0x2329, 'M', '〈'), + (0x232A, 'M', '〉'), + (0x232B, 'V'), + (0x2427, 'X'), + (0x2440, 'V'), + (0x244B, 'X'), + (0x2460, 'M', '1'), + (0x2461, 'M', '2'), + (0x2462, 'M', '3'), + (0x2463, 'M', '4'), + (0x2464, 'M', '5'), + (0x2465, 'M', '6'), + (0x2466, 'M', '7'), + (0x2467, 'M', '8'), + (0x2468, 'M', '9'), + (0x2469, 'M', '10'), + (0x246A, 'M', '11'), + (0x246B, 'M', '12'), + (0x246C, 'M', '13'), + (0x246D, 'M', '14'), + (0x246E, 'M', '15'), + (0x246F, 'M', '16'), + (0x2470, 'M', '17'), + (0x2471, 'M', '18'), + (0x2472, 'M', '19'), + (0x2473, 'M', '20'), + (0x2474, '3', '(1)'), + (0x2475, '3', '(2)'), + (0x2476, '3', '(3)'), + (0x2477, '3', '(4)'), + (0x2478, '3', '(5)'), + (0x2479, '3', '(6)'), + (0x247A, '3', '(7)'), + (0x247B, '3', '(8)'), + (0x247C, '3', '(9)'), + (0x247D, '3', '(10)'), + (0x247E, '3', '(11)'), + (0x247F, '3', '(12)'), + (0x2480, '3', '(13)'), + (0x2481, '3', '(14)'), + (0x2482, '3', '(15)'), + (0x2483, '3', '(16)'), + (0x2484, '3', '(17)'), + (0x2485, '3', '(18)'), + (0x2486, '3', '(19)'), + (0x2487, '3', '(20)'), + (0x2488, 'X'), + (0x249C, '3', '(a)'), + (0x249D, '3', '(b)'), + (0x249E, '3', '(c)'), + (0x249F, '3', '(d)'), + (0x24A0, '3', '(e)'), + (0x24A1, '3', '(f)'), + (0x24A2, '3', '(g)'), + (0x24A3, '3', '(h)'), + (0x24A4, '3', '(i)'), + (0x24A5, '3', '(j)'), + (0x24A6, '3', '(k)'), + (0x24A7, '3', '(l)'), + (0x24A8, '3', '(m)'), + (0x24A9, '3', '(n)'), + (0x24AA, '3', '(o)'), + (0x24AB, '3', '(p)'), + (0x24AC, '3', '(q)'), + (0x24AD, '3', '(r)'), + (0x24AE, '3', '(s)'), + (0x24AF, '3', '(t)'), + (0x24B0, '3', '(u)'), + (0x24B1, '3', '(v)'), + (0x24B2, '3', '(w)'), + (0x24B3, '3', '(x)'), + (0x24B4, '3', '(y)'), + (0x24B5, '3', '(z)'), + (0x24B6, 'M', 'a'), + (0x24B7, 'M', 'b'), + (0x24B8, 'M', 'c'), + (0x24B9, 'M', 'd'), + (0x24BA, 'M', 'e'), + (0x24BB, 'M', 'f'), + (0x24BC, 'M', 'g'), + ] + +def _seg_24() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x24BD, 'M', 'h'), + (0x24BE, 'M', 'i'), + (0x24BF, 'M', 'j'), + (0x24C0, 'M', 'k'), + (0x24C1, 'M', 'l'), + (0x24C2, 'M', 'm'), + (0x24C3, 'M', 'n'), + (0x24C4, 'M', 'o'), + (0x24C5, 'M', 'p'), + (0x24C6, 'M', 'q'), + (0x24C7, 'M', 'r'), + (0x24C8, 'M', 's'), + (0x24C9, 'M', 't'), + (0x24CA, 'M', 'u'), + (0x24CB, 'M', 'v'), + (0x24CC, 'M', 'w'), + (0x24CD, 'M', 'x'), + (0x24CE, 'M', 'y'), + (0x24CF, 'M', 'z'), + (0x24D0, 'M', 'a'), + (0x24D1, 'M', 'b'), + (0x24D2, 'M', 'c'), + (0x24D3, 'M', 'd'), + (0x24D4, 'M', 'e'), + (0x24D5, 'M', 'f'), + (0x24D6, 'M', 'g'), + (0x24D7, 'M', 'h'), + (0x24D8, 'M', 'i'), + (0x24D9, 'M', 'j'), + (0x24DA, 'M', 'k'), + (0x24DB, 'M', 'l'), + (0x24DC, 'M', 'm'), + (0x24DD, 'M', 'n'), + (0x24DE, 'M', 'o'), + (0x24DF, 'M', 'p'), + (0x24E0, 'M', 'q'), + (0x24E1, 'M', 'r'), + (0x24E2, 'M', 's'), + (0x24E3, 'M', 't'), + (0x24E4, 'M', 'u'), + (0x24E5, 'M', 'v'), + (0x24E6, 'M', 'w'), + (0x24E7, 'M', 'x'), + (0x24E8, 'M', 'y'), + (0x24E9, 'M', 'z'), + (0x24EA, 'M', '0'), + (0x24EB, 'V'), + (0x2A0C, 'M', '∫∫∫∫'), + (0x2A0D, 'V'), + (0x2A74, '3', '::='), + (0x2A75, '3', '=='), + (0x2A76, '3', '==='), + (0x2A77, 'V'), + (0x2ADC, 'M', '⫝̸'), + (0x2ADD, 'V'), + (0x2B74, 'X'), + (0x2B76, 'V'), + (0x2B96, 'X'), + (0x2B97, 'V'), + (0x2C00, 'M', 'ⰰ'), + (0x2C01, 'M', 'ⰱ'), + (0x2C02, 'M', 'ⰲ'), + (0x2C03, 'M', 'ⰳ'), + (0x2C04, 'M', 'ⰴ'), + (0x2C05, 'M', 'ⰵ'), + (0x2C06, 'M', 'ⰶ'), + (0x2C07, 'M', 'ⰷ'), + (0x2C08, 'M', 'ⰸ'), + (0x2C09, 'M', 'ⰹ'), + (0x2C0A, 'M', 'ⰺ'), + (0x2C0B, 'M', 'ⰻ'), + (0x2C0C, 'M', 'ⰼ'), + (0x2C0D, 'M', 'ⰽ'), + (0x2C0E, 'M', 'ⰾ'), + (0x2C0F, 'M', 'ⰿ'), + (0x2C10, 'M', 'ⱀ'), + (0x2C11, 'M', 'ⱁ'), + (0x2C12, 'M', 'ⱂ'), + (0x2C13, 'M', 'ⱃ'), + (0x2C14, 'M', 'ⱄ'), + (0x2C15, 'M', 'ⱅ'), + (0x2C16, 'M', 'ⱆ'), + (0x2C17, 'M', 'ⱇ'), + (0x2C18, 'M', 'ⱈ'), + (0x2C19, 'M', 'ⱉ'), + (0x2C1A, 'M', 'ⱊ'), + (0x2C1B, 'M', 'ⱋ'), + (0x2C1C, 'M', 'ⱌ'), + (0x2C1D, 'M', 'ⱍ'), + (0x2C1E, 'M', 'ⱎ'), + (0x2C1F, 'M', 'ⱏ'), + (0x2C20, 'M', 'ⱐ'), + (0x2C21, 'M', 'ⱑ'), + (0x2C22, 'M', 'ⱒ'), + (0x2C23, 'M', 'ⱓ'), + (0x2C24, 'M', 'ⱔ'), + (0x2C25, 'M', 'ⱕ'), + (0x2C26, 'M', 'ⱖ'), + (0x2C27, 'M', 'ⱗ'), + (0x2C28, 'M', 'ⱘ'), + ] + +def _seg_25() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2C29, 'M', 'ⱙ'), + (0x2C2A, 'M', 'ⱚ'), + (0x2C2B, 'M', 'ⱛ'), + (0x2C2C, 'M', 'ⱜ'), + (0x2C2D, 'M', 'ⱝ'), + (0x2C2E, 'M', 'ⱞ'), + (0x2C2F, 'M', 'ⱟ'), + (0x2C30, 'V'), + (0x2C60, 'M', 'ⱡ'), + (0x2C61, 'V'), + (0x2C62, 'M', 'ɫ'), + (0x2C63, 'M', 'ᵽ'), + (0x2C64, 'M', 'ɽ'), + (0x2C65, 'V'), + (0x2C67, 'M', 'ⱨ'), + (0x2C68, 'V'), + (0x2C69, 'M', 'ⱪ'), + (0x2C6A, 'V'), + (0x2C6B, 'M', 'ⱬ'), + (0x2C6C, 'V'), + (0x2C6D, 'M', 'ɑ'), + (0x2C6E, 'M', 'ɱ'), + (0x2C6F, 'M', 'ɐ'), + (0x2C70, 'M', 'ɒ'), + (0x2C71, 'V'), + (0x2C72, 'M', 'ⱳ'), + (0x2C73, 'V'), + (0x2C75, 'M', 'ⱶ'), + (0x2C76, 'V'), + (0x2C7C, 'M', 'j'), + (0x2C7D, 'M', 'v'), + (0x2C7E, 'M', 'ȿ'), + (0x2C7F, 'M', 'ɀ'), + (0x2C80, 'M', 'ⲁ'), + (0x2C81, 'V'), + (0x2C82, 'M', 'ⲃ'), + (0x2C83, 'V'), + (0x2C84, 'M', 'ⲅ'), + (0x2C85, 'V'), + (0x2C86, 'M', 'ⲇ'), + (0x2C87, 'V'), + (0x2C88, 'M', 'ⲉ'), + (0x2C89, 'V'), + (0x2C8A, 'M', 'ⲋ'), + (0x2C8B, 'V'), + (0x2C8C, 'M', 'ⲍ'), + (0x2C8D, 'V'), + (0x2C8E, 'M', 'ⲏ'), + (0x2C8F, 'V'), + (0x2C90, 'M', 'ⲑ'), + (0x2C91, 'V'), + (0x2C92, 'M', 'ⲓ'), + (0x2C93, 'V'), + (0x2C94, 'M', 'ⲕ'), + (0x2C95, 'V'), + (0x2C96, 'M', 'ⲗ'), + (0x2C97, 'V'), + (0x2C98, 'M', 'ⲙ'), + (0x2C99, 'V'), + (0x2C9A, 'M', 'ⲛ'), + (0x2C9B, 'V'), + (0x2C9C, 'M', 'ⲝ'), + (0x2C9D, 'V'), + (0x2C9E, 'M', 'ⲟ'), + (0x2C9F, 'V'), + (0x2CA0, 'M', 'ⲡ'), + (0x2CA1, 'V'), + (0x2CA2, 'M', 'ⲣ'), + (0x2CA3, 'V'), + (0x2CA4, 'M', 'ⲥ'), + (0x2CA5, 'V'), + (0x2CA6, 'M', 'ⲧ'), + (0x2CA7, 'V'), + (0x2CA8, 'M', 'ⲩ'), + (0x2CA9, 'V'), + (0x2CAA, 'M', 'ⲫ'), + (0x2CAB, 'V'), + (0x2CAC, 'M', 'ⲭ'), + (0x2CAD, 'V'), + (0x2CAE, 'M', 'ⲯ'), + (0x2CAF, 'V'), + (0x2CB0, 'M', 'ⲱ'), + (0x2CB1, 'V'), + (0x2CB2, 'M', 'ⲳ'), + (0x2CB3, 'V'), + (0x2CB4, 'M', 'ⲵ'), + (0x2CB5, 'V'), + (0x2CB6, 'M', 'ⲷ'), + (0x2CB7, 'V'), + (0x2CB8, 'M', 'ⲹ'), + (0x2CB9, 'V'), + (0x2CBA, 'M', 'ⲻ'), + (0x2CBB, 'V'), + (0x2CBC, 'M', 'ⲽ'), + (0x2CBD, 'V'), + (0x2CBE, 'M', 'ⲿ'), + (0x2CBF, 'V'), + (0x2CC0, 'M', 'ⳁ'), + (0x2CC1, 'V'), + (0x2CC2, 'M', 'ⳃ'), + ] + +def _seg_26() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2CC3, 'V'), + (0x2CC4, 'M', 'ⳅ'), + (0x2CC5, 'V'), + (0x2CC6, 'M', 'ⳇ'), + (0x2CC7, 'V'), + (0x2CC8, 'M', 'ⳉ'), + (0x2CC9, 'V'), + (0x2CCA, 'M', 'ⳋ'), + (0x2CCB, 'V'), + (0x2CCC, 'M', 'ⳍ'), + (0x2CCD, 'V'), + (0x2CCE, 'M', 'ⳏ'), + (0x2CCF, 'V'), + (0x2CD0, 'M', 'ⳑ'), + (0x2CD1, 'V'), + (0x2CD2, 'M', 'ⳓ'), + (0x2CD3, 'V'), + (0x2CD4, 'M', 'ⳕ'), + (0x2CD5, 'V'), + (0x2CD6, 'M', 'ⳗ'), + (0x2CD7, 'V'), + (0x2CD8, 'M', 'ⳙ'), + (0x2CD9, 'V'), + (0x2CDA, 'M', 'ⳛ'), + (0x2CDB, 'V'), + (0x2CDC, 'M', 'ⳝ'), + (0x2CDD, 'V'), + (0x2CDE, 'M', 'ⳟ'), + (0x2CDF, 'V'), + (0x2CE0, 'M', 'ⳡ'), + (0x2CE1, 'V'), + (0x2CE2, 'M', 'ⳣ'), + (0x2CE3, 'V'), + (0x2CEB, 'M', 'ⳬ'), + (0x2CEC, 'V'), + (0x2CED, 'M', 'ⳮ'), + (0x2CEE, 'V'), + (0x2CF2, 'M', 'ⳳ'), + (0x2CF3, 'V'), + (0x2CF4, 'X'), + (0x2CF9, 'V'), + (0x2D26, 'X'), + (0x2D27, 'V'), + (0x2D28, 'X'), + (0x2D2D, 'V'), + (0x2D2E, 'X'), + (0x2D30, 'V'), + (0x2D68, 'X'), + (0x2D6F, 'M', 'ⵡ'), + (0x2D70, 'V'), + (0x2D71, 'X'), + (0x2D7F, 'V'), + (0x2D97, 'X'), + (0x2DA0, 'V'), + (0x2DA7, 'X'), + (0x2DA8, 'V'), + (0x2DAF, 'X'), + (0x2DB0, 'V'), + (0x2DB7, 'X'), + (0x2DB8, 'V'), + (0x2DBF, 'X'), + (0x2DC0, 'V'), + (0x2DC7, 'X'), + (0x2DC8, 'V'), + (0x2DCF, 'X'), + (0x2DD0, 'V'), + (0x2DD7, 'X'), + (0x2DD8, 'V'), + (0x2DDF, 'X'), + (0x2DE0, 'V'), + (0x2E5E, 'X'), + (0x2E80, 'V'), + (0x2E9A, 'X'), + (0x2E9B, 'V'), + (0x2E9F, 'M', '母'), + (0x2EA0, 'V'), + (0x2EF3, 'M', '龟'), + (0x2EF4, 'X'), + (0x2F00, 'M', '一'), + (0x2F01, 'M', '丨'), + (0x2F02, 'M', '丶'), + (0x2F03, 'M', '丿'), + (0x2F04, 'M', '乙'), + (0x2F05, 'M', '亅'), + (0x2F06, 'M', '二'), + (0x2F07, 'M', '亠'), + (0x2F08, 'M', '人'), + (0x2F09, 'M', '儿'), + (0x2F0A, 'M', '入'), + (0x2F0B, 'M', '八'), + (0x2F0C, 'M', '冂'), + (0x2F0D, 'M', '冖'), + (0x2F0E, 'M', '冫'), + (0x2F0F, 'M', '几'), + (0x2F10, 'M', '凵'), + (0x2F11, 'M', '刀'), + (0x2F12, 'M', '力'), + (0x2F13, 'M', '勹'), + (0x2F14, 'M', '匕'), + (0x2F15, 'M', '匚'), + ] + +def _seg_27() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F16, 'M', '匸'), + (0x2F17, 'M', '十'), + (0x2F18, 'M', '卜'), + (0x2F19, 'M', '卩'), + (0x2F1A, 'M', '厂'), + (0x2F1B, 'M', '厶'), + (0x2F1C, 'M', '又'), + (0x2F1D, 'M', '口'), + (0x2F1E, 'M', '囗'), + (0x2F1F, 'M', '土'), + (0x2F20, 'M', '士'), + (0x2F21, 'M', '夂'), + (0x2F22, 'M', '夊'), + (0x2F23, 'M', '夕'), + (0x2F24, 'M', '大'), + (0x2F25, 'M', '女'), + (0x2F26, 'M', '子'), + (0x2F27, 'M', '宀'), + (0x2F28, 'M', '寸'), + (0x2F29, 'M', '小'), + (0x2F2A, 'M', '尢'), + (0x2F2B, 'M', '尸'), + (0x2F2C, 'M', '屮'), + (0x2F2D, 'M', '山'), + (0x2F2E, 'M', '巛'), + (0x2F2F, 'M', '工'), + (0x2F30, 'M', '己'), + (0x2F31, 'M', '巾'), + (0x2F32, 'M', '干'), + (0x2F33, 'M', '幺'), + (0x2F34, 'M', '广'), + (0x2F35, 'M', '廴'), + (0x2F36, 'M', '廾'), + (0x2F37, 'M', '弋'), + (0x2F38, 'M', '弓'), + (0x2F39, 'M', '彐'), + (0x2F3A, 'M', '彡'), + (0x2F3B, 'M', '彳'), + (0x2F3C, 'M', '心'), + (0x2F3D, 'M', '戈'), + (0x2F3E, 'M', '戶'), + (0x2F3F, 'M', '手'), + (0x2F40, 'M', '支'), + (0x2F41, 'M', '攴'), + (0x2F42, 'M', '文'), + (0x2F43, 'M', '斗'), + (0x2F44, 'M', '斤'), + (0x2F45, 'M', '方'), + (0x2F46, 'M', '无'), + (0x2F47, 'M', '日'), + (0x2F48, 'M', '曰'), + (0x2F49, 'M', '月'), + (0x2F4A, 'M', '木'), + (0x2F4B, 'M', '欠'), + (0x2F4C, 'M', '止'), + (0x2F4D, 'M', '歹'), + (0x2F4E, 'M', '殳'), + (0x2F4F, 'M', '毋'), + (0x2F50, 'M', '比'), + (0x2F51, 'M', '毛'), + (0x2F52, 'M', '氏'), + (0x2F53, 'M', '气'), + (0x2F54, 'M', '水'), + (0x2F55, 'M', '火'), + (0x2F56, 'M', '爪'), + (0x2F57, 'M', '父'), + (0x2F58, 'M', '爻'), + (0x2F59, 'M', '爿'), + (0x2F5A, 'M', '片'), + (0x2F5B, 'M', '牙'), + (0x2F5C, 'M', '牛'), + (0x2F5D, 'M', '犬'), + (0x2F5E, 'M', '玄'), + (0x2F5F, 'M', '玉'), + (0x2F60, 'M', '瓜'), + (0x2F61, 'M', '瓦'), + (0x2F62, 'M', '甘'), + (0x2F63, 'M', '生'), + (0x2F64, 'M', '用'), + (0x2F65, 'M', '田'), + (0x2F66, 'M', '疋'), + (0x2F67, 'M', '疒'), + (0x2F68, 'M', '癶'), + (0x2F69, 'M', '白'), + (0x2F6A, 'M', '皮'), + (0x2F6B, 'M', '皿'), + (0x2F6C, 'M', '目'), + (0x2F6D, 'M', '矛'), + (0x2F6E, 'M', '矢'), + (0x2F6F, 'M', '石'), + (0x2F70, 'M', '示'), + (0x2F71, 'M', '禸'), + (0x2F72, 'M', '禾'), + (0x2F73, 'M', '穴'), + (0x2F74, 'M', '立'), + (0x2F75, 'M', '竹'), + (0x2F76, 'M', '米'), + (0x2F77, 'M', '糸'), + (0x2F78, 'M', '缶'), + (0x2F79, 'M', '网'), + ] + +def _seg_28() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F7A, 'M', '羊'), + (0x2F7B, 'M', '羽'), + (0x2F7C, 'M', '老'), + (0x2F7D, 'M', '而'), + (0x2F7E, 'M', '耒'), + (0x2F7F, 'M', '耳'), + (0x2F80, 'M', '聿'), + (0x2F81, 'M', '肉'), + (0x2F82, 'M', '臣'), + (0x2F83, 'M', '自'), + (0x2F84, 'M', '至'), + (0x2F85, 'M', '臼'), + (0x2F86, 'M', '舌'), + (0x2F87, 'M', '舛'), + (0x2F88, 'M', '舟'), + (0x2F89, 'M', '艮'), + (0x2F8A, 'M', '色'), + (0x2F8B, 'M', '艸'), + (0x2F8C, 'M', '虍'), + (0x2F8D, 'M', '虫'), + (0x2F8E, 'M', '血'), + (0x2F8F, 'M', '行'), + (0x2F90, 'M', '衣'), + (0x2F91, 'M', '襾'), + (0x2F92, 'M', '見'), + (0x2F93, 'M', '角'), + (0x2F94, 'M', '言'), + (0x2F95, 'M', '谷'), + (0x2F96, 'M', '豆'), + (0x2F97, 'M', '豕'), + (0x2F98, 'M', '豸'), + (0x2F99, 'M', '貝'), + (0x2F9A, 'M', '赤'), + (0x2F9B, 'M', '走'), + (0x2F9C, 'M', '足'), + (0x2F9D, 'M', '身'), + (0x2F9E, 'M', '車'), + (0x2F9F, 'M', '辛'), + (0x2FA0, 'M', '辰'), + (0x2FA1, 'M', '辵'), + (0x2FA2, 'M', '邑'), + (0x2FA3, 'M', '酉'), + (0x2FA4, 'M', '釆'), + (0x2FA5, 'M', '里'), + (0x2FA6, 'M', '金'), + (0x2FA7, 'M', '長'), + (0x2FA8, 'M', '門'), + (0x2FA9, 'M', '阜'), + (0x2FAA, 'M', '隶'), + (0x2FAB, 'M', '隹'), + (0x2FAC, 'M', '雨'), + (0x2FAD, 'M', '靑'), + (0x2FAE, 'M', '非'), + (0x2FAF, 'M', '面'), + (0x2FB0, 'M', '革'), + (0x2FB1, 'M', '韋'), + (0x2FB2, 'M', '韭'), + (0x2FB3, 'M', '音'), + (0x2FB4, 'M', '頁'), + (0x2FB5, 'M', '風'), + (0x2FB6, 'M', '飛'), + (0x2FB7, 'M', '食'), + (0x2FB8, 'M', '首'), + (0x2FB9, 'M', '香'), + (0x2FBA, 'M', '馬'), + (0x2FBB, 'M', '骨'), + (0x2FBC, 'M', '高'), + (0x2FBD, 'M', '髟'), + (0x2FBE, 'M', '鬥'), + (0x2FBF, 'M', '鬯'), + (0x2FC0, 'M', '鬲'), + (0x2FC1, 'M', '鬼'), + (0x2FC2, 'M', '魚'), + (0x2FC3, 'M', '鳥'), + (0x2FC4, 'M', '鹵'), + (0x2FC5, 'M', '鹿'), + (0x2FC6, 'M', '麥'), + (0x2FC7, 'M', '麻'), + (0x2FC8, 'M', '黃'), + (0x2FC9, 'M', '黍'), + (0x2FCA, 'M', '黑'), + (0x2FCB, 'M', '黹'), + (0x2FCC, 'M', '黽'), + (0x2FCD, 'M', '鼎'), + (0x2FCE, 'M', '鼓'), + (0x2FCF, 'M', '鼠'), + (0x2FD0, 'M', '鼻'), + (0x2FD1, 'M', '齊'), + (0x2FD2, 'M', '齒'), + (0x2FD3, 'M', '龍'), + (0x2FD4, 'M', '龜'), + (0x2FD5, 'M', '龠'), + (0x2FD6, 'X'), + (0x3000, '3', ' '), + (0x3001, 'V'), + (0x3002, 'M', '.'), + (0x3003, 'V'), + (0x3036, 'M', '〒'), + (0x3037, 'V'), + (0x3038, 'M', '十'), + ] + +def _seg_29() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x3039, 'M', '卄'), + (0x303A, 'M', '卅'), + (0x303B, 'V'), + (0x3040, 'X'), + (0x3041, 'V'), + (0x3097, 'X'), + (0x3099, 'V'), + (0x309B, '3', ' ゙'), + (0x309C, '3', ' ゚'), + (0x309D, 'V'), + (0x309F, 'M', 'より'), + (0x30A0, 'V'), + (0x30FF, 'M', 'コト'), + (0x3100, 'X'), + (0x3105, 'V'), + (0x3130, 'X'), + (0x3131, 'M', 'ᄀ'), + (0x3132, 'M', 'ᄁ'), + (0x3133, 'M', 'ᆪ'), + (0x3134, 'M', 'ᄂ'), + (0x3135, 'M', 'ᆬ'), + (0x3136, 'M', 'ᆭ'), + (0x3137, 'M', 'ᄃ'), + (0x3138, 'M', 'ᄄ'), + (0x3139, 'M', 'ᄅ'), + (0x313A, 'M', 'ᆰ'), + (0x313B, 'M', 'ᆱ'), + (0x313C, 'M', 'ᆲ'), + (0x313D, 'M', 'ᆳ'), + (0x313E, 'M', 'ᆴ'), + (0x313F, 'M', 'ᆵ'), + (0x3140, 'M', 'ᄚ'), + (0x3141, 'M', 'ᄆ'), + (0x3142, 'M', 'ᄇ'), + (0x3143, 'M', 'ᄈ'), + (0x3144, 'M', 'ᄡ'), + (0x3145, 'M', 'ᄉ'), + (0x3146, 'M', 'ᄊ'), + (0x3147, 'M', 'ᄋ'), + (0x3148, 'M', 'ᄌ'), + (0x3149, 'M', 'ᄍ'), + (0x314A, 'M', 'ᄎ'), + (0x314B, 'M', 'ᄏ'), + (0x314C, 'M', 'ᄐ'), + (0x314D, 'M', 'ᄑ'), + (0x314E, 'M', 'ᄒ'), + (0x314F, 'M', 'ᅡ'), + (0x3150, 'M', 'ᅢ'), + (0x3151, 'M', 'ᅣ'), + (0x3152, 'M', 'ᅤ'), + (0x3153, 'M', 'ᅥ'), + (0x3154, 'M', 'ᅦ'), + (0x3155, 'M', 'ᅧ'), + (0x3156, 'M', 'ᅨ'), + (0x3157, 'M', 'ᅩ'), + (0x3158, 'M', 'ᅪ'), + (0x3159, 'M', 'ᅫ'), + (0x315A, 'M', 'ᅬ'), + (0x315B, 'M', 'ᅭ'), + (0x315C, 'M', 'ᅮ'), + (0x315D, 'M', 'ᅯ'), + (0x315E, 'M', 'ᅰ'), + (0x315F, 'M', 'ᅱ'), + (0x3160, 'M', 'ᅲ'), + (0x3161, 'M', 'ᅳ'), + (0x3162, 'M', 'ᅴ'), + (0x3163, 'M', 'ᅵ'), + (0x3164, 'X'), + (0x3165, 'M', 'ᄔ'), + (0x3166, 'M', 'ᄕ'), + (0x3167, 'M', 'ᇇ'), + (0x3168, 'M', 'ᇈ'), + (0x3169, 'M', 'ᇌ'), + (0x316A, 'M', 'ᇎ'), + (0x316B, 'M', 'ᇓ'), + (0x316C, 'M', 'ᇗ'), + (0x316D, 'M', 'ᇙ'), + (0x316E, 'M', 'ᄜ'), + (0x316F, 'M', 'ᇝ'), + (0x3170, 'M', 'ᇟ'), + (0x3171, 'M', 'ᄝ'), + (0x3172, 'M', 'ᄞ'), + (0x3173, 'M', 'ᄠ'), + (0x3174, 'M', 'ᄢ'), + (0x3175, 'M', 'ᄣ'), + (0x3176, 'M', 'ᄧ'), + (0x3177, 'M', 'ᄩ'), + (0x3178, 'M', 'ᄫ'), + (0x3179, 'M', 'ᄬ'), + (0x317A, 'M', 'ᄭ'), + (0x317B, 'M', 'ᄮ'), + (0x317C, 'M', 'ᄯ'), + (0x317D, 'M', 'ᄲ'), + (0x317E, 'M', 'ᄶ'), + (0x317F, 'M', 'ᅀ'), + (0x3180, 'M', 'ᅇ'), + (0x3181, 'M', 'ᅌ'), + (0x3182, 'M', 'ᇱ'), + (0x3183, 'M', 'ᇲ'), + (0x3184, 'M', 'ᅗ'), + ] + +def _seg_30() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x3185, 'M', 'ᅘ'), + (0x3186, 'M', 'ᅙ'), + (0x3187, 'M', 'ᆄ'), + (0x3188, 'M', 'ᆅ'), + (0x3189, 'M', 'ᆈ'), + (0x318A, 'M', 'ᆑ'), + (0x318B, 'M', 'ᆒ'), + (0x318C, 'M', 'ᆔ'), + (0x318D, 'M', 'ᆞ'), + (0x318E, 'M', 'ᆡ'), + (0x318F, 'X'), + (0x3190, 'V'), + (0x3192, 'M', '一'), + (0x3193, 'M', '二'), + (0x3194, 'M', '三'), + (0x3195, 'M', '四'), + (0x3196, 'M', '上'), + (0x3197, 'M', '中'), + (0x3198, 'M', '下'), + (0x3199, 'M', '甲'), + (0x319A, 'M', '乙'), + (0x319B, 'M', '丙'), + (0x319C, 'M', '丁'), + (0x319D, 'M', '天'), + (0x319E, 'M', '地'), + (0x319F, 'M', '人'), + (0x31A0, 'V'), + (0x31E4, 'X'), + (0x31F0, 'V'), + (0x3200, '3', '(ᄀ)'), + (0x3201, '3', '(ᄂ)'), + (0x3202, '3', '(ᄃ)'), + (0x3203, '3', '(ᄅ)'), + (0x3204, '3', '(ᄆ)'), + (0x3205, '3', '(ᄇ)'), + (0x3206, '3', '(ᄉ)'), + (0x3207, '3', '(ᄋ)'), + (0x3208, '3', '(ᄌ)'), + (0x3209, '3', '(ᄎ)'), + (0x320A, '3', '(ᄏ)'), + (0x320B, '3', '(ᄐ)'), + (0x320C, '3', '(ᄑ)'), + (0x320D, '3', '(ᄒ)'), + (0x320E, '3', '(가)'), + (0x320F, '3', '(나)'), + (0x3210, '3', '(다)'), + (0x3211, '3', '(라)'), + (0x3212, '3', '(마)'), + (0x3213, '3', '(바)'), + (0x3214, '3', '(사)'), + (0x3215, '3', '(아)'), + (0x3216, '3', '(자)'), + (0x3217, '3', '(차)'), + (0x3218, '3', '(카)'), + (0x3219, '3', '(타)'), + (0x321A, '3', '(파)'), + (0x321B, '3', '(하)'), + (0x321C, '3', '(주)'), + (0x321D, '3', '(오전)'), + (0x321E, '3', '(오후)'), + (0x321F, 'X'), + (0x3220, '3', '(一)'), + (0x3221, '3', '(二)'), + (0x3222, '3', '(三)'), + (0x3223, '3', '(四)'), + (0x3224, '3', '(五)'), + (0x3225, '3', '(六)'), + (0x3226, '3', '(七)'), + (0x3227, '3', '(八)'), + (0x3228, '3', '(九)'), + (0x3229, '3', '(十)'), + (0x322A, '3', '(月)'), + (0x322B, '3', '(火)'), + (0x322C, '3', '(水)'), + (0x322D, '3', '(木)'), + (0x322E, '3', '(金)'), + (0x322F, '3', '(土)'), + (0x3230, '3', '(日)'), + (0x3231, '3', '(株)'), + (0x3232, '3', '(有)'), + (0x3233, '3', '(社)'), + (0x3234, '3', '(名)'), + (0x3235, '3', '(特)'), + (0x3236, '3', '(財)'), + (0x3237, '3', '(祝)'), + (0x3238, '3', '(労)'), + (0x3239, '3', '(代)'), + (0x323A, '3', '(呼)'), + (0x323B, '3', '(学)'), + (0x323C, '3', '(監)'), + (0x323D, '3', '(企)'), + (0x323E, '3', '(資)'), + (0x323F, '3', '(協)'), + (0x3240, '3', '(祭)'), + (0x3241, '3', '(休)'), + (0x3242, '3', '(自)'), + (0x3243, '3', '(至)'), + (0x3244, 'M', '問'), + (0x3245, 'M', '幼'), + (0x3246, 'M', '文'), + ] + +def _seg_31() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x3247, 'M', '箏'), + (0x3248, 'V'), + (0x3250, 'M', 'pte'), + (0x3251, 'M', '21'), + (0x3252, 'M', '22'), + (0x3253, 'M', '23'), + (0x3254, 'M', '24'), + (0x3255, 'M', '25'), + (0x3256, 'M', '26'), + (0x3257, 'M', '27'), + (0x3258, 'M', '28'), + (0x3259, 'M', '29'), + (0x325A, 'M', '30'), + (0x325B, 'M', '31'), + (0x325C, 'M', '32'), + (0x325D, 'M', '33'), + (0x325E, 'M', '34'), + (0x325F, 'M', '35'), + (0x3260, 'M', 'ᄀ'), + (0x3261, 'M', 'ᄂ'), + (0x3262, 'M', 'ᄃ'), + (0x3263, 'M', 'ᄅ'), + (0x3264, 'M', 'ᄆ'), + (0x3265, 'M', 'ᄇ'), + (0x3266, 'M', 'ᄉ'), + (0x3267, 'M', 'ᄋ'), + (0x3268, 'M', 'ᄌ'), + (0x3269, 'M', 'ᄎ'), + (0x326A, 'M', 'ᄏ'), + (0x326B, 'M', 'ᄐ'), + (0x326C, 'M', 'ᄑ'), + (0x326D, 'M', 'ᄒ'), + (0x326E, 'M', '가'), + (0x326F, 'M', '나'), + (0x3270, 'M', '다'), + (0x3271, 'M', '라'), + (0x3272, 'M', '마'), + (0x3273, 'M', '바'), + (0x3274, 'M', '사'), + (0x3275, 'M', '아'), + (0x3276, 'M', '자'), + (0x3277, 'M', '차'), + (0x3278, 'M', '카'), + (0x3279, 'M', '타'), + (0x327A, 'M', '파'), + (0x327B, 'M', '하'), + (0x327C, 'M', '참고'), + (0x327D, 'M', '주의'), + (0x327E, 'M', '우'), + (0x327F, 'V'), + (0x3280, 'M', '一'), + (0x3281, 'M', '二'), + (0x3282, 'M', '三'), + (0x3283, 'M', '四'), + (0x3284, 'M', '五'), + (0x3285, 'M', '六'), + (0x3286, 'M', '七'), + (0x3287, 'M', '八'), + (0x3288, 'M', '九'), + (0x3289, 'M', '十'), + (0x328A, 'M', '月'), + (0x328B, 'M', '火'), + (0x328C, 'M', '水'), + (0x328D, 'M', '木'), + (0x328E, 'M', '金'), + (0x328F, 'M', '土'), + (0x3290, 'M', '日'), + (0x3291, 'M', '株'), + (0x3292, 'M', '有'), + (0x3293, 'M', '社'), + (0x3294, 'M', '名'), + (0x3295, 'M', '特'), + (0x3296, 'M', '財'), + (0x3297, 'M', '祝'), + (0x3298, 'M', '労'), + (0x3299, 'M', '秘'), + (0x329A, 'M', '男'), + (0x329B, 'M', '女'), + (0x329C, 'M', '適'), + (0x329D, 'M', '優'), + (0x329E, 'M', '印'), + (0x329F, 'M', '注'), + (0x32A0, 'M', '項'), + (0x32A1, 'M', '休'), + (0x32A2, 'M', '写'), + (0x32A3, 'M', '正'), + (0x32A4, 'M', '上'), + (0x32A5, 'M', '中'), + (0x32A6, 'M', '下'), + (0x32A7, 'M', '左'), + (0x32A8, 'M', '右'), + (0x32A9, 'M', '医'), + (0x32AA, 'M', '宗'), + (0x32AB, 'M', '学'), + (0x32AC, 'M', '監'), + (0x32AD, 'M', '企'), + (0x32AE, 'M', '資'), + (0x32AF, 'M', '協'), + (0x32B0, 'M', '夜'), + (0x32B1, 'M', '36'), + ] + +def _seg_32() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x32B2, 'M', '37'), + (0x32B3, 'M', '38'), + (0x32B4, 'M', '39'), + (0x32B5, 'M', '40'), + (0x32B6, 'M', '41'), + (0x32B7, 'M', '42'), + (0x32B8, 'M', '43'), + (0x32B9, 'M', '44'), + (0x32BA, 'M', '45'), + (0x32BB, 'M', '46'), + (0x32BC, 'M', '47'), + (0x32BD, 'M', '48'), + (0x32BE, 'M', '49'), + (0x32BF, 'M', '50'), + (0x32C0, 'M', '1月'), + (0x32C1, 'M', '2月'), + (0x32C2, 'M', '3月'), + (0x32C3, 'M', '4月'), + (0x32C4, 'M', '5月'), + (0x32C5, 'M', '6月'), + (0x32C6, 'M', '7月'), + (0x32C7, 'M', '8月'), + (0x32C8, 'M', '9月'), + (0x32C9, 'M', '10月'), + (0x32CA, 'M', '11月'), + (0x32CB, 'M', '12月'), + (0x32CC, 'M', 'hg'), + (0x32CD, 'M', 'erg'), + (0x32CE, 'M', 'ev'), + (0x32CF, 'M', 'ltd'), + (0x32D0, 'M', 'ア'), + (0x32D1, 'M', 'イ'), + (0x32D2, 'M', 'ウ'), + (0x32D3, 'M', 'エ'), + (0x32D4, 'M', 'オ'), + (0x32D5, 'M', 'カ'), + (0x32D6, 'M', 'キ'), + (0x32D7, 'M', 'ク'), + (0x32D8, 'M', 'ケ'), + (0x32D9, 'M', 'コ'), + (0x32DA, 'M', 'サ'), + (0x32DB, 'M', 'シ'), + (0x32DC, 'M', 'ス'), + (0x32DD, 'M', 'セ'), + (0x32DE, 'M', 'ソ'), + (0x32DF, 'M', 'タ'), + (0x32E0, 'M', 'チ'), + (0x32E1, 'M', 'ツ'), + (0x32E2, 'M', 'テ'), + (0x32E3, 'M', 'ト'), + (0x32E4, 'M', 'ナ'), + (0x32E5, 'M', 'ニ'), + (0x32E6, 'M', 'ヌ'), + (0x32E7, 'M', 'ネ'), + (0x32E8, 'M', 'ノ'), + (0x32E9, 'M', 'ハ'), + (0x32EA, 'M', 'ヒ'), + (0x32EB, 'M', 'フ'), + (0x32EC, 'M', 'ヘ'), + (0x32ED, 'M', 'ホ'), + (0x32EE, 'M', 'マ'), + (0x32EF, 'M', 'ミ'), + (0x32F0, 'M', 'ム'), + (0x32F1, 'M', 'メ'), + (0x32F2, 'M', 'モ'), + (0x32F3, 'M', 'ヤ'), + (0x32F4, 'M', 'ユ'), + (0x32F5, 'M', 'ヨ'), + (0x32F6, 'M', 'ラ'), + (0x32F7, 'M', 'リ'), + (0x32F8, 'M', 'ル'), + (0x32F9, 'M', 'レ'), + (0x32FA, 'M', 'ロ'), + (0x32FB, 'M', 'ワ'), + (0x32FC, 'M', 'ヰ'), + (0x32FD, 'M', 'ヱ'), + (0x32FE, 'M', 'ヲ'), + (0x32FF, 'M', '令和'), + (0x3300, 'M', 'アパート'), + (0x3301, 'M', 'アルファ'), + (0x3302, 'M', 'アンペア'), + (0x3303, 'M', 'アール'), + (0x3304, 'M', 'イニング'), + (0x3305, 'M', 'インチ'), + (0x3306, 'M', 'ウォン'), + (0x3307, 'M', 'エスクード'), + (0x3308, 'M', 'エーカー'), + (0x3309, 'M', 'オンス'), + (0x330A, 'M', 'オーム'), + (0x330B, 'M', 'カイリ'), + (0x330C, 'M', 'カラット'), + (0x330D, 'M', 'カロリー'), + (0x330E, 'M', 'ガロン'), + (0x330F, 'M', 'ガンマ'), + (0x3310, 'M', 'ギガ'), + (0x3311, 'M', 'ギニー'), + (0x3312, 'M', 'キュリー'), + (0x3313, 'M', 'ギルダー'), + (0x3314, 'M', 'キロ'), + (0x3315, 'M', 'キログラム'), + ] + +def _seg_33() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x3316, 'M', 'キロメートル'), + (0x3317, 'M', 'キロワット'), + (0x3318, 'M', 'グラム'), + (0x3319, 'M', 'グラムトン'), + (0x331A, 'M', 'クルゼイロ'), + (0x331B, 'M', 'クローネ'), + (0x331C, 'M', 'ケース'), + (0x331D, 'M', 'コルナ'), + (0x331E, 'M', 'コーポ'), + (0x331F, 'M', 'サイクル'), + (0x3320, 'M', 'サンチーム'), + (0x3321, 'M', 'シリング'), + (0x3322, 'M', 'センチ'), + (0x3323, 'M', 'セント'), + (0x3324, 'M', 'ダース'), + (0x3325, 'M', 'デシ'), + (0x3326, 'M', 'ドル'), + (0x3327, 'M', 'トン'), + (0x3328, 'M', 'ナノ'), + (0x3329, 'M', 'ノット'), + (0x332A, 'M', 'ハイツ'), + (0x332B, 'M', 'パーセント'), + (0x332C, 'M', 'パーツ'), + (0x332D, 'M', 'バーレル'), + (0x332E, 'M', 'ピアストル'), + (0x332F, 'M', 'ピクル'), + (0x3330, 'M', 'ピコ'), + (0x3331, 'M', 'ビル'), + (0x3332, 'M', 'ファラッド'), + (0x3333, 'M', 'フィート'), + (0x3334, 'M', 'ブッシェル'), + (0x3335, 'M', 'フラン'), + (0x3336, 'M', 'ヘクタール'), + (0x3337, 'M', 'ペソ'), + (0x3338, 'M', 'ペニヒ'), + (0x3339, 'M', 'ヘルツ'), + (0x333A, 'M', 'ペンス'), + (0x333B, 'M', 'ページ'), + (0x333C, 'M', 'ベータ'), + (0x333D, 'M', 'ポイント'), + (0x333E, 'M', 'ボルト'), + (0x333F, 'M', 'ホン'), + (0x3340, 'M', 'ポンド'), + (0x3341, 'M', 'ホール'), + (0x3342, 'M', 'ホーン'), + (0x3343, 'M', 'マイクロ'), + (0x3344, 'M', 'マイル'), + (0x3345, 'M', 'マッハ'), + (0x3346, 'M', 'マルク'), + (0x3347, 'M', 'マンション'), + (0x3348, 'M', 'ミクロン'), + (0x3349, 'M', 'ミリ'), + (0x334A, 'M', 'ミリバール'), + (0x334B, 'M', 'メガ'), + (0x334C, 'M', 'メガトン'), + (0x334D, 'M', 'メートル'), + (0x334E, 'M', 'ヤード'), + (0x334F, 'M', 'ヤール'), + (0x3350, 'M', 'ユアン'), + (0x3351, 'M', 'リットル'), + (0x3352, 'M', 'リラ'), + (0x3353, 'M', 'ルピー'), + (0x3354, 'M', 'ルーブル'), + (0x3355, 'M', 'レム'), + (0x3356, 'M', 'レントゲン'), + (0x3357, 'M', 'ワット'), + (0x3358, 'M', '0点'), + (0x3359, 'M', '1点'), + (0x335A, 'M', '2点'), + (0x335B, 'M', '3点'), + (0x335C, 'M', '4点'), + (0x335D, 'M', '5点'), + (0x335E, 'M', '6点'), + (0x335F, 'M', '7点'), + (0x3360, 'M', '8点'), + (0x3361, 'M', '9点'), + (0x3362, 'M', '10点'), + (0x3363, 'M', '11点'), + (0x3364, 'M', '12点'), + (0x3365, 'M', '13点'), + (0x3366, 'M', '14点'), + (0x3367, 'M', '15点'), + (0x3368, 'M', '16点'), + (0x3369, 'M', '17点'), + (0x336A, 'M', '18点'), + (0x336B, 'M', '19点'), + (0x336C, 'M', '20点'), + (0x336D, 'M', '21点'), + (0x336E, 'M', '22点'), + (0x336F, 'M', '23点'), + (0x3370, 'M', '24点'), + (0x3371, 'M', 'hpa'), + (0x3372, 'M', 'da'), + (0x3373, 'M', 'au'), + (0x3374, 'M', 'bar'), + (0x3375, 'M', 'ov'), + (0x3376, 'M', 'pc'), + (0x3377, 'M', 'dm'), + (0x3378, 'M', 'dm2'), + (0x3379, 'M', 'dm3'), + ] + +def _seg_34() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x337A, 'M', 'iu'), + (0x337B, 'M', '平成'), + (0x337C, 'M', '昭和'), + (0x337D, 'M', '大正'), + (0x337E, 'M', '明治'), + (0x337F, 'M', '株式会社'), + (0x3380, 'M', 'pa'), + (0x3381, 'M', 'na'), + (0x3382, 'M', 'μa'), + (0x3383, 'M', 'ma'), + (0x3384, 'M', 'ka'), + (0x3385, 'M', 'kb'), + (0x3386, 'M', 'mb'), + (0x3387, 'M', 'gb'), + (0x3388, 'M', 'cal'), + (0x3389, 'M', 'kcal'), + (0x338A, 'M', 'pf'), + (0x338B, 'M', 'nf'), + (0x338C, 'M', 'μf'), + (0x338D, 'M', 'μg'), + (0x338E, 'M', 'mg'), + (0x338F, 'M', 'kg'), + (0x3390, 'M', 'hz'), + (0x3391, 'M', 'khz'), + (0x3392, 'M', 'mhz'), + (0x3393, 'M', 'ghz'), + (0x3394, 'M', 'thz'), + (0x3395, 'M', 'μl'), + (0x3396, 'M', 'ml'), + (0x3397, 'M', 'dl'), + (0x3398, 'M', 'kl'), + (0x3399, 'M', 'fm'), + (0x339A, 'M', 'nm'), + (0x339B, 'M', 'μm'), + (0x339C, 'M', 'mm'), + (0x339D, 'M', 'cm'), + (0x339E, 'M', 'km'), + (0x339F, 'M', 'mm2'), + (0x33A0, 'M', 'cm2'), + (0x33A1, 'M', 'm2'), + (0x33A2, 'M', 'km2'), + (0x33A3, 'M', 'mm3'), + (0x33A4, 'M', 'cm3'), + (0x33A5, 'M', 'm3'), + (0x33A6, 'M', 'km3'), + (0x33A7, 'M', 'm∕s'), + (0x33A8, 'M', 'm∕s2'), + (0x33A9, 'M', 'pa'), + (0x33AA, 'M', 'kpa'), + (0x33AB, 'M', 'mpa'), + (0x33AC, 'M', 'gpa'), + (0x33AD, 'M', 'rad'), + (0x33AE, 'M', 'rad∕s'), + (0x33AF, 'M', 'rad∕s2'), + (0x33B0, 'M', 'ps'), + (0x33B1, 'M', 'ns'), + (0x33B2, 'M', 'μs'), + (0x33B3, 'M', 'ms'), + (0x33B4, 'M', 'pv'), + (0x33B5, 'M', 'nv'), + (0x33B6, 'M', 'μv'), + (0x33B7, 'M', 'mv'), + (0x33B8, 'M', 'kv'), + (0x33B9, 'M', 'mv'), + (0x33BA, 'M', 'pw'), + (0x33BB, 'M', 'nw'), + (0x33BC, 'M', 'μw'), + (0x33BD, 'M', 'mw'), + (0x33BE, 'M', 'kw'), + (0x33BF, 'M', 'mw'), + (0x33C0, 'M', 'kω'), + (0x33C1, 'M', 'mω'), + (0x33C2, 'X'), + (0x33C3, 'M', 'bq'), + (0x33C4, 'M', 'cc'), + (0x33C5, 'M', 'cd'), + (0x33C6, 'M', 'c∕kg'), + (0x33C7, 'X'), + (0x33C8, 'M', 'db'), + (0x33C9, 'M', 'gy'), + (0x33CA, 'M', 'ha'), + (0x33CB, 'M', 'hp'), + (0x33CC, 'M', 'in'), + (0x33CD, 'M', 'kk'), + (0x33CE, 'M', 'km'), + (0x33CF, 'M', 'kt'), + (0x33D0, 'M', 'lm'), + (0x33D1, 'M', 'ln'), + (0x33D2, 'M', 'log'), + (0x33D3, 'M', 'lx'), + (0x33D4, 'M', 'mb'), + (0x33D5, 'M', 'mil'), + (0x33D6, 'M', 'mol'), + (0x33D7, 'M', 'ph'), + (0x33D8, 'X'), + (0x33D9, 'M', 'ppm'), + (0x33DA, 'M', 'pr'), + (0x33DB, 'M', 'sr'), + (0x33DC, 'M', 'sv'), + (0x33DD, 'M', 'wb'), + ] + +def _seg_35() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x33DE, 'M', 'v∕m'), + (0x33DF, 'M', 'a∕m'), + (0x33E0, 'M', '1日'), + (0x33E1, 'M', '2日'), + (0x33E2, 'M', '3日'), + (0x33E3, 'M', '4日'), + (0x33E4, 'M', '5日'), + (0x33E5, 'M', '6日'), + (0x33E6, 'M', '7日'), + (0x33E7, 'M', '8日'), + (0x33E8, 'M', '9日'), + (0x33E9, 'M', '10日'), + (0x33EA, 'M', '11日'), + (0x33EB, 'M', '12日'), + (0x33EC, 'M', '13日'), + (0x33ED, 'M', '14日'), + (0x33EE, 'M', '15日'), + (0x33EF, 'M', '16日'), + (0x33F0, 'M', '17日'), + (0x33F1, 'M', '18日'), + (0x33F2, 'M', '19日'), + (0x33F3, 'M', '20日'), + (0x33F4, 'M', '21日'), + (0x33F5, 'M', '22日'), + (0x33F6, 'M', '23日'), + (0x33F7, 'M', '24日'), + (0x33F8, 'M', '25日'), + (0x33F9, 'M', '26日'), + (0x33FA, 'M', '27日'), + (0x33FB, 'M', '28日'), + (0x33FC, 'M', '29日'), + (0x33FD, 'M', '30日'), + (0x33FE, 'M', '31日'), + (0x33FF, 'M', 'gal'), + (0x3400, 'V'), + (0xA48D, 'X'), + (0xA490, 'V'), + (0xA4C7, 'X'), + (0xA4D0, 'V'), + (0xA62C, 'X'), + (0xA640, 'M', 'ꙁ'), + (0xA641, 'V'), + (0xA642, 'M', 'ꙃ'), + (0xA643, 'V'), + (0xA644, 'M', 'ꙅ'), + (0xA645, 'V'), + (0xA646, 'M', 'ꙇ'), + (0xA647, 'V'), + (0xA648, 'M', 'ꙉ'), + (0xA649, 'V'), + (0xA64A, 'M', 'ꙋ'), + (0xA64B, 'V'), + (0xA64C, 'M', 'ꙍ'), + (0xA64D, 'V'), + (0xA64E, 'M', 'ꙏ'), + (0xA64F, 'V'), + (0xA650, 'M', 'ꙑ'), + (0xA651, 'V'), + (0xA652, 'M', 'ꙓ'), + (0xA653, 'V'), + (0xA654, 'M', 'ꙕ'), + (0xA655, 'V'), + (0xA656, 'M', 'ꙗ'), + (0xA657, 'V'), + (0xA658, 'M', 'ꙙ'), + (0xA659, 'V'), + (0xA65A, 'M', 'ꙛ'), + (0xA65B, 'V'), + (0xA65C, 'M', 'ꙝ'), + (0xA65D, 'V'), + (0xA65E, 'M', 'ꙟ'), + (0xA65F, 'V'), + (0xA660, 'M', 'ꙡ'), + (0xA661, 'V'), + (0xA662, 'M', 'ꙣ'), + (0xA663, 'V'), + (0xA664, 'M', 'ꙥ'), + (0xA665, 'V'), + (0xA666, 'M', 'ꙧ'), + (0xA667, 'V'), + (0xA668, 'M', 'ꙩ'), + (0xA669, 'V'), + (0xA66A, 'M', 'ꙫ'), + (0xA66B, 'V'), + (0xA66C, 'M', 'ꙭ'), + (0xA66D, 'V'), + (0xA680, 'M', 'ꚁ'), + (0xA681, 'V'), + (0xA682, 'M', 'ꚃ'), + (0xA683, 'V'), + (0xA684, 'M', 'ꚅ'), + (0xA685, 'V'), + (0xA686, 'M', 'ꚇ'), + (0xA687, 'V'), + (0xA688, 'M', 'ꚉ'), + (0xA689, 'V'), + (0xA68A, 'M', 'ꚋ'), + (0xA68B, 'V'), + (0xA68C, 'M', 'ꚍ'), + (0xA68D, 'V'), + ] + +def _seg_36() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xA68E, 'M', 'ꚏ'), + (0xA68F, 'V'), + (0xA690, 'M', 'ꚑ'), + (0xA691, 'V'), + (0xA692, 'M', 'ꚓ'), + (0xA693, 'V'), + (0xA694, 'M', 'ꚕ'), + (0xA695, 'V'), + (0xA696, 'M', 'ꚗ'), + (0xA697, 'V'), + (0xA698, 'M', 'ꚙ'), + (0xA699, 'V'), + (0xA69A, 'M', 'ꚛ'), + (0xA69B, 'V'), + (0xA69C, 'M', 'ъ'), + (0xA69D, 'M', 'ь'), + (0xA69E, 'V'), + (0xA6F8, 'X'), + (0xA700, 'V'), + (0xA722, 'M', 'ꜣ'), + (0xA723, 'V'), + (0xA724, 'M', 'ꜥ'), + (0xA725, 'V'), + (0xA726, 'M', 'ꜧ'), + (0xA727, 'V'), + (0xA728, 'M', 'ꜩ'), + (0xA729, 'V'), + (0xA72A, 'M', 'ꜫ'), + (0xA72B, 'V'), + (0xA72C, 'M', 'ꜭ'), + (0xA72D, 'V'), + (0xA72E, 'M', 'ꜯ'), + (0xA72F, 'V'), + (0xA732, 'M', 'ꜳ'), + (0xA733, 'V'), + (0xA734, 'M', 'ꜵ'), + (0xA735, 'V'), + (0xA736, 'M', 'ꜷ'), + (0xA737, 'V'), + (0xA738, 'M', 'ꜹ'), + (0xA739, 'V'), + (0xA73A, 'M', 'ꜻ'), + (0xA73B, 'V'), + (0xA73C, 'M', 'ꜽ'), + (0xA73D, 'V'), + (0xA73E, 'M', 'ꜿ'), + (0xA73F, 'V'), + (0xA740, 'M', 'ꝁ'), + (0xA741, 'V'), + (0xA742, 'M', 'ꝃ'), + (0xA743, 'V'), + (0xA744, 'M', 'ꝅ'), + (0xA745, 'V'), + (0xA746, 'M', 'ꝇ'), + (0xA747, 'V'), + (0xA748, 'M', 'ꝉ'), + (0xA749, 'V'), + (0xA74A, 'M', 'ꝋ'), + (0xA74B, 'V'), + (0xA74C, 'M', 'ꝍ'), + (0xA74D, 'V'), + (0xA74E, 'M', 'ꝏ'), + (0xA74F, 'V'), + (0xA750, 'M', 'ꝑ'), + (0xA751, 'V'), + (0xA752, 'M', 'ꝓ'), + (0xA753, 'V'), + (0xA754, 'M', 'ꝕ'), + (0xA755, 'V'), + (0xA756, 'M', 'ꝗ'), + (0xA757, 'V'), + (0xA758, 'M', 'ꝙ'), + (0xA759, 'V'), + (0xA75A, 'M', 'ꝛ'), + (0xA75B, 'V'), + (0xA75C, 'M', 'ꝝ'), + (0xA75D, 'V'), + (0xA75E, 'M', 'ꝟ'), + (0xA75F, 'V'), + (0xA760, 'M', 'ꝡ'), + (0xA761, 'V'), + (0xA762, 'M', 'ꝣ'), + (0xA763, 'V'), + (0xA764, 'M', 'ꝥ'), + (0xA765, 'V'), + (0xA766, 'M', 'ꝧ'), + (0xA767, 'V'), + (0xA768, 'M', 'ꝩ'), + (0xA769, 'V'), + (0xA76A, 'M', 'ꝫ'), + (0xA76B, 'V'), + (0xA76C, 'M', 'ꝭ'), + (0xA76D, 'V'), + (0xA76E, 'M', 'ꝯ'), + (0xA76F, 'V'), + (0xA770, 'M', 'ꝯ'), + (0xA771, 'V'), + (0xA779, 'M', 'ꝺ'), + (0xA77A, 'V'), + (0xA77B, 'M', 'ꝼ'), + ] + +def _seg_37() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xA77C, 'V'), + (0xA77D, 'M', 'ᵹ'), + (0xA77E, 'M', 'ꝿ'), + (0xA77F, 'V'), + (0xA780, 'M', 'ꞁ'), + (0xA781, 'V'), + (0xA782, 'M', 'ꞃ'), + (0xA783, 'V'), + (0xA784, 'M', 'ꞅ'), + (0xA785, 'V'), + (0xA786, 'M', 'ꞇ'), + (0xA787, 'V'), + (0xA78B, 'M', 'ꞌ'), + (0xA78C, 'V'), + (0xA78D, 'M', 'ɥ'), + (0xA78E, 'V'), + (0xA790, 'M', 'ꞑ'), + (0xA791, 'V'), + (0xA792, 'M', 'ꞓ'), + (0xA793, 'V'), + (0xA796, 'M', 'ꞗ'), + (0xA797, 'V'), + (0xA798, 'M', 'ꞙ'), + (0xA799, 'V'), + (0xA79A, 'M', 'ꞛ'), + (0xA79B, 'V'), + (0xA79C, 'M', 'ꞝ'), + (0xA79D, 'V'), + (0xA79E, 'M', 'ꞟ'), + (0xA79F, 'V'), + (0xA7A0, 'M', 'ꞡ'), + (0xA7A1, 'V'), + (0xA7A2, 'M', 'ꞣ'), + (0xA7A3, 'V'), + (0xA7A4, 'M', 'ꞥ'), + (0xA7A5, 'V'), + (0xA7A6, 'M', 'ꞧ'), + (0xA7A7, 'V'), + (0xA7A8, 'M', 'ꞩ'), + (0xA7A9, 'V'), + (0xA7AA, 'M', 'ɦ'), + (0xA7AB, 'M', 'ɜ'), + (0xA7AC, 'M', 'ɡ'), + (0xA7AD, 'M', 'ɬ'), + (0xA7AE, 'M', 'ɪ'), + (0xA7AF, 'V'), + (0xA7B0, 'M', 'ʞ'), + (0xA7B1, 'M', 'ʇ'), + (0xA7B2, 'M', 'ʝ'), + (0xA7B3, 'M', 'ꭓ'), + (0xA7B4, 'M', 'ꞵ'), + (0xA7B5, 'V'), + (0xA7B6, 'M', 'ꞷ'), + (0xA7B7, 'V'), + (0xA7B8, 'M', 'ꞹ'), + (0xA7B9, 'V'), + (0xA7BA, 'M', 'ꞻ'), + (0xA7BB, 'V'), + (0xA7BC, 'M', 'ꞽ'), + (0xA7BD, 'V'), + (0xA7BE, 'M', 'ꞿ'), + (0xA7BF, 'V'), + (0xA7C0, 'M', 'ꟁ'), + (0xA7C1, 'V'), + (0xA7C2, 'M', 'ꟃ'), + (0xA7C3, 'V'), + (0xA7C4, 'M', 'ꞔ'), + (0xA7C5, 'M', 'ʂ'), + (0xA7C6, 'M', 'ᶎ'), + (0xA7C7, 'M', 'ꟈ'), + (0xA7C8, 'V'), + (0xA7C9, 'M', 'ꟊ'), + (0xA7CA, 'V'), + (0xA7CB, 'X'), + (0xA7D0, 'M', 'ꟑ'), + (0xA7D1, 'V'), + (0xA7D2, 'X'), + (0xA7D3, 'V'), + (0xA7D4, 'X'), + (0xA7D5, 'V'), + (0xA7D6, 'M', 'ꟗ'), + (0xA7D7, 'V'), + (0xA7D8, 'M', 'ꟙ'), + (0xA7D9, 'V'), + (0xA7DA, 'X'), + (0xA7F2, 'M', 'c'), + (0xA7F3, 'M', 'f'), + (0xA7F4, 'M', 'q'), + (0xA7F5, 'M', 'ꟶ'), + (0xA7F6, 'V'), + (0xA7F8, 'M', 'ħ'), + (0xA7F9, 'M', 'œ'), + (0xA7FA, 'V'), + (0xA82D, 'X'), + (0xA830, 'V'), + (0xA83A, 'X'), + (0xA840, 'V'), + (0xA878, 'X'), + (0xA880, 'V'), + (0xA8C6, 'X'), + ] + +def _seg_38() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xA8CE, 'V'), + (0xA8DA, 'X'), + (0xA8E0, 'V'), + (0xA954, 'X'), + (0xA95F, 'V'), + (0xA97D, 'X'), + (0xA980, 'V'), + (0xA9CE, 'X'), + (0xA9CF, 'V'), + (0xA9DA, 'X'), + (0xA9DE, 'V'), + (0xA9FF, 'X'), + (0xAA00, 'V'), + (0xAA37, 'X'), + (0xAA40, 'V'), + (0xAA4E, 'X'), + (0xAA50, 'V'), + (0xAA5A, 'X'), + (0xAA5C, 'V'), + (0xAAC3, 'X'), + (0xAADB, 'V'), + (0xAAF7, 'X'), + (0xAB01, 'V'), + (0xAB07, 'X'), + (0xAB09, 'V'), + (0xAB0F, 'X'), + (0xAB11, 'V'), + (0xAB17, 'X'), + (0xAB20, 'V'), + (0xAB27, 'X'), + (0xAB28, 'V'), + (0xAB2F, 'X'), + (0xAB30, 'V'), + (0xAB5C, 'M', 'ꜧ'), + (0xAB5D, 'M', 'ꬷ'), + (0xAB5E, 'M', 'ɫ'), + (0xAB5F, 'M', 'ꭒ'), + (0xAB60, 'V'), + (0xAB69, 'M', 'ʍ'), + (0xAB6A, 'V'), + (0xAB6C, 'X'), + (0xAB70, 'M', 'Ꭰ'), + (0xAB71, 'M', 'Ꭱ'), + (0xAB72, 'M', 'Ꭲ'), + (0xAB73, 'M', 'Ꭳ'), + (0xAB74, 'M', 'Ꭴ'), + (0xAB75, 'M', 'Ꭵ'), + (0xAB76, 'M', 'Ꭶ'), + (0xAB77, 'M', 'Ꭷ'), + (0xAB78, 'M', 'Ꭸ'), + (0xAB79, 'M', 'Ꭹ'), + (0xAB7A, 'M', 'Ꭺ'), + (0xAB7B, 'M', 'Ꭻ'), + (0xAB7C, 'M', 'Ꭼ'), + (0xAB7D, 'M', 'Ꭽ'), + (0xAB7E, 'M', 'Ꭾ'), + (0xAB7F, 'M', 'Ꭿ'), + (0xAB80, 'M', 'Ꮀ'), + (0xAB81, 'M', 'Ꮁ'), + (0xAB82, 'M', 'Ꮂ'), + (0xAB83, 'M', 'Ꮃ'), + (0xAB84, 'M', 'Ꮄ'), + (0xAB85, 'M', 'Ꮅ'), + (0xAB86, 'M', 'Ꮆ'), + (0xAB87, 'M', 'Ꮇ'), + (0xAB88, 'M', 'Ꮈ'), + (0xAB89, 'M', 'Ꮉ'), + (0xAB8A, 'M', 'Ꮊ'), + (0xAB8B, 'M', 'Ꮋ'), + (0xAB8C, 'M', 'Ꮌ'), + (0xAB8D, 'M', 'Ꮍ'), + (0xAB8E, 'M', 'Ꮎ'), + (0xAB8F, 'M', 'Ꮏ'), + (0xAB90, 'M', 'Ꮐ'), + (0xAB91, 'M', 'Ꮑ'), + (0xAB92, 'M', 'Ꮒ'), + (0xAB93, 'M', 'Ꮓ'), + (0xAB94, 'M', 'Ꮔ'), + (0xAB95, 'M', 'Ꮕ'), + (0xAB96, 'M', 'Ꮖ'), + (0xAB97, 'M', 'Ꮗ'), + (0xAB98, 'M', 'Ꮘ'), + (0xAB99, 'M', 'Ꮙ'), + (0xAB9A, 'M', 'Ꮚ'), + (0xAB9B, 'M', 'Ꮛ'), + (0xAB9C, 'M', 'Ꮜ'), + (0xAB9D, 'M', 'Ꮝ'), + (0xAB9E, 'M', 'Ꮞ'), + (0xAB9F, 'M', 'Ꮟ'), + (0xABA0, 'M', 'Ꮠ'), + (0xABA1, 'M', 'Ꮡ'), + (0xABA2, 'M', 'Ꮢ'), + (0xABA3, 'M', 'Ꮣ'), + (0xABA4, 'M', 'Ꮤ'), + (0xABA5, 'M', 'Ꮥ'), + (0xABA6, 'M', 'Ꮦ'), + (0xABA7, 'M', 'Ꮧ'), + (0xABA8, 'M', 'Ꮨ'), + (0xABA9, 'M', 'Ꮩ'), + (0xABAA, 'M', 'Ꮪ'), + ] + +def _seg_39() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xABAB, 'M', 'Ꮫ'), + (0xABAC, 'M', 'Ꮬ'), + (0xABAD, 'M', 'Ꮭ'), + (0xABAE, 'M', 'Ꮮ'), + (0xABAF, 'M', 'Ꮯ'), + (0xABB0, 'M', 'Ꮰ'), + (0xABB1, 'M', 'Ꮱ'), + (0xABB2, 'M', 'Ꮲ'), + (0xABB3, 'M', 'Ꮳ'), + (0xABB4, 'M', 'Ꮴ'), + (0xABB5, 'M', 'Ꮵ'), + (0xABB6, 'M', 'Ꮶ'), + (0xABB7, 'M', 'Ꮷ'), + (0xABB8, 'M', 'Ꮸ'), + (0xABB9, 'M', 'Ꮹ'), + (0xABBA, 'M', 'Ꮺ'), + (0xABBB, 'M', 'Ꮻ'), + (0xABBC, 'M', 'Ꮼ'), + (0xABBD, 'M', 'Ꮽ'), + (0xABBE, 'M', 'Ꮾ'), + (0xABBF, 'M', 'Ꮿ'), + (0xABC0, 'V'), + (0xABEE, 'X'), + (0xABF0, 'V'), + (0xABFA, 'X'), + (0xAC00, 'V'), + (0xD7A4, 'X'), + (0xD7B0, 'V'), + (0xD7C7, 'X'), + (0xD7CB, 'V'), + (0xD7FC, 'X'), + (0xF900, 'M', '豈'), + (0xF901, 'M', '更'), + (0xF902, 'M', '車'), + (0xF903, 'M', '賈'), + (0xF904, 'M', '滑'), + (0xF905, 'M', '串'), + (0xF906, 'M', '句'), + (0xF907, 'M', '龜'), + (0xF909, 'M', '契'), + (0xF90A, 'M', '金'), + (0xF90B, 'M', '喇'), + (0xF90C, 'M', '奈'), + (0xF90D, 'M', '懶'), + (0xF90E, 'M', '癩'), + (0xF90F, 'M', '羅'), + (0xF910, 'M', '蘿'), + (0xF911, 'M', '螺'), + (0xF912, 'M', '裸'), + (0xF913, 'M', '邏'), + (0xF914, 'M', '樂'), + (0xF915, 'M', '洛'), + (0xF916, 'M', '烙'), + (0xF917, 'M', '珞'), + (0xF918, 'M', '落'), + (0xF919, 'M', '酪'), + (0xF91A, 'M', '駱'), + (0xF91B, 'M', '亂'), + (0xF91C, 'M', '卵'), + (0xF91D, 'M', '欄'), + (0xF91E, 'M', '爛'), + (0xF91F, 'M', '蘭'), + (0xF920, 'M', '鸞'), + (0xF921, 'M', '嵐'), + (0xF922, 'M', '濫'), + (0xF923, 'M', '藍'), + (0xF924, 'M', '襤'), + (0xF925, 'M', '拉'), + (0xF926, 'M', '臘'), + (0xF927, 'M', '蠟'), + (0xF928, 'M', '廊'), + (0xF929, 'M', '朗'), + (0xF92A, 'M', '浪'), + (0xF92B, 'M', '狼'), + (0xF92C, 'M', '郎'), + (0xF92D, 'M', '來'), + (0xF92E, 'M', '冷'), + (0xF92F, 'M', '勞'), + (0xF930, 'M', '擄'), + (0xF931, 'M', '櫓'), + (0xF932, 'M', '爐'), + (0xF933, 'M', '盧'), + (0xF934, 'M', '老'), + (0xF935, 'M', '蘆'), + (0xF936, 'M', '虜'), + (0xF937, 'M', '路'), + (0xF938, 'M', '露'), + (0xF939, 'M', '魯'), + (0xF93A, 'M', '鷺'), + (0xF93B, 'M', '碌'), + (0xF93C, 'M', '祿'), + (0xF93D, 'M', '綠'), + (0xF93E, 'M', '菉'), + (0xF93F, 'M', '錄'), + (0xF940, 'M', '鹿'), + (0xF941, 'M', '論'), + (0xF942, 'M', '壟'), + (0xF943, 'M', '弄'), + (0xF944, 'M', '籠'), + (0xF945, 'M', '聾'), + ] + +def _seg_40() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xF946, 'M', '牢'), + (0xF947, 'M', '磊'), + (0xF948, 'M', '賂'), + (0xF949, 'M', '雷'), + (0xF94A, 'M', '壘'), + (0xF94B, 'M', '屢'), + (0xF94C, 'M', '樓'), + (0xF94D, 'M', '淚'), + (0xF94E, 'M', '漏'), + (0xF94F, 'M', '累'), + (0xF950, 'M', '縷'), + (0xF951, 'M', '陋'), + (0xF952, 'M', '勒'), + (0xF953, 'M', '肋'), + (0xF954, 'M', '凜'), + (0xF955, 'M', '凌'), + (0xF956, 'M', '稜'), + (0xF957, 'M', '綾'), + (0xF958, 'M', '菱'), + (0xF959, 'M', '陵'), + (0xF95A, 'M', '讀'), + (0xF95B, 'M', '拏'), + (0xF95C, 'M', '樂'), + (0xF95D, 'M', '諾'), + (0xF95E, 'M', '丹'), + (0xF95F, 'M', '寧'), + (0xF960, 'M', '怒'), + (0xF961, 'M', '率'), + (0xF962, 'M', '異'), + (0xF963, 'M', '北'), + (0xF964, 'M', '磻'), + (0xF965, 'M', '便'), + (0xF966, 'M', '復'), + (0xF967, 'M', '不'), + (0xF968, 'M', '泌'), + (0xF969, 'M', '數'), + (0xF96A, 'M', '索'), + (0xF96B, 'M', '參'), + (0xF96C, 'M', '塞'), + (0xF96D, 'M', '省'), + (0xF96E, 'M', '葉'), + (0xF96F, 'M', '說'), + (0xF970, 'M', '殺'), + (0xF971, 'M', '辰'), + (0xF972, 'M', '沈'), + (0xF973, 'M', '拾'), + (0xF974, 'M', '若'), + (0xF975, 'M', '掠'), + (0xF976, 'M', '略'), + (0xF977, 'M', '亮'), + (0xF978, 'M', '兩'), + (0xF979, 'M', '凉'), + (0xF97A, 'M', '梁'), + (0xF97B, 'M', '糧'), + (0xF97C, 'M', '良'), + (0xF97D, 'M', '諒'), + (0xF97E, 'M', '量'), + (0xF97F, 'M', '勵'), + (0xF980, 'M', '呂'), + (0xF981, 'M', '女'), + (0xF982, 'M', '廬'), + (0xF983, 'M', '旅'), + (0xF984, 'M', '濾'), + (0xF985, 'M', '礪'), + (0xF986, 'M', '閭'), + (0xF987, 'M', '驪'), + (0xF988, 'M', '麗'), + (0xF989, 'M', '黎'), + (0xF98A, 'M', '力'), + (0xF98B, 'M', '曆'), + (0xF98C, 'M', '歷'), + (0xF98D, 'M', '轢'), + (0xF98E, 'M', '年'), + (0xF98F, 'M', '憐'), + (0xF990, 'M', '戀'), + (0xF991, 'M', '撚'), + (0xF992, 'M', '漣'), + (0xF993, 'M', '煉'), + (0xF994, 'M', '璉'), + (0xF995, 'M', '秊'), + (0xF996, 'M', '練'), + (0xF997, 'M', '聯'), + (0xF998, 'M', '輦'), + (0xF999, 'M', '蓮'), + (0xF99A, 'M', '連'), + (0xF99B, 'M', '鍊'), + (0xF99C, 'M', '列'), + (0xF99D, 'M', '劣'), + (0xF99E, 'M', '咽'), + (0xF99F, 'M', '烈'), + (0xF9A0, 'M', '裂'), + (0xF9A1, 'M', '說'), + (0xF9A2, 'M', '廉'), + (0xF9A3, 'M', '念'), + (0xF9A4, 'M', '捻'), + (0xF9A5, 'M', '殮'), + (0xF9A6, 'M', '簾'), + (0xF9A7, 'M', '獵'), + (0xF9A8, 'M', '令'), + (0xF9A9, 'M', '囹'), + ] + +def _seg_41() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xF9AA, 'M', '寧'), + (0xF9AB, 'M', '嶺'), + (0xF9AC, 'M', '怜'), + (0xF9AD, 'M', '玲'), + (0xF9AE, 'M', '瑩'), + (0xF9AF, 'M', '羚'), + (0xF9B0, 'M', '聆'), + (0xF9B1, 'M', '鈴'), + (0xF9B2, 'M', '零'), + (0xF9B3, 'M', '靈'), + (0xF9B4, 'M', '領'), + (0xF9B5, 'M', '例'), + (0xF9B6, 'M', '禮'), + (0xF9B7, 'M', '醴'), + (0xF9B8, 'M', '隸'), + (0xF9B9, 'M', '惡'), + (0xF9BA, 'M', '了'), + (0xF9BB, 'M', '僚'), + (0xF9BC, 'M', '寮'), + (0xF9BD, 'M', '尿'), + (0xF9BE, 'M', '料'), + (0xF9BF, 'M', '樂'), + (0xF9C0, 'M', '燎'), + (0xF9C1, 'M', '療'), + (0xF9C2, 'M', '蓼'), + (0xF9C3, 'M', '遼'), + (0xF9C4, 'M', '龍'), + (0xF9C5, 'M', '暈'), + (0xF9C6, 'M', '阮'), + (0xF9C7, 'M', '劉'), + (0xF9C8, 'M', '杻'), + (0xF9C9, 'M', '柳'), + (0xF9CA, 'M', '流'), + (0xF9CB, 'M', '溜'), + (0xF9CC, 'M', '琉'), + (0xF9CD, 'M', '留'), + (0xF9CE, 'M', '硫'), + (0xF9CF, 'M', '紐'), + (0xF9D0, 'M', '類'), + (0xF9D1, 'M', '六'), + (0xF9D2, 'M', '戮'), + (0xF9D3, 'M', '陸'), + (0xF9D4, 'M', '倫'), + (0xF9D5, 'M', '崙'), + (0xF9D6, 'M', '淪'), + (0xF9D7, 'M', '輪'), + (0xF9D8, 'M', '律'), + (0xF9D9, 'M', '慄'), + (0xF9DA, 'M', '栗'), + (0xF9DB, 'M', '率'), + (0xF9DC, 'M', '隆'), + (0xF9DD, 'M', '利'), + (0xF9DE, 'M', '吏'), + (0xF9DF, 'M', '履'), + (0xF9E0, 'M', '易'), + (0xF9E1, 'M', '李'), + (0xF9E2, 'M', '梨'), + (0xF9E3, 'M', '泥'), + (0xF9E4, 'M', '理'), + (0xF9E5, 'M', '痢'), + (0xF9E6, 'M', '罹'), + (0xF9E7, 'M', '裏'), + (0xF9E8, 'M', '裡'), + (0xF9E9, 'M', '里'), + (0xF9EA, 'M', '離'), + (0xF9EB, 'M', '匿'), + (0xF9EC, 'M', '溺'), + (0xF9ED, 'M', '吝'), + (0xF9EE, 'M', '燐'), + (0xF9EF, 'M', '璘'), + (0xF9F0, 'M', '藺'), + (0xF9F1, 'M', '隣'), + (0xF9F2, 'M', '鱗'), + (0xF9F3, 'M', '麟'), + (0xF9F4, 'M', '林'), + (0xF9F5, 'M', '淋'), + (0xF9F6, 'M', '臨'), + (0xF9F7, 'M', '立'), + (0xF9F8, 'M', '笠'), + (0xF9F9, 'M', '粒'), + (0xF9FA, 'M', '狀'), + (0xF9FB, 'M', '炙'), + (0xF9FC, 'M', '識'), + (0xF9FD, 'M', '什'), + (0xF9FE, 'M', '茶'), + (0xF9FF, 'M', '刺'), + (0xFA00, 'M', '切'), + (0xFA01, 'M', '度'), + (0xFA02, 'M', '拓'), + (0xFA03, 'M', '糖'), + (0xFA04, 'M', '宅'), + (0xFA05, 'M', '洞'), + (0xFA06, 'M', '暴'), + (0xFA07, 'M', '輻'), + (0xFA08, 'M', '行'), + (0xFA09, 'M', '降'), + (0xFA0A, 'M', '見'), + (0xFA0B, 'M', '廓'), + (0xFA0C, 'M', '兀'), + (0xFA0D, 'M', '嗀'), + ] + +def _seg_42() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFA0E, 'V'), + (0xFA10, 'M', '塚'), + (0xFA11, 'V'), + (0xFA12, 'M', '晴'), + (0xFA13, 'V'), + (0xFA15, 'M', '凞'), + (0xFA16, 'M', '猪'), + (0xFA17, 'M', '益'), + (0xFA18, 'M', '礼'), + (0xFA19, 'M', '神'), + (0xFA1A, 'M', '祥'), + (0xFA1B, 'M', '福'), + (0xFA1C, 'M', '靖'), + (0xFA1D, 'M', '精'), + (0xFA1E, 'M', '羽'), + (0xFA1F, 'V'), + (0xFA20, 'M', '蘒'), + (0xFA21, 'V'), + (0xFA22, 'M', '諸'), + (0xFA23, 'V'), + (0xFA25, 'M', '逸'), + (0xFA26, 'M', '都'), + (0xFA27, 'V'), + (0xFA2A, 'M', '飯'), + (0xFA2B, 'M', '飼'), + (0xFA2C, 'M', '館'), + (0xFA2D, 'M', '鶴'), + (0xFA2E, 'M', '郞'), + (0xFA2F, 'M', '隷'), + (0xFA30, 'M', '侮'), + (0xFA31, 'M', '僧'), + (0xFA32, 'M', '免'), + (0xFA33, 'M', '勉'), + (0xFA34, 'M', '勤'), + (0xFA35, 'M', '卑'), + (0xFA36, 'M', '喝'), + (0xFA37, 'M', '嘆'), + (0xFA38, 'M', '器'), + (0xFA39, 'M', '塀'), + (0xFA3A, 'M', '墨'), + (0xFA3B, 'M', '層'), + (0xFA3C, 'M', '屮'), + (0xFA3D, 'M', '悔'), + (0xFA3E, 'M', '慨'), + (0xFA3F, 'M', '憎'), + (0xFA40, 'M', '懲'), + (0xFA41, 'M', '敏'), + (0xFA42, 'M', '既'), + (0xFA43, 'M', '暑'), + (0xFA44, 'M', '梅'), + (0xFA45, 'M', '海'), + (0xFA46, 'M', '渚'), + (0xFA47, 'M', '漢'), + (0xFA48, 'M', '煮'), + (0xFA49, 'M', '爫'), + (0xFA4A, 'M', '琢'), + (0xFA4B, 'M', '碑'), + (0xFA4C, 'M', '社'), + (0xFA4D, 'M', '祉'), + (0xFA4E, 'M', '祈'), + (0xFA4F, 'M', '祐'), + (0xFA50, 'M', '祖'), + (0xFA51, 'M', '祝'), + (0xFA52, 'M', '禍'), + (0xFA53, 'M', '禎'), + (0xFA54, 'M', '穀'), + (0xFA55, 'M', '突'), + (0xFA56, 'M', '節'), + (0xFA57, 'M', '練'), + (0xFA58, 'M', '縉'), + (0xFA59, 'M', '繁'), + (0xFA5A, 'M', '署'), + (0xFA5B, 'M', '者'), + (0xFA5C, 'M', '臭'), + (0xFA5D, 'M', '艹'), + (0xFA5F, 'M', '著'), + (0xFA60, 'M', '褐'), + (0xFA61, 'M', '視'), + (0xFA62, 'M', '謁'), + (0xFA63, 'M', '謹'), + (0xFA64, 'M', '賓'), + (0xFA65, 'M', '贈'), + (0xFA66, 'M', '辶'), + (0xFA67, 'M', '逸'), + (0xFA68, 'M', '難'), + (0xFA69, 'M', '響'), + (0xFA6A, 'M', '頻'), + (0xFA6B, 'M', '恵'), + (0xFA6C, 'M', '𤋮'), + (0xFA6D, 'M', '舘'), + (0xFA6E, 'X'), + (0xFA70, 'M', '並'), + (0xFA71, 'M', '况'), + (0xFA72, 'M', '全'), + (0xFA73, 'M', '侀'), + (0xFA74, 'M', '充'), + (0xFA75, 'M', '冀'), + (0xFA76, 'M', '勇'), + (0xFA77, 'M', '勺'), + (0xFA78, 'M', '喝'), + ] + +def _seg_43() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFA79, 'M', '啕'), + (0xFA7A, 'M', '喙'), + (0xFA7B, 'M', '嗢'), + (0xFA7C, 'M', '塚'), + (0xFA7D, 'M', '墳'), + (0xFA7E, 'M', '奄'), + (0xFA7F, 'M', '奔'), + (0xFA80, 'M', '婢'), + (0xFA81, 'M', '嬨'), + (0xFA82, 'M', '廒'), + (0xFA83, 'M', '廙'), + (0xFA84, 'M', '彩'), + (0xFA85, 'M', '徭'), + (0xFA86, 'M', '惘'), + (0xFA87, 'M', '慎'), + (0xFA88, 'M', '愈'), + (0xFA89, 'M', '憎'), + (0xFA8A, 'M', '慠'), + (0xFA8B, 'M', '懲'), + (0xFA8C, 'M', '戴'), + (0xFA8D, 'M', '揄'), + (0xFA8E, 'M', '搜'), + (0xFA8F, 'M', '摒'), + (0xFA90, 'M', '敖'), + (0xFA91, 'M', '晴'), + (0xFA92, 'M', '朗'), + (0xFA93, 'M', '望'), + (0xFA94, 'M', '杖'), + (0xFA95, 'M', '歹'), + (0xFA96, 'M', '殺'), + (0xFA97, 'M', '流'), + (0xFA98, 'M', '滛'), + (0xFA99, 'M', '滋'), + (0xFA9A, 'M', '漢'), + (0xFA9B, 'M', '瀞'), + (0xFA9C, 'M', '煮'), + (0xFA9D, 'M', '瞧'), + (0xFA9E, 'M', '爵'), + (0xFA9F, 'M', '犯'), + (0xFAA0, 'M', '猪'), + (0xFAA1, 'M', '瑱'), + (0xFAA2, 'M', '甆'), + (0xFAA3, 'M', '画'), + (0xFAA4, 'M', '瘝'), + (0xFAA5, 'M', '瘟'), + (0xFAA6, 'M', '益'), + (0xFAA7, 'M', '盛'), + (0xFAA8, 'M', '直'), + (0xFAA9, 'M', '睊'), + (0xFAAA, 'M', '着'), + (0xFAAB, 'M', '磌'), + (0xFAAC, 'M', '窱'), + (0xFAAD, 'M', '節'), + (0xFAAE, 'M', '类'), + (0xFAAF, 'M', '絛'), + (0xFAB0, 'M', '練'), + (0xFAB1, 'M', '缾'), + (0xFAB2, 'M', '者'), + (0xFAB3, 'M', '荒'), + (0xFAB4, 'M', '華'), + (0xFAB5, 'M', '蝹'), + (0xFAB6, 'M', '襁'), + (0xFAB7, 'M', '覆'), + (0xFAB8, 'M', '視'), + (0xFAB9, 'M', '調'), + (0xFABA, 'M', '諸'), + (0xFABB, 'M', '請'), + (0xFABC, 'M', '謁'), + (0xFABD, 'M', '諾'), + (0xFABE, 'M', '諭'), + (0xFABF, 'M', '謹'), + (0xFAC0, 'M', '變'), + (0xFAC1, 'M', '贈'), + (0xFAC2, 'M', '輸'), + (0xFAC3, 'M', '遲'), + (0xFAC4, 'M', '醙'), + (0xFAC5, 'M', '鉶'), + (0xFAC6, 'M', '陼'), + (0xFAC7, 'M', '難'), + (0xFAC8, 'M', '靖'), + (0xFAC9, 'M', '韛'), + (0xFACA, 'M', '響'), + (0xFACB, 'M', '頋'), + (0xFACC, 'M', '頻'), + (0xFACD, 'M', '鬒'), + (0xFACE, 'M', '龜'), + (0xFACF, 'M', '𢡊'), + (0xFAD0, 'M', '𢡄'), + (0xFAD1, 'M', '𣏕'), + (0xFAD2, 'M', '㮝'), + (0xFAD3, 'M', '䀘'), + (0xFAD4, 'M', '䀹'), + (0xFAD5, 'M', '𥉉'), + (0xFAD6, 'M', '𥳐'), + (0xFAD7, 'M', '𧻓'), + (0xFAD8, 'M', '齃'), + (0xFAD9, 'M', '龎'), + (0xFADA, 'X'), + (0xFB00, 'M', 'ff'), + (0xFB01, 'M', 'fi'), + ] + +def _seg_44() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFB02, 'M', 'fl'), + (0xFB03, 'M', 'ffi'), + (0xFB04, 'M', 'ffl'), + (0xFB05, 'M', 'st'), + (0xFB07, 'X'), + (0xFB13, 'M', 'մն'), + (0xFB14, 'M', 'մե'), + (0xFB15, 'M', 'մի'), + (0xFB16, 'M', 'վն'), + (0xFB17, 'M', 'մխ'), + (0xFB18, 'X'), + (0xFB1D, 'M', 'יִ'), + (0xFB1E, 'V'), + (0xFB1F, 'M', 'ײַ'), + (0xFB20, 'M', 'ע'), + (0xFB21, 'M', 'א'), + (0xFB22, 'M', 'ד'), + (0xFB23, 'M', 'ה'), + (0xFB24, 'M', 'כ'), + (0xFB25, 'M', 'ל'), + (0xFB26, 'M', 'ם'), + (0xFB27, 'M', 'ר'), + (0xFB28, 'M', 'ת'), + (0xFB29, '3', '+'), + (0xFB2A, 'M', 'שׁ'), + (0xFB2B, 'M', 'שׂ'), + (0xFB2C, 'M', 'שּׁ'), + (0xFB2D, 'M', 'שּׂ'), + (0xFB2E, 'M', 'אַ'), + (0xFB2F, 'M', 'אָ'), + (0xFB30, 'M', 'אּ'), + (0xFB31, 'M', 'בּ'), + (0xFB32, 'M', 'גּ'), + (0xFB33, 'M', 'דּ'), + (0xFB34, 'M', 'הּ'), + (0xFB35, 'M', 'וּ'), + (0xFB36, 'M', 'זּ'), + (0xFB37, 'X'), + (0xFB38, 'M', 'טּ'), + (0xFB39, 'M', 'יּ'), + (0xFB3A, 'M', 'ךּ'), + (0xFB3B, 'M', 'כּ'), + (0xFB3C, 'M', 'לּ'), + (0xFB3D, 'X'), + (0xFB3E, 'M', 'מּ'), + (0xFB3F, 'X'), + (0xFB40, 'M', 'נּ'), + (0xFB41, 'M', 'סּ'), + (0xFB42, 'X'), + (0xFB43, 'M', 'ףּ'), + (0xFB44, 'M', 'פּ'), + (0xFB45, 'X'), + (0xFB46, 'M', 'צּ'), + (0xFB47, 'M', 'קּ'), + (0xFB48, 'M', 'רּ'), + (0xFB49, 'M', 'שּ'), + (0xFB4A, 'M', 'תּ'), + (0xFB4B, 'M', 'וֹ'), + (0xFB4C, 'M', 'בֿ'), + (0xFB4D, 'M', 'כֿ'), + (0xFB4E, 'M', 'פֿ'), + (0xFB4F, 'M', 'אל'), + (0xFB50, 'M', 'ٱ'), + (0xFB52, 'M', 'ٻ'), + (0xFB56, 'M', 'پ'), + (0xFB5A, 'M', 'ڀ'), + (0xFB5E, 'M', 'ٺ'), + (0xFB62, 'M', 'ٿ'), + (0xFB66, 'M', 'ٹ'), + (0xFB6A, 'M', 'ڤ'), + (0xFB6E, 'M', 'ڦ'), + (0xFB72, 'M', 'ڄ'), + (0xFB76, 'M', 'ڃ'), + (0xFB7A, 'M', 'چ'), + (0xFB7E, 'M', 'ڇ'), + (0xFB82, 'M', 'ڍ'), + (0xFB84, 'M', 'ڌ'), + (0xFB86, 'M', 'ڎ'), + (0xFB88, 'M', 'ڈ'), + (0xFB8A, 'M', 'ژ'), + (0xFB8C, 'M', 'ڑ'), + (0xFB8E, 'M', 'ک'), + (0xFB92, 'M', 'گ'), + (0xFB96, 'M', 'ڳ'), + (0xFB9A, 'M', 'ڱ'), + (0xFB9E, 'M', 'ں'), + (0xFBA0, 'M', 'ڻ'), + (0xFBA4, 'M', 'ۀ'), + (0xFBA6, 'M', 'ہ'), + (0xFBAA, 'M', 'ھ'), + (0xFBAE, 'M', 'ے'), + (0xFBB0, 'M', 'ۓ'), + (0xFBB2, 'V'), + (0xFBC3, 'X'), + (0xFBD3, 'M', 'ڭ'), + (0xFBD7, 'M', 'ۇ'), + (0xFBD9, 'M', 'ۆ'), + (0xFBDB, 'M', 'ۈ'), + (0xFBDD, 'M', 'ۇٴ'), + (0xFBDE, 'M', 'ۋ'), + ] + +def _seg_45() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFBE0, 'M', 'ۅ'), + (0xFBE2, 'M', 'ۉ'), + (0xFBE4, 'M', 'ې'), + (0xFBE8, 'M', 'ى'), + (0xFBEA, 'M', 'ئا'), + (0xFBEC, 'M', 'ئە'), + (0xFBEE, 'M', 'ئو'), + (0xFBF0, 'M', 'ئۇ'), + (0xFBF2, 'M', 'ئۆ'), + (0xFBF4, 'M', 'ئۈ'), + (0xFBF6, 'M', 'ئې'), + (0xFBF9, 'M', 'ئى'), + (0xFBFC, 'M', 'ی'), + (0xFC00, 'M', 'ئج'), + (0xFC01, 'M', 'ئح'), + (0xFC02, 'M', 'ئم'), + (0xFC03, 'M', 'ئى'), + (0xFC04, 'M', 'ئي'), + (0xFC05, 'M', 'بج'), + (0xFC06, 'M', 'بح'), + (0xFC07, 'M', 'بخ'), + (0xFC08, 'M', 'بم'), + (0xFC09, 'M', 'بى'), + (0xFC0A, 'M', 'بي'), + (0xFC0B, 'M', 'تج'), + (0xFC0C, 'M', 'تح'), + (0xFC0D, 'M', 'تخ'), + (0xFC0E, 'M', 'تم'), + (0xFC0F, 'M', 'تى'), + (0xFC10, 'M', 'تي'), + (0xFC11, 'M', 'ثج'), + (0xFC12, 'M', 'ثم'), + (0xFC13, 'M', 'ثى'), + (0xFC14, 'M', 'ثي'), + (0xFC15, 'M', 'جح'), + (0xFC16, 'M', 'جم'), + (0xFC17, 'M', 'حج'), + (0xFC18, 'M', 'حم'), + (0xFC19, 'M', 'خج'), + (0xFC1A, 'M', 'خح'), + (0xFC1B, 'M', 'خم'), + (0xFC1C, 'M', 'سج'), + (0xFC1D, 'M', 'سح'), + (0xFC1E, 'M', 'سخ'), + (0xFC1F, 'M', 'سم'), + (0xFC20, 'M', 'صح'), + (0xFC21, 'M', 'صم'), + (0xFC22, 'M', 'ضج'), + (0xFC23, 'M', 'ضح'), + (0xFC24, 'M', 'ضخ'), + (0xFC25, 'M', 'ضم'), + (0xFC26, 'M', 'طح'), + (0xFC27, 'M', 'طم'), + (0xFC28, 'M', 'ظم'), + (0xFC29, 'M', 'عج'), + (0xFC2A, 'M', 'عم'), + (0xFC2B, 'M', 'غج'), + (0xFC2C, 'M', 'غم'), + (0xFC2D, 'M', 'فج'), + (0xFC2E, 'M', 'فح'), + (0xFC2F, 'M', 'فخ'), + (0xFC30, 'M', 'فم'), + (0xFC31, 'M', 'فى'), + (0xFC32, 'M', 'في'), + (0xFC33, 'M', 'قح'), + (0xFC34, 'M', 'قم'), + (0xFC35, 'M', 'قى'), + (0xFC36, 'M', 'قي'), + (0xFC37, 'M', 'كا'), + (0xFC38, 'M', 'كج'), + (0xFC39, 'M', 'كح'), + (0xFC3A, 'M', 'كخ'), + (0xFC3B, 'M', 'كل'), + (0xFC3C, 'M', 'كم'), + (0xFC3D, 'M', 'كى'), + (0xFC3E, 'M', 'كي'), + (0xFC3F, 'M', 'لج'), + (0xFC40, 'M', 'لح'), + (0xFC41, 'M', 'لخ'), + (0xFC42, 'M', 'لم'), + (0xFC43, 'M', 'لى'), + (0xFC44, 'M', 'لي'), + (0xFC45, 'M', 'مج'), + (0xFC46, 'M', 'مح'), + (0xFC47, 'M', 'مخ'), + (0xFC48, 'M', 'مم'), + (0xFC49, 'M', 'مى'), + (0xFC4A, 'M', 'مي'), + (0xFC4B, 'M', 'نج'), + (0xFC4C, 'M', 'نح'), + (0xFC4D, 'M', 'نخ'), + (0xFC4E, 'M', 'نم'), + (0xFC4F, 'M', 'نى'), + (0xFC50, 'M', 'ني'), + (0xFC51, 'M', 'هج'), + (0xFC52, 'M', 'هم'), + (0xFC53, 'M', 'هى'), + (0xFC54, 'M', 'هي'), + (0xFC55, 'M', 'يج'), + (0xFC56, 'M', 'يح'), + ] + +def _seg_46() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFC57, 'M', 'يخ'), + (0xFC58, 'M', 'يم'), + (0xFC59, 'M', 'يى'), + (0xFC5A, 'M', 'يي'), + (0xFC5B, 'M', 'ذٰ'), + (0xFC5C, 'M', 'رٰ'), + (0xFC5D, 'M', 'ىٰ'), + (0xFC5E, '3', ' ٌّ'), + (0xFC5F, '3', ' ٍّ'), + (0xFC60, '3', ' َّ'), + (0xFC61, '3', ' ُّ'), + (0xFC62, '3', ' ِّ'), + (0xFC63, '3', ' ّٰ'), + (0xFC64, 'M', 'ئر'), + (0xFC65, 'M', 'ئز'), + (0xFC66, 'M', 'ئم'), + (0xFC67, 'M', 'ئن'), + (0xFC68, 'M', 'ئى'), + (0xFC69, 'M', 'ئي'), + (0xFC6A, 'M', 'بر'), + (0xFC6B, 'M', 'بز'), + (0xFC6C, 'M', 'بم'), + (0xFC6D, 'M', 'بن'), + (0xFC6E, 'M', 'بى'), + (0xFC6F, 'M', 'بي'), + (0xFC70, 'M', 'تر'), + (0xFC71, 'M', 'تز'), + (0xFC72, 'M', 'تم'), + (0xFC73, 'M', 'تن'), + (0xFC74, 'M', 'تى'), + (0xFC75, 'M', 'تي'), + (0xFC76, 'M', 'ثر'), + (0xFC77, 'M', 'ثز'), + (0xFC78, 'M', 'ثم'), + (0xFC79, 'M', 'ثن'), + (0xFC7A, 'M', 'ثى'), + (0xFC7B, 'M', 'ثي'), + (0xFC7C, 'M', 'فى'), + (0xFC7D, 'M', 'في'), + (0xFC7E, 'M', 'قى'), + (0xFC7F, 'M', 'قي'), + (0xFC80, 'M', 'كا'), + (0xFC81, 'M', 'كل'), + (0xFC82, 'M', 'كم'), + (0xFC83, 'M', 'كى'), + (0xFC84, 'M', 'كي'), + (0xFC85, 'M', 'لم'), + (0xFC86, 'M', 'لى'), + (0xFC87, 'M', 'لي'), + (0xFC88, 'M', 'ما'), + (0xFC89, 'M', 'مم'), + (0xFC8A, 'M', 'نر'), + (0xFC8B, 'M', 'نز'), + (0xFC8C, 'M', 'نم'), + (0xFC8D, 'M', 'نن'), + (0xFC8E, 'M', 'نى'), + (0xFC8F, 'M', 'ني'), + (0xFC90, 'M', 'ىٰ'), + (0xFC91, 'M', 'ير'), + (0xFC92, 'M', 'يز'), + (0xFC93, 'M', 'يم'), + (0xFC94, 'M', 'ين'), + (0xFC95, 'M', 'يى'), + (0xFC96, 'M', 'يي'), + (0xFC97, 'M', 'ئج'), + (0xFC98, 'M', 'ئح'), + (0xFC99, 'M', 'ئخ'), + (0xFC9A, 'M', 'ئم'), + (0xFC9B, 'M', 'ئه'), + (0xFC9C, 'M', 'بج'), + (0xFC9D, 'M', 'بح'), + (0xFC9E, 'M', 'بخ'), + (0xFC9F, 'M', 'بم'), + (0xFCA0, 'M', 'به'), + (0xFCA1, 'M', 'تج'), + (0xFCA2, 'M', 'تح'), + (0xFCA3, 'M', 'تخ'), + (0xFCA4, 'M', 'تم'), + (0xFCA5, 'M', 'ته'), + (0xFCA6, 'M', 'ثم'), + (0xFCA7, 'M', 'جح'), + (0xFCA8, 'M', 'جم'), + (0xFCA9, 'M', 'حج'), + (0xFCAA, 'M', 'حم'), + (0xFCAB, 'M', 'خج'), + (0xFCAC, 'M', 'خم'), + (0xFCAD, 'M', 'سج'), + (0xFCAE, 'M', 'سح'), + (0xFCAF, 'M', 'سخ'), + (0xFCB0, 'M', 'سم'), + (0xFCB1, 'M', 'صح'), + (0xFCB2, 'M', 'صخ'), + (0xFCB3, 'M', 'صم'), + (0xFCB4, 'M', 'ضج'), + (0xFCB5, 'M', 'ضح'), + (0xFCB6, 'M', 'ضخ'), + (0xFCB7, 'M', 'ضم'), + (0xFCB8, 'M', 'طح'), + (0xFCB9, 'M', 'ظم'), + (0xFCBA, 'M', 'عج'), + ] + +def _seg_47() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFCBB, 'M', 'عم'), + (0xFCBC, 'M', 'غج'), + (0xFCBD, 'M', 'غم'), + (0xFCBE, 'M', 'فج'), + (0xFCBF, 'M', 'فح'), + (0xFCC0, 'M', 'فخ'), + (0xFCC1, 'M', 'فم'), + (0xFCC2, 'M', 'قح'), + (0xFCC3, 'M', 'قم'), + (0xFCC4, 'M', 'كج'), + (0xFCC5, 'M', 'كح'), + (0xFCC6, 'M', 'كخ'), + (0xFCC7, 'M', 'كل'), + (0xFCC8, 'M', 'كم'), + (0xFCC9, 'M', 'لج'), + (0xFCCA, 'M', 'لح'), + (0xFCCB, 'M', 'لخ'), + (0xFCCC, 'M', 'لم'), + (0xFCCD, 'M', 'له'), + (0xFCCE, 'M', 'مج'), + (0xFCCF, 'M', 'مح'), + (0xFCD0, 'M', 'مخ'), + (0xFCD1, 'M', 'مم'), + (0xFCD2, 'M', 'نج'), + (0xFCD3, 'M', 'نح'), + (0xFCD4, 'M', 'نخ'), + (0xFCD5, 'M', 'نم'), + (0xFCD6, 'M', 'نه'), + (0xFCD7, 'M', 'هج'), + (0xFCD8, 'M', 'هم'), + (0xFCD9, 'M', 'هٰ'), + (0xFCDA, 'M', 'يج'), + (0xFCDB, 'M', 'يح'), + (0xFCDC, 'M', 'يخ'), + (0xFCDD, 'M', 'يم'), + (0xFCDE, 'M', 'يه'), + (0xFCDF, 'M', 'ئم'), + (0xFCE0, 'M', 'ئه'), + (0xFCE1, 'M', 'بم'), + (0xFCE2, 'M', 'به'), + (0xFCE3, 'M', 'تم'), + (0xFCE4, 'M', 'ته'), + (0xFCE5, 'M', 'ثم'), + (0xFCE6, 'M', 'ثه'), + (0xFCE7, 'M', 'سم'), + (0xFCE8, 'M', 'سه'), + (0xFCE9, 'M', 'شم'), + (0xFCEA, 'M', 'شه'), + (0xFCEB, 'M', 'كل'), + (0xFCEC, 'M', 'كم'), + (0xFCED, 'M', 'لم'), + (0xFCEE, 'M', 'نم'), + (0xFCEF, 'M', 'نه'), + (0xFCF0, 'M', 'يم'), + (0xFCF1, 'M', 'يه'), + (0xFCF2, 'M', 'ـَّ'), + (0xFCF3, 'M', 'ـُّ'), + (0xFCF4, 'M', 'ـِّ'), + (0xFCF5, 'M', 'طى'), + (0xFCF6, 'M', 'طي'), + (0xFCF7, 'M', 'عى'), + (0xFCF8, 'M', 'عي'), + (0xFCF9, 'M', 'غى'), + (0xFCFA, 'M', 'غي'), + (0xFCFB, 'M', 'سى'), + (0xFCFC, 'M', 'سي'), + (0xFCFD, 'M', 'شى'), + (0xFCFE, 'M', 'شي'), + (0xFCFF, 'M', 'حى'), + (0xFD00, 'M', 'حي'), + (0xFD01, 'M', 'جى'), + (0xFD02, 'M', 'جي'), + (0xFD03, 'M', 'خى'), + (0xFD04, 'M', 'خي'), + (0xFD05, 'M', 'صى'), + (0xFD06, 'M', 'صي'), + (0xFD07, 'M', 'ضى'), + (0xFD08, 'M', 'ضي'), + (0xFD09, 'M', 'شج'), + (0xFD0A, 'M', 'شح'), + (0xFD0B, 'M', 'شخ'), + (0xFD0C, 'M', 'شم'), + (0xFD0D, 'M', 'شر'), + (0xFD0E, 'M', 'سر'), + (0xFD0F, 'M', 'صر'), + (0xFD10, 'M', 'ضر'), + (0xFD11, 'M', 'طى'), + (0xFD12, 'M', 'طي'), + (0xFD13, 'M', 'عى'), + (0xFD14, 'M', 'عي'), + (0xFD15, 'M', 'غى'), + (0xFD16, 'M', 'غي'), + (0xFD17, 'M', 'سى'), + (0xFD18, 'M', 'سي'), + (0xFD19, 'M', 'شى'), + (0xFD1A, 'M', 'شي'), + (0xFD1B, 'M', 'حى'), + (0xFD1C, 'M', 'حي'), + (0xFD1D, 'M', 'جى'), + (0xFD1E, 'M', 'جي'), + ] + +def _seg_48() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFD1F, 'M', 'خى'), + (0xFD20, 'M', 'خي'), + (0xFD21, 'M', 'صى'), + (0xFD22, 'M', 'صي'), + (0xFD23, 'M', 'ضى'), + (0xFD24, 'M', 'ضي'), + (0xFD25, 'M', 'شج'), + (0xFD26, 'M', 'شح'), + (0xFD27, 'M', 'شخ'), + (0xFD28, 'M', 'شم'), + (0xFD29, 'M', 'شر'), + (0xFD2A, 'M', 'سر'), + (0xFD2B, 'M', 'صر'), + (0xFD2C, 'M', 'ضر'), + (0xFD2D, 'M', 'شج'), + (0xFD2E, 'M', 'شح'), + (0xFD2F, 'M', 'شخ'), + (0xFD30, 'M', 'شم'), + (0xFD31, 'M', 'سه'), + (0xFD32, 'M', 'شه'), + (0xFD33, 'M', 'طم'), + (0xFD34, 'M', 'سج'), + (0xFD35, 'M', 'سح'), + (0xFD36, 'M', 'سخ'), + (0xFD37, 'M', 'شج'), + (0xFD38, 'M', 'شح'), + (0xFD39, 'M', 'شخ'), + (0xFD3A, 'M', 'طم'), + (0xFD3B, 'M', 'ظم'), + (0xFD3C, 'M', 'اً'), + (0xFD3E, 'V'), + (0xFD50, 'M', 'تجم'), + (0xFD51, 'M', 'تحج'), + (0xFD53, 'M', 'تحم'), + (0xFD54, 'M', 'تخم'), + (0xFD55, 'M', 'تمج'), + (0xFD56, 'M', 'تمح'), + (0xFD57, 'M', 'تمخ'), + (0xFD58, 'M', 'جمح'), + (0xFD5A, 'M', 'حمي'), + (0xFD5B, 'M', 'حمى'), + (0xFD5C, 'M', 'سحج'), + (0xFD5D, 'M', 'سجح'), + (0xFD5E, 'M', 'سجى'), + (0xFD5F, 'M', 'سمح'), + (0xFD61, 'M', 'سمج'), + (0xFD62, 'M', 'سمم'), + (0xFD64, 'M', 'صحح'), + (0xFD66, 'M', 'صمم'), + (0xFD67, 'M', 'شحم'), + (0xFD69, 'M', 'شجي'), + (0xFD6A, 'M', 'شمخ'), + (0xFD6C, 'M', 'شمم'), + (0xFD6E, 'M', 'ضحى'), + (0xFD6F, 'M', 'ضخم'), + (0xFD71, 'M', 'طمح'), + (0xFD73, 'M', 'طمم'), + (0xFD74, 'M', 'طمي'), + (0xFD75, 'M', 'عجم'), + (0xFD76, 'M', 'عمم'), + (0xFD78, 'M', 'عمى'), + (0xFD79, 'M', 'غمم'), + (0xFD7A, 'M', 'غمي'), + (0xFD7B, 'M', 'غمى'), + (0xFD7C, 'M', 'فخم'), + (0xFD7E, 'M', 'قمح'), + (0xFD7F, 'M', 'قمم'), + (0xFD80, 'M', 'لحم'), + (0xFD81, 'M', 'لحي'), + (0xFD82, 'M', 'لحى'), + (0xFD83, 'M', 'لجج'), + (0xFD85, 'M', 'لخم'), + (0xFD87, 'M', 'لمح'), + (0xFD89, 'M', 'محج'), + (0xFD8A, 'M', 'محم'), + (0xFD8B, 'M', 'محي'), + (0xFD8C, 'M', 'مجح'), + (0xFD8D, 'M', 'مجم'), + (0xFD8E, 'M', 'مخج'), + (0xFD8F, 'M', 'مخم'), + (0xFD90, 'X'), + (0xFD92, 'M', 'مجخ'), + (0xFD93, 'M', 'همج'), + (0xFD94, 'M', 'همم'), + (0xFD95, 'M', 'نحم'), + (0xFD96, 'M', 'نحى'), + (0xFD97, 'M', 'نجم'), + (0xFD99, 'M', 'نجى'), + (0xFD9A, 'M', 'نمي'), + (0xFD9B, 'M', 'نمى'), + (0xFD9C, 'M', 'يمم'), + (0xFD9E, 'M', 'بخي'), + (0xFD9F, 'M', 'تجي'), + (0xFDA0, 'M', 'تجى'), + (0xFDA1, 'M', 'تخي'), + (0xFDA2, 'M', 'تخى'), + (0xFDA3, 'M', 'تمي'), + (0xFDA4, 'M', 'تمى'), + (0xFDA5, 'M', 'جمي'), + (0xFDA6, 'M', 'جحى'), + ] + +def _seg_49() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFDA7, 'M', 'جمى'), + (0xFDA8, 'M', 'سخى'), + (0xFDA9, 'M', 'صحي'), + (0xFDAA, 'M', 'شحي'), + (0xFDAB, 'M', 'ضحي'), + (0xFDAC, 'M', 'لجي'), + (0xFDAD, 'M', 'لمي'), + (0xFDAE, 'M', 'يحي'), + (0xFDAF, 'M', 'يجي'), + (0xFDB0, 'M', 'يمي'), + (0xFDB1, 'M', 'ممي'), + (0xFDB2, 'M', 'قمي'), + (0xFDB3, 'M', 'نحي'), + (0xFDB4, 'M', 'قمح'), + (0xFDB5, 'M', 'لحم'), + (0xFDB6, 'M', 'عمي'), + (0xFDB7, 'M', 'كمي'), + (0xFDB8, 'M', 'نجح'), + (0xFDB9, 'M', 'مخي'), + (0xFDBA, 'M', 'لجم'), + (0xFDBB, 'M', 'كمم'), + (0xFDBC, 'M', 'لجم'), + (0xFDBD, 'M', 'نجح'), + (0xFDBE, 'M', 'جحي'), + (0xFDBF, 'M', 'حجي'), + (0xFDC0, 'M', 'مجي'), + (0xFDC1, 'M', 'فمي'), + (0xFDC2, 'M', 'بحي'), + (0xFDC3, 'M', 'كمم'), + (0xFDC4, 'M', 'عجم'), + (0xFDC5, 'M', 'صمم'), + (0xFDC6, 'M', 'سخي'), + (0xFDC7, 'M', 'نجي'), + (0xFDC8, 'X'), + (0xFDCF, 'V'), + (0xFDD0, 'X'), + (0xFDF0, 'M', 'صلے'), + (0xFDF1, 'M', 'قلے'), + (0xFDF2, 'M', 'الله'), + (0xFDF3, 'M', 'اكبر'), + (0xFDF4, 'M', 'محمد'), + (0xFDF5, 'M', 'صلعم'), + (0xFDF6, 'M', 'رسول'), + (0xFDF7, 'M', 'عليه'), + (0xFDF8, 'M', 'وسلم'), + (0xFDF9, 'M', 'صلى'), + (0xFDFA, '3', 'صلى الله عليه وسلم'), + (0xFDFB, '3', 'جل جلاله'), + (0xFDFC, 'M', 'ریال'), + (0xFDFD, 'V'), + (0xFE00, 'I'), + (0xFE10, '3', ','), + (0xFE11, 'M', '、'), + (0xFE12, 'X'), + (0xFE13, '3', ':'), + (0xFE14, '3', ';'), + (0xFE15, '3', '!'), + (0xFE16, '3', '?'), + (0xFE17, 'M', '〖'), + (0xFE18, 'M', '〗'), + (0xFE19, 'X'), + (0xFE20, 'V'), + (0xFE30, 'X'), + (0xFE31, 'M', '—'), + (0xFE32, 'M', '–'), + (0xFE33, '3', '_'), + (0xFE35, '3', '('), + (0xFE36, '3', ')'), + (0xFE37, '3', '{'), + (0xFE38, '3', '}'), + (0xFE39, 'M', '〔'), + (0xFE3A, 'M', '〕'), + (0xFE3B, 'M', '【'), + (0xFE3C, 'M', '】'), + (0xFE3D, 'M', '《'), + (0xFE3E, 'M', '》'), + (0xFE3F, 'M', '〈'), + (0xFE40, 'M', '〉'), + (0xFE41, 'M', '「'), + (0xFE42, 'M', '」'), + (0xFE43, 'M', '『'), + (0xFE44, 'M', '』'), + (0xFE45, 'V'), + (0xFE47, '3', '['), + (0xFE48, '3', ']'), + (0xFE49, '3', ' ̅'), + (0xFE4D, '3', '_'), + (0xFE50, '3', ','), + (0xFE51, 'M', '、'), + (0xFE52, 'X'), + (0xFE54, '3', ';'), + (0xFE55, '3', ':'), + (0xFE56, '3', '?'), + (0xFE57, '3', '!'), + (0xFE58, 'M', '—'), + (0xFE59, '3', '('), + (0xFE5A, '3', ')'), + (0xFE5B, '3', '{'), + (0xFE5C, '3', '}'), + (0xFE5D, 'M', '〔'), + ] + +def _seg_50() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFE5E, 'M', '〕'), + (0xFE5F, '3', '#'), + (0xFE60, '3', '&'), + (0xFE61, '3', '*'), + (0xFE62, '3', '+'), + (0xFE63, 'M', '-'), + (0xFE64, '3', '<'), + (0xFE65, '3', '>'), + (0xFE66, '3', '='), + (0xFE67, 'X'), + (0xFE68, '3', '\\'), + (0xFE69, '3', '$'), + (0xFE6A, '3', '%'), + (0xFE6B, '3', '@'), + (0xFE6C, 'X'), + (0xFE70, '3', ' ً'), + (0xFE71, 'M', 'ـً'), + (0xFE72, '3', ' ٌ'), + (0xFE73, 'V'), + (0xFE74, '3', ' ٍ'), + (0xFE75, 'X'), + (0xFE76, '3', ' َ'), + (0xFE77, 'M', 'ـَ'), + (0xFE78, '3', ' ُ'), + (0xFE79, 'M', 'ـُ'), + (0xFE7A, '3', ' ِ'), + (0xFE7B, 'M', 'ـِ'), + (0xFE7C, '3', ' ّ'), + (0xFE7D, 'M', 'ـّ'), + (0xFE7E, '3', ' ْ'), + (0xFE7F, 'M', 'ـْ'), + (0xFE80, 'M', 'ء'), + (0xFE81, 'M', 'آ'), + (0xFE83, 'M', 'أ'), + (0xFE85, 'M', 'ؤ'), + (0xFE87, 'M', 'إ'), + (0xFE89, 'M', 'ئ'), + (0xFE8D, 'M', 'ا'), + (0xFE8F, 'M', 'ب'), + (0xFE93, 'M', 'ة'), + (0xFE95, 'M', 'ت'), + (0xFE99, 'M', 'ث'), + (0xFE9D, 'M', 'ج'), + (0xFEA1, 'M', 'ح'), + (0xFEA5, 'M', 'خ'), + (0xFEA9, 'M', 'د'), + (0xFEAB, 'M', 'ذ'), + (0xFEAD, 'M', 'ر'), + (0xFEAF, 'M', 'ز'), + (0xFEB1, 'M', 'س'), + (0xFEB5, 'M', 'ش'), + (0xFEB9, 'M', 'ص'), + (0xFEBD, 'M', 'ض'), + (0xFEC1, 'M', 'ط'), + (0xFEC5, 'M', 'ظ'), + (0xFEC9, 'M', 'ع'), + (0xFECD, 'M', 'غ'), + (0xFED1, 'M', 'ف'), + (0xFED5, 'M', 'ق'), + (0xFED9, 'M', 'ك'), + (0xFEDD, 'M', 'ل'), + (0xFEE1, 'M', 'م'), + (0xFEE5, 'M', 'ن'), + (0xFEE9, 'M', 'ه'), + (0xFEED, 'M', 'و'), + (0xFEEF, 'M', 'ى'), + (0xFEF1, 'M', 'ي'), + (0xFEF5, 'M', 'لآ'), + (0xFEF7, 'M', 'لأ'), + (0xFEF9, 'M', 'لإ'), + (0xFEFB, 'M', 'لا'), + (0xFEFD, 'X'), + (0xFEFF, 'I'), + (0xFF00, 'X'), + (0xFF01, '3', '!'), + (0xFF02, '3', '"'), + (0xFF03, '3', '#'), + (0xFF04, '3', '$'), + (0xFF05, '3', '%'), + (0xFF06, '3', '&'), + (0xFF07, '3', '\''), + (0xFF08, '3', '('), + (0xFF09, '3', ')'), + (0xFF0A, '3', '*'), + (0xFF0B, '3', '+'), + (0xFF0C, '3', ','), + (0xFF0D, 'M', '-'), + (0xFF0E, 'M', '.'), + (0xFF0F, '3', '/'), + (0xFF10, 'M', '0'), + (0xFF11, 'M', '1'), + (0xFF12, 'M', '2'), + (0xFF13, 'M', '3'), + (0xFF14, 'M', '4'), + (0xFF15, 'M', '5'), + (0xFF16, 'M', '6'), + (0xFF17, 'M', '7'), + (0xFF18, 'M', '8'), + (0xFF19, 'M', '9'), + (0xFF1A, '3', ':'), + ] + +def _seg_51() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFF1B, '3', ';'), + (0xFF1C, '3', '<'), + (0xFF1D, '3', '='), + (0xFF1E, '3', '>'), + (0xFF1F, '3', '?'), + (0xFF20, '3', '@'), + (0xFF21, 'M', 'a'), + (0xFF22, 'M', 'b'), + (0xFF23, 'M', 'c'), + (0xFF24, 'M', 'd'), + (0xFF25, 'M', 'e'), + (0xFF26, 'M', 'f'), + (0xFF27, 'M', 'g'), + (0xFF28, 'M', 'h'), + (0xFF29, 'M', 'i'), + (0xFF2A, 'M', 'j'), + (0xFF2B, 'M', 'k'), + (0xFF2C, 'M', 'l'), + (0xFF2D, 'M', 'm'), + (0xFF2E, 'M', 'n'), + (0xFF2F, 'M', 'o'), + (0xFF30, 'M', 'p'), + (0xFF31, 'M', 'q'), + (0xFF32, 'M', 'r'), + (0xFF33, 'M', 's'), + (0xFF34, 'M', 't'), + (0xFF35, 'M', 'u'), + (0xFF36, 'M', 'v'), + (0xFF37, 'M', 'w'), + (0xFF38, 'M', 'x'), + (0xFF39, 'M', 'y'), + (0xFF3A, 'M', 'z'), + (0xFF3B, '3', '['), + (0xFF3C, '3', '\\'), + (0xFF3D, '3', ']'), + (0xFF3E, '3', '^'), + (0xFF3F, '3', '_'), + (0xFF40, '3', '`'), + (0xFF41, 'M', 'a'), + (0xFF42, 'M', 'b'), + (0xFF43, 'M', 'c'), + (0xFF44, 'M', 'd'), + (0xFF45, 'M', 'e'), + (0xFF46, 'M', 'f'), + (0xFF47, 'M', 'g'), + (0xFF48, 'M', 'h'), + (0xFF49, 'M', 'i'), + (0xFF4A, 'M', 'j'), + (0xFF4B, 'M', 'k'), + (0xFF4C, 'M', 'l'), + (0xFF4D, 'M', 'm'), + (0xFF4E, 'M', 'n'), + (0xFF4F, 'M', 'o'), + (0xFF50, 'M', 'p'), + (0xFF51, 'M', 'q'), + (0xFF52, 'M', 'r'), + (0xFF53, 'M', 's'), + (0xFF54, 'M', 't'), + (0xFF55, 'M', 'u'), + (0xFF56, 'M', 'v'), + (0xFF57, 'M', 'w'), + (0xFF58, 'M', 'x'), + (0xFF59, 'M', 'y'), + (0xFF5A, 'M', 'z'), + (0xFF5B, '3', '{'), + (0xFF5C, '3', '|'), + (0xFF5D, '3', '}'), + (0xFF5E, '3', '~'), + (0xFF5F, 'M', '⦅'), + (0xFF60, 'M', '⦆'), + (0xFF61, 'M', '.'), + (0xFF62, 'M', '「'), + (0xFF63, 'M', '」'), + (0xFF64, 'M', '、'), + (0xFF65, 'M', '・'), + (0xFF66, 'M', 'ヲ'), + (0xFF67, 'M', 'ァ'), + (0xFF68, 'M', 'ィ'), + (0xFF69, 'M', 'ゥ'), + (0xFF6A, 'M', 'ェ'), + (0xFF6B, 'M', 'ォ'), + (0xFF6C, 'M', 'ャ'), + (0xFF6D, 'M', 'ュ'), + (0xFF6E, 'M', 'ョ'), + (0xFF6F, 'M', 'ッ'), + (0xFF70, 'M', 'ー'), + (0xFF71, 'M', 'ア'), + (0xFF72, 'M', 'イ'), + (0xFF73, 'M', 'ウ'), + (0xFF74, 'M', 'エ'), + (0xFF75, 'M', 'オ'), + (0xFF76, 'M', 'カ'), + (0xFF77, 'M', 'キ'), + (0xFF78, 'M', 'ク'), + (0xFF79, 'M', 'ケ'), + (0xFF7A, 'M', 'コ'), + (0xFF7B, 'M', 'サ'), + (0xFF7C, 'M', 'シ'), + (0xFF7D, 'M', 'ス'), + (0xFF7E, 'M', 'セ'), + ] + +def _seg_52() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFF7F, 'M', 'ソ'), + (0xFF80, 'M', 'タ'), + (0xFF81, 'M', 'チ'), + (0xFF82, 'M', 'ツ'), + (0xFF83, 'M', 'テ'), + (0xFF84, 'M', 'ト'), + (0xFF85, 'M', 'ナ'), + (0xFF86, 'M', 'ニ'), + (0xFF87, 'M', 'ヌ'), + (0xFF88, 'M', 'ネ'), + (0xFF89, 'M', 'ノ'), + (0xFF8A, 'M', 'ハ'), + (0xFF8B, 'M', 'ヒ'), + (0xFF8C, 'M', 'フ'), + (0xFF8D, 'M', 'ヘ'), + (0xFF8E, 'M', 'ホ'), + (0xFF8F, 'M', 'マ'), + (0xFF90, 'M', 'ミ'), + (0xFF91, 'M', 'ム'), + (0xFF92, 'M', 'メ'), + (0xFF93, 'M', 'モ'), + (0xFF94, 'M', 'ヤ'), + (0xFF95, 'M', 'ユ'), + (0xFF96, 'M', 'ヨ'), + (0xFF97, 'M', 'ラ'), + (0xFF98, 'M', 'リ'), + (0xFF99, 'M', 'ル'), + (0xFF9A, 'M', 'レ'), + (0xFF9B, 'M', 'ロ'), + (0xFF9C, 'M', 'ワ'), + (0xFF9D, 'M', 'ン'), + (0xFF9E, 'M', '゙'), + (0xFF9F, 'M', '゚'), + (0xFFA0, 'X'), + (0xFFA1, 'M', 'ᄀ'), + (0xFFA2, 'M', 'ᄁ'), + (0xFFA3, 'M', 'ᆪ'), + (0xFFA4, 'M', 'ᄂ'), + (0xFFA5, 'M', 'ᆬ'), + (0xFFA6, 'M', 'ᆭ'), + (0xFFA7, 'M', 'ᄃ'), + (0xFFA8, 'M', 'ᄄ'), + (0xFFA9, 'M', 'ᄅ'), + (0xFFAA, 'M', 'ᆰ'), + (0xFFAB, 'M', 'ᆱ'), + (0xFFAC, 'M', 'ᆲ'), + (0xFFAD, 'M', 'ᆳ'), + (0xFFAE, 'M', 'ᆴ'), + (0xFFAF, 'M', 'ᆵ'), + (0xFFB0, 'M', 'ᄚ'), + (0xFFB1, 'M', 'ᄆ'), + (0xFFB2, 'M', 'ᄇ'), + (0xFFB3, 'M', 'ᄈ'), + (0xFFB4, 'M', 'ᄡ'), + (0xFFB5, 'M', 'ᄉ'), + (0xFFB6, 'M', 'ᄊ'), + (0xFFB7, 'M', 'ᄋ'), + (0xFFB8, 'M', 'ᄌ'), + (0xFFB9, 'M', 'ᄍ'), + (0xFFBA, 'M', 'ᄎ'), + (0xFFBB, 'M', 'ᄏ'), + (0xFFBC, 'M', 'ᄐ'), + (0xFFBD, 'M', 'ᄑ'), + (0xFFBE, 'M', 'ᄒ'), + (0xFFBF, 'X'), + (0xFFC2, 'M', 'ᅡ'), + (0xFFC3, 'M', 'ᅢ'), + (0xFFC4, 'M', 'ᅣ'), + (0xFFC5, 'M', 'ᅤ'), + (0xFFC6, 'M', 'ᅥ'), + (0xFFC7, 'M', 'ᅦ'), + (0xFFC8, 'X'), + (0xFFCA, 'M', 'ᅧ'), + (0xFFCB, 'M', 'ᅨ'), + (0xFFCC, 'M', 'ᅩ'), + (0xFFCD, 'M', 'ᅪ'), + (0xFFCE, 'M', 'ᅫ'), + (0xFFCF, 'M', 'ᅬ'), + (0xFFD0, 'X'), + (0xFFD2, 'M', 'ᅭ'), + (0xFFD3, 'M', 'ᅮ'), + (0xFFD4, 'M', 'ᅯ'), + (0xFFD5, 'M', 'ᅰ'), + (0xFFD6, 'M', 'ᅱ'), + (0xFFD7, 'M', 'ᅲ'), + (0xFFD8, 'X'), + (0xFFDA, 'M', 'ᅳ'), + (0xFFDB, 'M', 'ᅴ'), + (0xFFDC, 'M', 'ᅵ'), + (0xFFDD, 'X'), + (0xFFE0, 'M', '¢'), + (0xFFE1, 'M', '£'), + (0xFFE2, 'M', '¬'), + (0xFFE3, '3', ' ̄'), + (0xFFE4, 'M', '¦'), + (0xFFE5, 'M', '¥'), + (0xFFE6, 'M', '₩'), + (0xFFE7, 'X'), + (0xFFE8, 'M', '│'), + (0xFFE9, 'M', '←'), + ] + +def _seg_53() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFFEA, 'M', '↑'), + (0xFFEB, 'M', '→'), + (0xFFEC, 'M', '↓'), + (0xFFED, 'M', '■'), + (0xFFEE, 'M', '○'), + (0xFFEF, 'X'), + (0x10000, 'V'), + (0x1000C, 'X'), + (0x1000D, 'V'), + (0x10027, 'X'), + (0x10028, 'V'), + (0x1003B, 'X'), + (0x1003C, 'V'), + (0x1003E, 'X'), + (0x1003F, 'V'), + (0x1004E, 'X'), + (0x10050, 'V'), + (0x1005E, 'X'), + (0x10080, 'V'), + (0x100FB, 'X'), + (0x10100, 'V'), + (0x10103, 'X'), + (0x10107, 'V'), + (0x10134, 'X'), + (0x10137, 'V'), + (0x1018F, 'X'), + (0x10190, 'V'), + (0x1019D, 'X'), + (0x101A0, 'V'), + (0x101A1, 'X'), + (0x101D0, 'V'), + (0x101FE, 'X'), + (0x10280, 'V'), + (0x1029D, 'X'), + (0x102A0, 'V'), + (0x102D1, 'X'), + (0x102E0, 'V'), + (0x102FC, 'X'), + (0x10300, 'V'), + (0x10324, 'X'), + (0x1032D, 'V'), + (0x1034B, 'X'), + (0x10350, 'V'), + (0x1037B, 'X'), + (0x10380, 'V'), + (0x1039E, 'X'), + (0x1039F, 'V'), + (0x103C4, 'X'), + (0x103C8, 'V'), + (0x103D6, 'X'), + (0x10400, 'M', '𐐨'), + (0x10401, 'M', '𐐩'), + (0x10402, 'M', '𐐪'), + (0x10403, 'M', '𐐫'), + (0x10404, 'M', '𐐬'), + (0x10405, 'M', '𐐭'), + (0x10406, 'M', '𐐮'), + (0x10407, 'M', '𐐯'), + (0x10408, 'M', '𐐰'), + (0x10409, 'M', '𐐱'), + (0x1040A, 'M', '𐐲'), + (0x1040B, 'M', '𐐳'), + (0x1040C, 'M', '𐐴'), + (0x1040D, 'M', '𐐵'), + (0x1040E, 'M', '𐐶'), + (0x1040F, 'M', '𐐷'), + (0x10410, 'M', '𐐸'), + (0x10411, 'M', '𐐹'), + (0x10412, 'M', '𐐺'), + (0x10413, 'M', '𐐻'), + (0x10414, 'M', '𐐼'), + (0x10415, 'M', '𐐽'), + (0x10416, 'M', '𐐾'), + (0x10417, 'M', '𐐿'), + (0x10418, 'M', '𐑀'), + (0x10419, 'M', '𐑁'), + (0x1041A, 'M', '𐑂'), + (0x1041B, 'M', '𐑃'), + (0x1041C, 'M', '𐑄'), + (0x1041D, 'M', '𐑅'), + (0x1041E, 'M', '𐑆'), + (0x1041F, 'M', '𐑇'), + (0x10420, 'M', '𐑈'), + (0x10421, 'M', '𐑉'), + (0x10422, 'M', '𐑊'), + (0x10423, 'M', '𐑋'), + (0x10424, 'M', '𐑌'), + (0x10425, 'M', '𐑍'), + (0x10426, 'M', '𐑎'), + (0x10427, 'M', '𐑏'), + (0x10428, 'V'), + (0x1049E, 'X'), + (0x104A0, 'V'), + (0x104AA, 'X'), + (0x104B0, 'M', '𐓘'), + (0x104B1, 'M', '𐓙'), + (0x104B2, 'M', '𐓚'), + (0x104B3, 'M', '𐓛'), + (0x104B4, 'M', '𐓜'), + (0x104B5, 'M', '𐓝'), + ] + +def _seg_54() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x104B6, 'M', '𐓞'), + (0x104B7, 'M', '𐓟'), + (0x104B8, 'M', '𐓠'), + (0x104B9, 'M', '𐓡'), + (0x104BA, 'M', '𐓢'), + (0x104BB, 'M', '𐓣'), + (0x104BC, 'M', '𐓤'), + (0x104BD, 'M', '𐓥'), + (0x104BE, 'M', '𐓦'), + (0x104BF, 'M', '𐓧'), + (0x104C0, 'M', '𐓨'), + (0x104C1, 'M', '𐓩'), + (0x104C2, 'M', '𐓪'), + (0x104C3, 'M', '𐓫'), + (0x104C4, 'M', '𐓬'), + (0x104C5, 'M', '𐓭'), + (0x104C6, 'M', '𐓮'), + (0x104C7, 'M', '𐓯'), + (0x104C8, 'M', '𐓰'), + (0x104C9, 'M', '𐓱'), + (0x104CA, 'M', '𐓲'), + (0x104CB, 'M', '𐓳'), + (0x104CC, 'M', '𐓴'), + (0x104CD, 'M', '𐓵'), + (0x104CE, 'M', '𐓶'), + (0x104CF, 'M', '𐓷'), + (0x104D0, 'M', '𐓸'), + (0x104D1, 'M', '𐓹'), + (0x104D2, 'M', '𐓺'), + (0x104D3, 'M', '𐓻'), + (0x104D4, 'X'), + (0x104D8, 'V'), + (0x104FC, 'X'), + (0x10500, 'V'), + (0x10528, 'X'), + (0x10530, 'V'), + (0x10564, 'X'), + (0x1056F, 'V'), + (0x10570, 'M', '𐖗'), + (0x10571, 'M', '𐖘'), + (0x10572, 'M', '𐖙'), + (0x10573, 'M', '𐖚'), + (0x10574, 'M', '𐖛'), + (0x10575, 'M', '𐖜'), + (0x10576, 'M', '𐖝'), + (0x10577, 'M', '𐖞'), + (0x10578, 'M', '𐖟'), + (0x10579, 'M', '𐖠'), + (0x1057A, 'M', '𐖡'), + (0x1057B, 'X'), + (0x1057C, 'M', '𐖣'), + (0x1057D, 'M', '𐖤'), + (0x1057E, 'M', '𐖥'), + (0x1057F, 'M', '𐖦'), + (0x10580, 'M', '𐖧'), + (0x10581, 'M', '𐖨'), + (0x10582, 'M', '𐖩'), + (0x10583, 'M', '𐖪'), + (0x10584, 'M', '𐖫'), + (0x10585, 'M', '𐖬'), + (0x10586, 'M', '𐖭'), + (0x10587, 'M', '𐖮'), + (0x10588, 'M', '𐖯'), + (0x10589, 'M', '𐖰'), + (0x1058A, 'M', '𐖱'), + (0x1058B, 'X'), + (0x1058C, 'M', '𐖳'), + (0x1058D, 'M', '𐖴'), + (0x1058E, 'M', '𐖵'), + (0x1058F, 'M', '𐖶'), + (0x10590, 'M', '𐖷'), + (0x10591, 'M', '𐖸'), + (0x10592, 'M', '𐖹'), + (0x10593, 'X'), + (0x10594, 'M', '𐖻'), + (0x10595, 'M', '𐖼'), + (0x10596, 'X'), + (0x10597, 'V'), + (0x105A2, 'X'), + (0x105A3, 'V'), + (0x105B2, 'X'), + (0x105B3, 'V'), + (0x105BA, 'X'), + (0x105BB, 'V'), + (0x105BD, 'X'), + (0x10600, 'V'), + (0x10737, 'X'), + (0x10740, 'V'), + (0x10756, 'X'), + (0x10760, 'V'), + (0x10768, 'X'), + (0x10780, 'V'), + (0x10781, 'M', 'ː'), + (0x10782, 'M', 'ˑ'), + (0x10783, 'M', 'æ'), + (0x10784, 'M', 'ʙ'), + (0x10785, 'M', 'ɓ'), + (0x10786, 'X'), + (0x10787, 'M', 'ʣ'), + (0x10788, 'M', 'ꭦ'), + ] + +def _seg_55() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x10789, 'M', 'ʥ'), + (0x1078A, 'M', 'ʤ'), + (0x1078B, 'M', 'ɖ'), + (0x1078C, 'M', 'ɗ'), + (0x1078D, 'M', 'ᶑ'), + (0x1078E, 'M', 'ɘ'), + (0x1078F, 'M', 'ɞ'), + (0x10790, 'M', 'ʩ'), + (0x10791, 'M', 'ɤ'), + (0x10792, 'M', 'ɢ'), + (0x10793, 'M', 'ɠ'), + (0x10794, 'M', 'ʛ'), + (0x10795, 'M', 'ħ'), + (0x10796, 'M', 'ʜ'), + (0x10797, 'M', 'ɧ'), + (0x10798, 'M', 'ʄ'), + (0x10799, 'M', 'ʪ'), + (0x1079A, 'M', 'ʫ'), + (0x1079B, 'M', 'ɬ'), + (0x1079C, 'M', '𝼄'), + (0x1079D, 'M', 'ꞎ'), + (0x1079E, 'M', 'ɮ'), + (0x1079F, 'M', '𝼅'), + (0x107A0, 'M', 'ʎ'), + (0x107A1, 'M', '𝼆'), + (0x107A2, 'M', 'ø'), + (0x107A3, 'M', 'ɶ'), + (0x107A4, 'M', 'ɷ'), + (0x107A5, 'M', 'q'), + (0x107A6, 'M', 'ɺ'), + (0x107A7, 'M', '𝼈'), + (0x107A8, 'M', 'ɽ'), + (0x107A9, 'M', 'ɾ'), + (0x107AA, 'M', 'ʀ'), + (0x107AB, 'M', 'ʨ'), + (0x107AC, 'M', 'ʦ'), + (0x107AD, 'M', 'ꭧ'), + (0x107AE, 'M', 'ʧ'), + (0x107AF, 'M', 'ʈ'), + (0x107B0, 'M', 'ⱱ'), + (0x107B1, 'X'), + (0x107B2, 'M', 'ʏ'), + (0x107B3, 'M', 'ʡ'), + (0x107B4, 'M', 'ʢ'), + (0x107B5, 'M', 'ʘ'), + (0x107B6, 'M', 'ǀ'), + (0x107B7, 'M', 'ǁ'), + (0x107B8, 'M', 'ǂ'), + (0x107B9, 'M', '𝼊'), + (0x107BA, 'M', '𝼞'), + (0x107BB, 'X'), + (0x10800, 'V'), + (0x10806, 'X'), + (0x10808, 'V'), + (0x10809, 'X'), + (0x1080A, 'V'), + (0x10836, 'X'), + (0x10837, 'V'), + (0x10839, 'X'), + (0x1083C, 'V'), + (0x1083D, 'X'), + (0x1083F, 'V'), + (0x10856, 'X'), + (0x10857, 'V'), + (0x1089F, 'X'), + (0x108A7, 'V'), + (0x108B0, 'X'), + (0x108E0, 'V'), + (0x108F3, 'X'), + (0x108F4, 'V'), + (0x108F6, 'X'), + (0x108FB, 'V'), + (0x1091C, 'X'), + (0x1091F, 'V'), + (0x1093A, 'X'), + (0x1093F, 'V'), + (0x10940, 'X'), + (0x10980, 'V'), + (0x109B8, 'X'), + (0x109BC, 'V'), + (0x109D0, 'X'), + (0x109D2, 'V'), + (0x10A04, 'X'), + (0x10A05, 'V'), + (0x10A07, 'X'), + (0x10A0C, 'V'), + (0x10A14, 'X'), + (0x10A15, 'V'), + (0x10A18, 'X'), + (0x10A19, 'V'), + (0x10A36, 'X'), + (0x10A38, 'V'), + (0x10A3B, 'X'), + (0x10A3F, 'V'), + (0x10A49, 'X'), + (0x10A50, 'V'), + (0x10A59, 'X'), + (0x10A60, 'V'), + (0x10AA0, 'X'), + (0x10AC0, 'V'), + ] + +def _seg_56() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x10AE7, 'X'), + (0x10AEB, 'V'), + (0x10AF7, 'X'), + (0x10B00, 'V'), + (0x10B36, 'X'), + (0x10B39, 'V'), + (0x10B56, 'X'), + (0x10B58, 'V'), + (0x10B73, 'X'), + (0x10B78, 'V'), + (0x10B92, 'X'), + (0x10B99, 'V'), + (0x10B9D, 'X'), + (0x10BA9, 'V'), + (0x10BB0, 'X'), + (0x10C00, 'V'), + (0x10C49, 'X'), + (0x10C80, 'M', '𐳀'), + (0x10C81, 'M', '𐳁'), + (0x10C82, 'M', '𐳂'), + (0x10C83, 'M', '𐳃'), + (0x10C84, 'M', '𐳄'), + (0x10C85, 'M', '𐳅'), + (0x10C86, 'M', '𐳆'), + (0x10C87, 'M', '𐳇'), + (0x10C88, 'M', '𐳈'), + (0x10C89, 'M', '𐳉'), + (0x10C8A, 'M', '𐳊'), + (0x10C8B, 'M', '𐳋'), + (0x10C8C, 'M', '𐳌'), + (0x10C8D, 'M', '𐳍'), + (0x10C8E, 'M', '𐳎'), + (0x10C8F, 'M', '𐳏'), + (0x10C90, 'M', '𐳐'), + (0x10C91, 'M', '𐳑'), + (0x10C92, 'M', '𐳒'), + (0x10C93, 'M', '𐳓'), + (0x10C94, 'M', '𐳔'), + (0x10C95, 'M', '𐳕'), + (0x10C96, 'M', '𐳖'), + (0x10C97, 'M', '𐳗'), + (0x10C98, 'M', '𐳘'), + (0x10C99, 'M', '𐳙'), + (0x10C9A, 'M', '𐳚'), + (0x10C9B, 'M', '𐳛'), + (0x10C9C, 'M', '𐳜'), + (0x10C9D, 'M', '𐳝'), + (0x10C9E, 'M', '𐳞'), + (0x10C9F, 'M', '𐳟'), + (0x10CA0, 'M', '𐳠'), + (0x10CA1, 'M', '𐳡'), + (0x10CA2, 'M', '𐳢'), + (0x10CA3, 'M', '𐳣'), + (0x10CA4, 'M', '𐳤'), + (0x10CA5, 'M', '𐳥'), + (0x10CA6, 'M', '𐳦'), + (0x10CA7, 'M', '𐳧'), + (0x10CA8, 'M', '𐳨'), + (0x10CA9, 'M', '𐳩'), + (0x10CAA, 'M', '𐳪'), + (0x10CAB, 'M', '𐳫'), + (0x10CAC, 'M', '𐳬'), + (0x10CAD, 'M', '𐳭'), + (0x10CAE, 'M', '𐳮'), + (0x10CAF, 'M', '𐳯'), + (0x10CB0, 'M', '𐳰'), + (0x10CB1, 'M', '𐳱'), + (0x10CB2, 'M', '𐳲'), + (0x10CB3, 'X'), + (0x10CC0, 'V'), + (0x10CF3, 'X'), + (0x10CFA, 'V'), + (0x10D28, 'X'), + (0x10D30, 'V'), + (0x10D3A, 'X'), + (0x10E60, 'V'), + (0x10E7F, 'X'), + (0x10E80, 'V'), + (0x10EAA, 'X'), + (0x10EAB, 'V'), + (0x10EAE, 'X'), + (0x10EB0, 'V'), + (0x10EB2, 'X'), + (0x10EFD, 'V'), + (0x10F28, 'X'), + (0x10F30, 'V'), + (0x10F5A, 'X'), + (0x10F70, 'V'), + (0x10F8A, 'X'), + (0x10FB0, 'V'), + (0x10FCC, 'X'), + (0x10FE0, 'V'), + (0x10FF7, 'X'), + (0x11000, 'V'), + (0x1104E, 'X'), + (0x11052, 'V'), + (0x11076, 'X'), + (0x1107F, 'V'), + (0x110BD, 'X'), + (0x110BE, 'V'), + ] + +def _seg_57() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x110C3, 'X'), + (0x110D0, 'V'), + (0x110E9, 'X'), + (0x110F0, 'V'), + (0x110FA, 'X'), + (0x11100, 'V'), + (0x11135, 'X'), + (0x11136, 'V'), + (0x11148, 'X'), + (0x11150, 'V'), + (0x11177, 'X'), + (0x11180, 'V'), + (0x111E0, 'X'), + (0x111E1, 'V'), + (0x111F5, 'X'), + (0x11200, 'V'), + (0x11212, 'X'), + (0x11213, 'V'), + (0x11242, 'X'), + (0x11280, 'V'), + (0x11287, 'X'), + (0x11288, 'V'), + (0x11289, 'X'), + (0x1128A, 'V'), + (0x1128E, 'X'), + (0x1128F, 'V'), + (0x1129E, 'X'), + (0x1129F, 'V'), + (0x112AA, 'X'), + (0x112B0, 'V'), + (0x112EB, 'X'), + (0x112F0, 'V'), + (0x112FA, 'X'), + (0x11300, 'V'), + (0x11304, 'X'), + (0x11305, 'V'), + (0x1130D, 'X'), + (0x1130F, 'V'), + (0x11311, 'X'), + (0x11313, 'V'), + (0x11329, 'X'), + (0x1132A, 'V'), + (0x11331, 'X'), + (0x11332, 'V'), + (0x11334, 'X'), + (0x11335, 'V'), + (0x1133A, 'X'), + (0x1133B, 'V'), + (0x11345, 'X'), + (0x11347, 'V'), + (0x11349, 'X'), + (0x1134B, 'V'), + (0x1134E, 'X'), + (0x11350, 'V'), + (0x11351, 'X'), + (0x11357, 'V'), + (0x11358, 'X'), + (0x1135D, 'V'), + (0x11364, 'X'), + (0x11366, 'V'), + (0x1136D, 'X'), + (0x11370, 'V'), + (0x11375, 'X'), + (0x11400, 'V'), + (0x1145C, 'X'), + (0x1145D, 'V'), + (0x11462, 'X'), + (0x11480, 'V'), + (0x114C8, 'X'), + (0x114D0, 'V'), + (0x114DA, 'X'), + (0x11580, 'V'), + (0x115B6, 'X'), + (0x115B8, 'V'), + (0x115DE, 'X'), + (0x11600, 'V'), + (0x11645, 'X'), + (0x11650, 'V'), + (0x1165A, 'X'), + (0x11660, 'V'), + (0x1166D, 'X'), + (0x11680, 'V'), + (0x116BA, 'X'), + (0x116C0, 'V'), + (0x116CA, 'X'), + (0x11700, 'V'), + (0x1171B, 'X'), + (0x1171D, 'V'), + (0x1172C, 'X'), + (0x11730, 'V'), + (0x11747, 'X'), + (0x11800, 'V'), + (0x1183C, 'X'), + (0x118A0, 'M', '𑣀'), + (0x118A1, 'M', '𑣁'), + (0x118A2, 'M', '𑣂'), + (0x118A3, 'M', '𑣃'), + (0x118A4, 'M', '𑣄'), + (0x118A5, 'M', '𑣅'), + (0x118A6, 'M', '𑣆'), + ] + +def _seg_58() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x118A7, 'M', '𑣇'), + (0x118A8, 'M', '𑣈'), + (0x118A9, 'M', '𑣉'), + (0x118AA, 'M', '𑣊'), + (0x118AB, 'M', '𑣋'), + (0x118AC, 'M', '𑣌'), + (0x118AD, 'M', '𑣍'), + (0x118AE, 'M', '𑣎'), + (0x118AF, 'M', '𑣏'), + (0x118B0, 'M', '𑣐'), + (0x118B1, 'M', '𑣑'), + (0x118B2, 'M', '𑣒'), + (0x118B3, 'M', '𑣓'), + (0x118B4, 'M', '𑣔'), + (0x118B5, 'M', '𑣕'), + (0x118B6, 'M', '𑣖'), + (0x118B7, 'M', '𑣗'), + (0x118B8, 'M', '𑣘'), + (0x118B9, 'M', '𑣙'), + (0x118BA, 'M', '𑣚'), + (0x118BB, 'M', '𑣛'), + (0x118BC, 'M', '𑣜'), + (0x118BD, 'M', '𑣝'), + (0x118BE, 'M', '𑣞'), + (0x118BF, 'M', '𑣟'), + (0x118C0, 'V'), + (0x118F3, 'X'), + (0x118FF, 'V'), + (0x11907, 'X'), + (0x11909, 'V'), + (0x1190A, 'X'), + (0x1190C, 'V'), + (0x11914, 'X'), + (0x11915, 'V'), + (0x11917, 'X'), + (0x11918, 'V'), + (0x11936, 'X'), + (0x11937, 'V'), + (0x11939, 'X'), + (0x1193B, 'V'), + (0x11947, 'X'), + (0x11950, 'V'), + (0x1195A, 'X'), + (0x119A0, 'V'), + (0x119A8, 'X'), + (0x119AA, 'V'), + (0x119D8, 'X'), + (0x119DA, 'V'), + (0x119E5, 'X'), + (0x11A00, 'V'), + (0x11A48, 'X'), + (0x11A50, 'V'), + (0x11AA3, 'X'), + (0x11AB0, 'V'), + (0x11AF9, 'X'), + (0x11B00, 'V'), + (0x11B0A, 'X'), + (0x11C00, 'V'), + (0x11C09, 'X'), + (0x11C0A, 'V'), + (0x11C37, 'X'), + (0x11C38, 'V'), + (0x11C46, 'X'), + (0x11C50, 'V'), + (0x11C6D, 'X'), + (0x11C70, 'V'), + (0x11C90, 'X'), + (0x11C92, 'V'), + (0x11CA8, 'X'), + (0x11CA9, 'V'), + (0x11CB7, 'X'), + (0x11D00, 'V'), + (0x11D07, 'X'), + (0x11D08, 'V'), + (0x11D0A, 'X'), + (0x11D0B, 'V'), + (0x11D37, 'X'), + (0x11D3A, 'V'), + (0x11D3B, 'X'), + (0x11D3C, 'V'), + (0x11D3E, 'X'), + (0x11D3F, 'V'), + (0x11D48, 'X'), + (0x11D50, 'V'), + (0x11D5A, 'X'), + (0x11D60, 'V'), + (0x11D66, 'X'), + (0x11D67, 'V'), + (0x11D69, 'X'), + (0x11D6A, 'V'), + (0x11D8F, 'X'), + (0x11D90, 'V'), + (0x11D92, 'X'), + (0x11D93, 'V'), + (0x11D99, 'X'), + (0x11DA0, 'V'), + (0x11DAA, 'X'), + (0x11EE0, 'V'), + (0x11EF9, 'X'), + (0x11F00, 'V'), + ] + +def _seg_59() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x11F11, 'X'), + (0x11F12, 'V'), + (0x11F3B, 'X'), + (0x11F3E, 'V'), + (0x11F5A, 'X'), + (0x11FB0, 'V'), + (0x11FB1, 'X'), + (0x11FC0, 'V'), + (0x11FF2, 'X'), + (0x11FFF, 'V'), + (0x1239A, 'X'), + (0x12400, 'V'), + (0x1246F, 'X'), + (0x12470, 'V'), + (0x12475, 'X'), + (0x12480, 'V'), + (0x12544, 'X'), + (0x12F90, 'V'), + (0x12FF3, 'X'), + (0x13000, 'V'), + (0x13430, 'X'), + (0x13440, 'V'), + (0x13456, 'X'), + (0x14400, 'V'), + (0x14647, 'X'), + (0x16800, 'V'), + (0x16A39, 'X'), + (0x16A40, 'V'), + (0x16A5F, 'X'), + (0x16A60, 'V'), + (0x16A6A, 'X'), + (0x16A6E, 'V'), + (0x16ABF, 'X'), + (0x16AC0, 'V'), + (0x16ACA, 'X'), + (0x16AD0, 'V'), + (0x16AEE, 'X'), + (0x16AF0, 'V'), + (0x16AF6, 'X'), + (0x16B00, 'V'), + (0x16B46, 'X'), + (0x16B50, 'V'), + (0x16B5A, 'X'), + (0x16B5B, 'V'), + (0x16B62, 'X'), + (0x16B63, 'V'), + (0x16B78, 'X'), + (0x16B7D, 'V'), + (0x16B90, 'X'), + (0x16E40, 'M', '𖹠'), + (0x16E41, 'M', '𖹡'), + (0x16E42, 'M', '𖹢'), + (0x16E43, 'M', '𖹣'), + (0x16E44, 'M', '𖹤'), + (0x16E45, 'M', '𖹥'), + (0x16E46, 'M', '𖹦'), + (0x16E47, 'M', '𖹧'), + (0x16E48, 'M', '𖹨'), + (0x16E49, 'M', '𖹩'), + (0x16E4A, 'M', '𖹪'), + (0x16E4B, 'M', '𖹫'), + (0x16E4C, 'M', '𖹬'), + (0x16E4D, 'M', '𖹭'), + (0x16E4E, 'M', '𖹮'), + (0x16E4F, 'M', '𖹯'), + (0x16E50, 'M', '𖹰'), + (0x16E51, 'M', '𖹱'), + (0x16E52, 'M', '𖹲'), + (0x16E53, 'M', '𖹳'), + (0x16E54, 'M', '𖹴'), + (0x16E55, 'M', '𖹵'), + (0x16E56, 'M', '𖹶'), + (0x16E57, 'M', '𖹷'), + (0x16E58, 'M', '𖹸'), + (0x16E59, 'M', '𖹹'), + (0x16E5A, 'M', '𖹺'), + (0x16E5B, 'M', '𖹻'), + (0x16E5C, 'M', '𖹼'), + (0x16E5D, 'M', '𖹽'), + (0x16E5E, 'M', '𖹾'), + (0x16E5F, 'M', '𖹿'), + (0x16E60, 'V'), + (0x16E9B, 'X'), + (0x16F00, 'V'), + (0x16F4B, 'X'), + (0x16F4F, 'V'), + (0x16F88, 'X'), + (0x16F8F, 'V'), + (0x16FA0, 'X'), + (0x16FE0, 'V'), + (0x16FE5, 'X'), + (0x16FF0, 'V'), + (0x16FF2, 'X'), + (0x17000, 'V'), + (0x187F8, 'X'), + (0x18800, 'V'), + (0x18CD6, 'X'), + (0x18D00, 'V'), + (0x18D09, 'X'), + (0x1AFF0, 'V'), + ] + +def _seg_60() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1AFF4, 'X'), + (0x1AFF5, 'V'), + (0x1AFFC, 'X'), + (0x1AFFD, 'V'), + (0x1AFFF, 'X'), + (0x1B000, 'V'), + (0x1B123, 'X'), + (0x1B132, 'V'), + (0x1B133, 'X'), + (0x1B150, 'V'), + (0x1B153, 'X'), + (0x1B155, 'V'), + (0x1B156, 'X'), + (0x1B164, 'V'), + (0x1B168, 'X'), + (0x1B170, 'V'), + (0x1B2FC, 'X'), + (0x1BC00, 'V'), + (0x1BC6B, 'X'), + (0x1BC70, 'V'), + (0x1BC7D, 'X'), + (0x1BC80, 'V'), + (0x1BC89, 'X'), + (0x1BC90, 'V'), + (0x1BC9A, 'X'), + (0x1BC9C, 'V'), + (0x1BCA0, 'I'), + (0x1BCA4, 'X'), + (0x1CF00, 'V'), + (0x1CF2E, 'X'), + (0x1CF30, 'V'), + (0x1CF47, 'X'), + (0x1CF50, 'V'), + (0x1CFC4, 'X'), + (0x1D000, 'V'), + (0x1D0F6, 'X'), + (0x1D100, 'V'), + (0x1D127, 'X'), + (0x1D129, 'V'), + (0x1D15E, 'M', '𝅗𝅥'), + (0x1D15F, 'M', '𝅘𝅥'), + (0x1D160, 'M', '𝅘𝅥𝅮'), + (0x1D161, 'M', '𝅘𝅥𝅯'), + (0x1D162, 'M', '𝅘𝅥𝅰'), + (0x1D163, 'M', '𝅘𝅥𝅱'), + (0x1D164, 'M', '𝅘𝅥𝅲'), + (0x1D165, 'V'), + (0x1D173, 'X'), + (0x1D17B, 'V'), + (0x1D1BB, 'M', '𝆹𝅥'), + (0x1D1BC, 'M', '𝆺𝅥'), + (0x1D1BD, 'M', '𝆹𝅥𝅮'), + (0x1D1BE, 'M', '𝆺𝅥𝅮'), + (0x1D1BF, 'M', '𝆹𝅥𝅯'), + (0x1D1C0, 'M', '𝆺𝅥𝅯'), + (0x1D1C1, 'V'), + (0x1D1EB, 'X'), + (0x1D200, 'V'), + (0x1D246, 'X'), + (0x1D2C0, 'V'), + (0x1D2D4, 'X'), + (0x1D2E0, 'V'), + (0x1D2F4, 'X'), + (0x1D300, 'V'), + (0x1D357, 'X'), + (0x1D360, 'V'), + (0x1D379, 'X'), + (0x1D400, 'M', 'a'), + (0x1D401, 'M', 'b'), + (0x1D402, 'M', 'c'), + (0x1D403, 'M', 'd'), + (0x1D404, 'M', 'e'), + (0x1D405, 'M', 'f'), + (0x1D406, 'M', 'g'), + (0x1D407, 'M', 'h'), + (0x1D408, 'M', 'i'), + (0x1D409, 'M', 'j'), + (0x1D40A, 'M', 'k'), + (0x1D40B, 'M', 'l'), + (0x1D40C, 'M', 'm'), + (0x1D40D, 'M', 'n'), + (0x1D40E, 'M', 'o'), + (0x1D40F, 'M', 'p'), + (0x1D410, 'M', 'q'), + (0x1D411, 'M', 'r'), + (0x1D412, 'M', 's'), + (0x1D413, 'M', 't'), + (0x1D414, 'M', 'u'), + (0x1D415, 'M', 'v'), + (0x1D416, 'M', 'w'), + (0x1D417, 'M', 'x'), + (0x1D418, 'M', 'y'), + (0x1D419, 'M', 'z'), + (0x1D41A, 'M', 'a'), + (0x1D41B, 'M', 'b'), + (0x1D41C, 'M', 'c'), + (0x1D41D, 'M', 'd'), + (0x1D41E, 'M', 'e'), + (0x1D41F, 'M', 'f'), + (0x1D420, 'M', 'g'), + ] + +def _seg_61() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D421, 'M', 'h'), + (0x1D422, 'M', 'i'), + (0x1D423, 'M', 'j'), + (0x1D424, 'M', 'k'), + (0x1D425, 'M', 'l'), + (0x1D426, 'M', 'm'), + (0x1D427, 'M', 'n'), + (0x1D428, 'M', 'o'), + (0x1D429, 'M', 'p'), + (0x1D42A, 'M', 'q'), + (0x1D42B, 'M', 'r'), + (0x1D42C, 'M', 's'), + (0x1D42D, 'M', 't'), + (0x1D42E, 'M', 'u'), + (0x1D42F, 'M', 'v'), + (0x1D430, 'M', 'w'), + (0x1D431, 'M', 'x'), + (0x1D432, 'M', 'y'), + (0x1D433, 'M', 'z'), + (0x1D434, 'M', 'a'), + (0x1D435, 'M', 'b'), + (0x1D436, 'M', 'c'), + (0x1D437, 'M', 'd'), + (0x1D438, 'M', 'e'), + (0x1D439, 'M', 'f'), + (0x1D43A, 'M', 'g'), + (0x1D43B, 'M', 'h'), + (0x1D43C, 'M', 'i'), + (0x1D43D, 'M', 'j'), + (0x1D43E, 'M', 'k'), + (0x1D43F, 'M', 'l'), + (0x1D440, 'M', 'm'), + (0x1D441, 'M', 'n'), + (0x1D442, 'M', 'o'), + (0x1D443, 'M', 'p'), + (0x1D444, 'M', 'q'), + (0x1D445, 'M', 'r'), + (0x1D446, 'M', 's'), + (0x1D447, 'M', 't'), + (0x1D448, 'M', 'u'), + (0x1D449, 'M', 'v'), + (0x1D44A, 'M', 'w'), + (0x1D44B, 'M', 'x'), + (0x1D44C, 'M', 'y'), + (0x1D44D, 'M', 'z'), + (0x1D44E, 'M', 'a'), + (0x1D44F, 'M', 'b'), + (0x1D450, 'M', 'c'), + (0x1D451, 'M', 'd'), + (0x1D452, 'M', 'e'), + (0x1D453, 'M', 'f'), + (0x1D454, 'M', 'g'), + (0x1D455, 'X'), + (0x1D456, 'M', 'i'), + (0x1D457, 'M', 'j'), + (0x1D458, 'M', 'k'), + (0x1D459, 'M', 'l'), + (0x1D45A, 'M', 'm'), + (0x1D45B, 'M', 'n'), + (0x1D45C, 'M', 'o'), + (0x1D45D, 'M', 'p'), + (0x1D45E, 'M', 'q'), + (0x1D45F, 'M', 'r'), + (0x1D460, 'M', 's'), + (0x1D461, 'M', 't'), + (0x1D462, 'M', 'u'), + (0x1D463, 'M', 'v'), + (0x1D464, 'M', 'w'), + (0x1D465, 'M', 'x'), + (0x1D466, 'M', 'y'), + (0x1D467, 'M', 'z'), + (0x1D468, 'M', 'a'), + (0x1D469, 'M', 'b'), + (0x1D46A, 'M', 'c'), + (0x1D46B, 'M', 'd'), + (0x1D46C, 'M', 'e'), + (0x1D46D, 'M', 'f'), + (0x1D46E, 'M', 'g'), + (0x1D46F, 'M', 'h'), + (0x1D470, 'M', 'i'), + (0x1D471, 'M', 'j'), + (0x1D472, 'M', 'k'), + (0x1D473, 'M', 'l'), + (0x1D474, 'M', 'm'), + (0x1D475, 'M', 'n'), + (0x1D476, 'M', 'o'), + (0x1D477, 'M', 'p'), + (0x1D478, 'M', 'q'), + (0x1D479, 'M', 'r'), + (0x1D47A, 'M', 's'), + (0x1D47B, 'M', 't'), + (0x1D47C, 'M', 'u'), + (0x1D47D, 'M', 'v'), + (0x1D47E, 'M', 'w'), + (0x1D47F, 'M', 'x'), + (0x1D480, 'M', 'y'), + (0x1D481, 'M', 'z'), + (0x1D482, 'M', 'a'), + (0x1D483, 'M', 'b'), + (0x1D484, 'M', 'c'), + ] + +def _seg_62() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D485, 'M', 'd'), + (0x1D486, 'M', 'e'), + (0x1D487, 'M', 'f'), + (0x1D488, 'M', 'g'), + (0x1D489, 'M', 'h'), + (0x1D48A, 'M', 'i'), + (0x1D48B, 'M', 'j'), + (0x1D48C, 'M', 'k'), + (0x1D48D, 'M', 'l'), + (0x1D48E, 'M', 'm'), + (0x1D48F, 'M', 'n'), + (0x1D490, 'M', 'o'), + (0x1D491, 'M', 'p'), + (0x1D492, 'M', 'q'), + (0x1D493, 'M', 'r'), + (0x1D494, 'M', 's'), + (0x1D495, 'M', 't'), + (0x1D496, 'M', 'u'), + (0x1D497, 'M', 'v'), + (0x1D498, 'M', 'w'), + (0x1D499, 'M', 'x'), + (0x1D49A, 'M', 'y'), + (0x1D49B, 'M', 'z'), + (0x1D49C, 'M', 'a'), + (0x1D49D, 'X'), + (0x1D49E, 'M', 'c'), + (0x1D49F, 'M', 'd'), + (0x1D4A0, 'X'), + (0x1D4A2, 'M', 'g'), + (0x1D4A3, 'X'), + (0x1D4A5, 'M', 'j'), + (0x1D4A6, 'M', 'k'), + (0x1D4A7, 'X'), + (0x1D4A9, 'M', 'n'), + (0x1D4AA, 'M', 'o'), + (0x1D4AB, 'M', 'p'), + (0x1D4AC, 'M', 'q'), + (0x1D4AD, 'X'), + (0x1D4AE, 'M', 's'), + (0x1D4AF, 'M', 't'), + (0x1D4B0, 'M', 'u'), + (0x1D4B1, 'M', 'v'), + (0x1D4B2, 'M', 'w'), + (0x1D4B3, 'M', 'x'), + (0x1D4B4, 'M', 'y'), + (0x1D4B5, 'M', 'z'), + (0x1D4B6, 'M', 'a'), + (0x1D4B7, 'M', 'b'), + (0x1D4B8, 'M', 'c'), + (0x1D4B9, 'M', 'd'), + (0x1D4BA, 'X'), + (0x1D4BB, 'M', 'f'), + (0x1D4BC, 'X'), + (0x1D4BD, 'M', 'h'), + (0x1D4BE, 'M', 'i'), + (0x1D4BF, 'M', 'j'), + (0x1D4C0, 'M', 'k'), + (0x1D4C1, 'M', 'l'), + (0x1D4C2, 'M', 'm'), + (0x1D4C3, 'M', 'n'), + (0x1D4C4, 'X'), + (0x1D4C5, 'M', 'p'), + (0x1D4C6, 'M', 'q'), + (0x1D4C7, 'M', 'r'), + (0x1D4C8, 'M', 's'), + (0x1D4C9, 'M', 't'), + (0x1D4CA, 'M', 'u'), + (0x1D4CB, 'M', 'v'), + (0x1D4CC, 'M', 'w'), + (0x1D4CD, 'M', 'x'), + (0x1D4CE, 'M', 'y'), + (0x1D4CF, 'M', 'z'), + (0x1D4D0, 'M', 'a'), + (0x1D4D1, 'M', 'b'), + (0x1D4D2, 'M', 'c'), + (0x1D4D3, 'M', 'd'), + (0x1D4D4, 'M', 'e'), + (0x1D4D5, 'M', 'f'), + (0x1D4D6, 'M', 'g'), + (0x1D4D7, 'M', 'h'), + (0x1D4D8, 'M', 'i'), + (0x1D4D9, 'M', 'j'), + (0x1D4DA, 'M', 'k'), + (0x1D4DB, 'M', 'l'), + (0x1D4DC, 'M', 'm'), + (0x1D4DD, 'M', 'n'), + (0x1D4DE, 'M', 'o'), + (0x1D4DF, 'M', 'p'), + (0x1D4E0, 'M', 'q'), + (0x1D4E1, 'M', 'r'), + (0x1D4E2, 'M', 's'), + (0x1D4E3, 'M', 't'), + (0x1D4E4, 'M', 'u'), + (0x1D4E5, 'M', 'v'), + (0x1D4E6, 'M', 'w'), + (0x1D4E7, 'M', 'x'), + (0x1D4E8, 'M', 'y'), + (0x1D4E9, 'M', 'z'), + (0x1D4EA, 'M', 'a'), + (0x1D4EB, 'M', 'b'), + ] + +def _seg_63() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D4EC, 'M', 'c'), + (0x1D4ED, 'M', 'd'), + (0x1D4EE, 'M', 'e'), + (0x1D4EF, 'M', 'f'), + (0x1D4F0, 'M', 'g'), + (0x1D4F1, 'M', 'h'), + (0x1D4F2, 'M', 'i'), + (0x1D4F3, 'M', 'j'), + (0x1D4F4, 'M', 'k'), + (0x1D4F5, 'M', 'l'), + (0x1D4F6, 'M', 'm'), + (0x1D4F7, 'M', 'n'), + (0x1D4F8, 'M', 'o'), + (0x1D4F9, 'M', 'p'), + (0x1D4FA, 'M', 'q'), + (0x1D4FB, 'M', 'r'), + (0x1D4FC, 'M', 's'), + (0x1D4FD, 'M', 't'), + (0x1D4FE, 'M', 'u'), + (0x1D4FF, 'M', 'v'), + (0x1D500, 'M', 'w'), + (0x1D501, 'M', 'x'), + (0x1D502, 'M', 'y'), + (0x1D503, 'M', 'z'), + (0x1D504, 'M', 'a'), + (0x1D505, 'M', 'b'), + (0x1D506, 'X'), + (0x1D507, 'M', 'd'), + (0x1D508, 'M', 'e'), + (0x1D509, 'M', 'f'), + (0x1D50A, 'M', 'g'), + (0x1D50B, 'X'), + (0x1D50D, 'M', 'j'), + (0x1D50E, 'M', 'k'), + (0x1D50F, 'M', 'l'), + (0x1D510, 'M', 'm'), + (0x1D511, 'M', 'n'), + (0x1D512, 'M', 'o'), + (0x1D513, 'M', 'p'), + (0x1D514, 'M', 'q'), + (0x1D515, 'X'), + (0x1D516, 'M', 's'), + (0x1D517, 'M', 't'), + (0x1D518, 'M', 'u'), + (0x1D519, 'M', 'v'), + (0x1D51A, 'M', 'w'), + (0x1D51B, 'M', 'x'), + (0x1D51C, 'M', 'y'), + (0x1D51D, 'X'), + (0x1D51E, 'M', 'a'), + (0x1D51F, 'M', 'b'), + (0x1D520, 'M', 'c'), + (0x1D521, 'M', 'd'), + (0x1D522, 'M', 'e'), + (0x1D523, 'M', 'f'), + (0x1D524, 'M', 'g'), + (0x1D525, 'M', 'h'), + (0x1D526, 'M', 'i'), + (0x1D527, 'M', 'j'), + (0x1D528, 'M', 'k'), + (0x1D529, 'M', 'l'), + (0x1D52A, 'M', 'm'), + (0x1D52B, 'M', 'n'), + (0x1D52C, 'M', 'o'), + (0x1D52D, 'M', 'p'), + (0x1D52E, 'M', 'q'), + (0x1D52F, 'M', 'r'), + (0x1D530, 'M', 's'), + (0x1D531, 'M', 't'), + (0x1D532, 'M', 'u'), + (0x1D533, 'M', 'v'), + (0x1D534, 'M', 'w'), + (0x1D535, 'M', 'x'), + (0x1D536, 'M', 'y'), + (0x1D537, 'M', 'z'), + (0x1D538, 'M', 'a'), + (0x1D539, 'M', 'b'), + (0x1D53A, 'X'), + (0x1D53B, 'M', 'd'), + (0x1D53C, 'M', 'e'), + (0x1D53D, 'M', 'f'), + (0x1D53E, 'M', 'g'), + (0x1D53F, 'X'), + (0x1D540, 'M', 'i'), + (0x1D541, 'M', 'j'), + (0x1D542, 'M', 'k'), + (0x1D543, 'M', 'l'), + (0x1D544, 'M', 'm'), + (0x1D545, 'X'), + (0x1D546, 'M', 'o'), + (0x1D547, 'X'), + (0x1D54A, 'M', 's'), + (0x1D54B, 'M', 't'), + (0x1D54C, 'M', 'u'), + (0x1D54D, 'M', 'v'), + (0x1D54E, 'M', 'w'), + (0x1D54F, 'M', 'x'), + (0x1D550, 'M', 'y'), + (0x1D551, 'X'), + (0x1D552, 'M', 'a'), + ] + +def _seg_64() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D553, 'M', 'b'), + (0x1D554, 'M', 'c'), + (0x1D555, 'M', 'd'), + (0x1D556, 'M', 'e'), + (0x1D557, 'M', 'f'), + (0x1D558, 'M', 'g'), + (0x1D559, 'M', 'h'), + (0x1D55A, 'M', 'i'), + (0x1D55B, 'M', 'j'), + (0x1D55C, 'M', 'k'), + (0x1D55D, 'M', 'l'), + (0x1D55E, 'M', 'm'), + (0x1D55F, 'M', 'n'), + (0x1D560, 'M', 'o'), + (0x1D561, 'M', 'p'), + (0x1D562, 'M', 'q'), + (0x1D563, 'M', 'r'), + (0x1D564, 'M', 's'), + (0x1D565, 'M', 't'), + (0x1D566, 'M', 'u'), + (0x1D567, 'M', 'v'), + (0x1D568, 'M', 'w'), + (0x1D569, 'M', 'x'), + (0x1D56A, 'M', 'y'), + (0x1D56B, 'M', 'z'), + (0x1D56C, 'M', 'a'), + (0x1D56D, 'M', 'b'), + (0x1D56E, 'M', 'c'), + (0x1D56F, 'M', 'd'), + (0x1D570, 'M', 'e'), + (0x1D571, 'M', 'f'), + (0x1D572, 'M', 'g'), + (0x1D573, 'M', 'h'), + (0x1D574, 'M', 'i'), + (0x1D575, 'M', 'j'), + (0x1D576, 'M', 'k'), + (0x1D577, 'M', 'l'), + (0x1D578, 'M', 'm'), + (0x1D579, 'M', 'n'), + (0x1D57A, 'M', 'o'), + (0x1D57B, 'M', 'p'), + (0x1D57C, 'M', 'q'), + (0x1D57D, 'M', 'r'), + (0x1D57E, 'M', 's'), + (0x1D57F, 'M', 't'), + (0x1D580, 'M', 'u'), + (0x1D581, 'M', 'v'), + (0x1D582, 'M', 'w'), + (0x1D583, 'M', 'x'), + (0x1D584, 'M', 'y'), + (0x1D585, 'M', 'z'), + (0x1D586, 'M', 'a'), + (0x1D587, 'M', 'b'), + (0x1D588, 'M', 'c'), + (0x1D589, 'M', 'd'), + (0x1D58A, 'M', 'e'), + (0x1D58B, 'M', 'f'), + (0x1D58C, 'M', 'g'), + (0x1D58D, 'M', 'h'), + (0x1D58E, 'M', 'i'), + (0x1D58F, 'M', 'j'), + (0x1D590, 'M', 'k'), + (0x1D591, 'M', 'l'), + (0x1D592, 'M', 'm'), + (0x1D593, 'M', 'n'), + (0x1D594, 'M', 'o'), + (0x1D595, 'M', 'p'), + (0x1D596, 'M', 'q'), + (0x1D597, 'M', 'r'), + (0x1D598, 'M', 's'), + (0x1D599, 'M', 't'), + (0x1D59A, 'M', 'u'), + (0x1D59B, 'M', 'v'), + (0x1D59C, 'M', 'w'), + (0x1D59D, 'M', 'x'), + (0x1D59E, 'M', 'y'), + (0x1D59F, 'M', 'z'), + (0x1D5A0, 'M', 'a'), + (0x1D5A1, 'M', 'b'), + (0x1D5A2, 'M', 'c'), + (0x1D5A3, 'M', 'd'), + (0x1D5A4, 'M', 'e'), + (0x1D5A5, 'M', 'f'), + (0x1D5A6, 'M', 'g'), + (0x1D5A7, 'M', 'h'), + (0x1D5A8, 'M', 'i'), + (0x1D5A9, 'M', 'j'), + (0x1D5AA, 'M', 'k'), + (0x1D5AB, 'M', 'l'), + (0x1D5AC, 'M', 'm'), + (0x1D5AD, 'M', 'n'), + (0x1D5AE, 'M', 'o'), + (0x1D5AF, 'M', 'p'), + (0x1D5B0, 'M', 'q'), + (0x1D5B1, 'M', 'r'), + (0x1D5B2, 'M', 's'), + (0x1D5B3, 'M', 't'), + (0x1D5B4, 'M', 'u'), + (0x1D5B5, 'M', 'v'), + (0x1D5B6, 'M', 'w'), + ] + +def _seg_65() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D5B7, 'M', 'x'), + (0x1D5B8, 'M', 'y'), + (0x1D5B9, 'M', 'z'), + (0x1D5BA, 'M', 'a'), + (0x1D5BB, 'M', 'b'), + (0x1D5BC, 'M', 'c'), + (0x1D5BD, 'M', 'd'), + (0x1D5BE, 'M', 'e'), + (0x1D5BF, 'M', 'f'), + (0x1D5C0, 'M', 'g'), + (0x1D5C1, 'M', 'h'), + (0x1D5C2, 'M', 'i'), + (0x1D5C3, 'M', 'j'), + (0x1D5C4, 'M', 'k'), + (0x1D5C5, 'M', 'l'), + (0x1D5C6, 'M', 'm'), + (0x1D5C7, 'M', 'n'), + (0x1D5C8, 'M', 'o'), + (0x1D5C9, 'M', 'p'), + (0x1D5CA, 'M', 'q'), + (0x1D5CB, 'M', 'r'), + (0x1D5CC, 'M', 's'), + (0x1D5CD, 'M', 't'), + (0x1D5CE, 'M', 'u'), + (0x1D5CF, 'M', 'v'), + (0x1D5D0, 'M', 'w'), + (0x1D5D1, 'M', 'x'), + (0x1D5D2, 'M', 'y'), + (0x1D5D3, 'M', 'z'), + (0x1D5D4, 'M', 'a'), + (0x1D5D5, 'M', 'b'), + (0x1D5D6, 'M', 'c'), + (0x1D5D7, 'M', 'd'), + (0x1D5D8, 'M', 'e'), + (0x1D5D9, 'M', 'f'), + (0x1D5DA, 'M', 'g'), + (0x1D5DB, 'M', 'h'), + (0x1D5DC, 'M', 'i'), + (0x1D5DD, 'M', 'j'), + (0x1D5DE, 'M', 'k'), + (0x1D5DF, 'M', 'l'), + (0x1D5E0, 'M', 'm'), + (0x1D5E1, 'M', 'n'), + (0x1D5E2, 'M', 'o'), + (0x1D5E3, 'M', 'p'), + (0x1D5E4, 'M', 'q'), + (0x1D5E5, 'M', 'r'), + (0x1D5E6, 'M', 's'), + (0x1D5E7, 'M', 't'), + (0x1D5E8, 'M', 'u'), + (0x1D5E9, 'M', 'v'), + (0x1D5EA, 'M', 'w'), + (0x1D5EB, 'M', 'x'), + (0x1D5EC, 'M', 'y'), + (0x1D5ED, 'M', 'z'), + (0x1D5EE, 'M', 'a'), + (0x1D5EF, 'M', 'b'), + (0x1D5F0, 'M', 'c'), + (0x1D5F1, 'M', 'd'), + (0x1D5F2, 'M', 'e'), + (0x1D5F3, 'M', 'f'), + (0x1D5F4, 'M', 'g'), + (0x1D5F5, 'M', 'h'), + (0x1D5F6, 'M', 'i'), + (0x1D5F7, 'M', 'j'), + (0x1D5F8, 'M', 'k'), + (0x1D5F9, 'M', 'l'), + (0x1D5FA, 'M', 'm'), + (0x1D5FB, 'M', 'n'), + (0x1D5FC, 'M', 'o'), + (0x1D5FD, 'M', 'p'), + (0x1D5FE, 'M', 'q'), + (0x1D5FF, 'M', 'r'), + (0x1D600, 'M', 's'), + (0x1D601, 'M', 't'), + (0x1D602, 'M', 'u'), + (0x1D603, 'M', 'v'), + (0x1D604, 'M', 'w'), + (0x1D605, 'M', 'x'), + (0x1D606, 'M', 'y'), + (0x1D607, 'M', 'z'), + (0x1D608, 'M', 'a'), + (0x1D609, 'M', 'b'), + (0x1D60A, 'M', 'c'), + (0x1D60B, 'M', 'd'), + (0x1D60C, 'M', 'e'), + (0x1D60D, 'M', 'f'), + (0x1D60E, 'M', 'g'), + (0x1D60F, 'M', 'h'), + (0x1D610, 'M', 'i'), + (0x1D611, 'M', 'j'), + (0x1D612, 'M', 'k'), + (0x1D613, 'M', 'l'), + (0x1D614, 'M', 'm'), + (0x1D615, 'M', 'n'), + (0x1D616, 'M', 'o'), + (0x1D617, 'M', 'p'), + (0x1D618, 'M', 'q'), + (0x1D619, 'M', 'r'), + (0x1D61A, 'M', 's'), + ] + +def _seg_66() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D61B, 'M', 't'), + (0x1D61C, 'M', 'u'), + (0x1D61D, 'M', 'v'), + (0x1D61E, 'M', 'w'), + (0x1D61F, 'M', 'x'), + (0x1D620, 'M', 'y'), + (0x1D621, 'M', 'z'), + (0x1D622, 'M', 'a'), + (0x1D623, 'M', 'b'), + (0x1D624, 'M', 'c'), + (0x1D625, 'M', 'd'), + (0x1D626, 'M', 'e'), + (0x1D627, 'M', 'f'), + (0x1D628, 'M', 'g'), + (0x1D629, 'M', 'h'), + (0x1D62A, 'M', 'i'), + (0x1D62B, 'M', 'j'), + (0x1D62C, 'M', 'k'), + (0x1D62D, 'M', 'l'), + (0x1D62E, 'M', 'm'), + (0x1D62F, 'M', 'n'), + (0x1D630, 'M', 'o'), + (0x1D631, 'M', 'p'), + (0x1D632, 'M', 'q'), + (0x1D633, 'M', 'r'), + (0x1D634, 'M', 's'), + (0x1D635, 'M', 't'), + (0x1D636, 'M', 'u'), + (0x1D637, 'M', 'v'), + (0x1D638, 'M', 'w'), + (0x1D639, 'M', 'x'), + (0x1D63A, 'M', 'y'), + (0x1D63B, 'M', 'z'), + (0x1D63C, 'M', 'a'), + (0x1D63D, 'M', 'b'), + (0x1D63E, 'M', 'c'), + (0x1D63F, 'M', 'd'), + (0x1D640, 'M', 'e'), + (0x1D641, 'M', 'f'), + (0x1D642, 'M', 'g'), + (0x1D643, 'M', 'h'), + (0x1D644, 'M', 'i'), + (0x1D645, 'M', 'j'), + (0x1D646, 'M', 'k'), + (0x1D647, 'M', 'l'), + (0x1D648, 'M', 'm'), + (0x1D649, 'M', 'n'), + (0x1D64A, 'M', 'o'), + (0x1D64B, 'M', 'p'), + (0x1D64C, 'M', 'q'), + (0x1D64D, 'M', 'r'), + (0x1D64E, 'M', 's'), + (0x1D64F, 'M', 't'), + (0x1D650, 'M', 'u'), + (0x1D651, 'M', 'v'), + (0x1D652, 'M', 'w'), + (0x1D653, 'M', 'x'), + (0x1D654, 'M', 'y'), + (0x1D655, 'M', 'z'), + (0x1D656, 'M', 'a'), + (0x1D657, 'M', 'b'), + (0x1D658, 'M', 'c'), + (0x1D659, 'M', 'd'), + (0x1D65A, 'M', 'e'), + (0x1D65B, 'M', 'f'), + (0x1D65C, 'M', 'g'), + (0x1D65D, 'M', 'h'), + (0x1D65E, 'M', 'i'), + (0x1D65F, 'M', 'j'), + (0x1D660, 'M', 'k'), + (0x1D661, 'M', 'l'), + (0x1D662, 'M', 'm'), + (0x1D663, 'M', 'n'), + (0x1D664, 'M', 'o'), + (0x1D665, 'M', 'p'), + (0x1D666, 'M', 'q'), + (0x1D667, 'M', 'r'), + (0x1D668, 'M', 's'), + (0x1D669, 'M', 't'), + (0x1D66A, 'M', 'u'), + (0x1D66B, 'M', 'v'), + (0x1D66C, 'M', 'w'), + (0x1D66D, 'M', 'x'), + (0x1D66E, 'M', 'y'), + (0x1D66F, 'M', 'z'), + (0x1D670, 'M', 'a'), + (0x1D671, 'M', 'b'), + (0x1D672, 'M', 'c'), + (0x1D673, 'M', 'd'), + (0x1D674, 'M', 'e'), + (0x1D675, 'M', 'f'), + (0x1D676, 'M', 'g'), + (0x1D677, 'M', 'h'), + (0x1D678, 'M', 'i'), + (0x1D679, 'M', 'j'), + (0x1D67A, 'M', 'k'), + (0x1D67B, 'M', 'l'), + (0x1D67C, 'M', 'm'), + (0x1D67D, 'M', 'n'), + (0x1D67E, 'M', 'o'), + ] + +def _seg_67() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D67F, 'M', 'p'), + (0x1D680, 'M', 'q'), + (0x1D681, 'M', 'r'), + (0x1D682, 'M', 's'), + (0x1D683, 'M', 't'), + (0x1D684, 'M', 'u'), + (0x1D685, 'M', 'v'), + (0x1D686, 'M', 'w'), + (0x1D687, 'M', 'x'), + (0x1D688, 'M', 'y'), + (0x1D689, 'M', 'z'), + (0x1D68A, 'M', 'a'), + (0x1D68B, 'M', 'b'), + (0x1D68C, 'M', 'c'), + (0x1D68D, 'M', 'd'), + (0x1D68E, 'M', 'e'), + (0x1D68F, 'M', 'f'), + (0x1D690, 'M', 'g'), + (0x1D691, 'M', 'h'), + (0x1D692, 'M', 'i'), + (0x1D693, 'M', 'j'), + (0x1D694, 'M', 'k'), + (0x1D695, 'M', 'l'), + (0x1D696, 'M', 'm'), + (0x1D697, 'M', 'n'), + (0x1D698, 'M', 'o'), + (0x1D699, 'M', 'p'), + (0x1D69A, 'M', 'q'), + (0x1D69B, 'M', 'r'), + (0x1D69C, 'M', 's'), + (0x1D69D, 'M', 't'), + (0x1D69E, 'M', 'u'), + (0x1D69F, 'M', 'v'), + (0x1D6A0, 'M', 'w'), + (0x1D6A1, 'M', 'x'), + (0x1D6A2, 'M', 'y'), + (0x1D6A3, 'M', 'z'), + (0x1D6A4, 'M', 'ı'), + (0x1D6A5, 'M', 'ȷ'), + (0x1D6A6, 'X'), + (0x1D6A8, 'M', 'α'), + (0x1D6A9, 'M', 'β'), + (0x1D6AA, 'M', 'γ'), + (0x1D6AB, 'M', 'δ'), + (0x1D6AC, 'M', 'ε'), + (0x1D6AD, 'M', 'ζ'), + (0x1D6AE, 'M', 'η'), + (0x1D6AF, 'M', 'θ'), + (0x1D6B0, 'M', 'ι'), + (0x1D6B1, 'M', 'κ'), + (0x1D6B2, 'M', 'λ'), + (0x1D6B3, 'M', 'μ'), + (0x1D6B4, 'M', 'ν'), + (0x1D6B5, 'M', 'ξ'), + (0x1D6B6, 'M', 'ο'), + (0x1D6B7, 'M', 'π'), + (0x1D6B8, 'M', 'ρ'), + (0x1D6B9, 'M', 'θ'), + (0x1D6BA, 'M', 'σ'), + (0x1D6BB, 'M', 'τ'), + (0x1D6BC, 'M', 'υ'), + (0x1D6BD, 'M', 'φ'), + (0x1D6BE, 'M', 'χ'), + (0x1D6BF, 'M', 'ψ'), + (0x1D6C0, 'M', 'ω'), + (0x1D6C1, 'M', '∇'), + (0x1D6C2, 'M', 'α'), + (0x1D6C3, 'M', 'β'), + (0x1D6C4, 'M', 'γ'), + (0x1D6C5, 'M', 'δ'), + (0x1D6C6, 'M', 'ε'), + (0x1D6C7, 'M', 'ζ'), + (0x1D6C8, 'M', 'η'), + (0x1D6C9, 'M', 'θ'), + (0x1D6CA, 'M', 'ι'), + (0x1D6CB, 'M', 'κ'), + (0x1D6CC, 'M', 'λ'), + (0x1D6CD, 'M', 'μ'), + (0x1D6CE, 'M', 'ν'), + (0x1D6CF, 'M', 'ξ'), + (0x1D6D0, 'M', 'ο'), + (0x1D6D1, 'M', 'π'), + (0x1D6D2, 'M', 'ρ'), + (0x1D6D3, 'M', 'σ'), + (0x1D6D5, 'M', 'τ'), + (0x1D6D6, 'M', 'υ'), + (0x1D6D7, 'M', 'φ'), + (0x1D6D8, 'M', 'χ'), + (0x1D6D9, 'M', 'ψ'), + (0x1D6DA, 'M', 'ω'), + (0x1D6DB, 'M', '∂'), + (0x1D6DC, 'M', 'ε'), + (0x1D6DD, 'M', 'θ'), + (0x1D6DE, 'M', 'κ'), + (0x1D6DF, 'M', 'φ'), + (0x1D6E0, 'M', 'ρ'), + (0x1D6E1, 'M', 'π'), + (0x1D6E2, 'M', 'α'), + (0x1D6E3, 'M', 'β'), + (0x1D6E4, 'M', 'γ'), + ] + +def _seg_68() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D6E5, 'M', 'δ'), + (0x1D6E6, 'M', 'ε'), + (0x1D6E7, 'M', 'ζ'), + (0x1D6E8, 'M', 'η'), + (0x1D6E9, 'M', 'θ'), + (0x1D6EA, 'M', 'ι'), + (0x1D6EB, 'M', 'κ'), + (0x1D6EC, 'M', 'λ'), + (0x1D6ED, 'M', 'μ'), + (0x1D6EE, 'M', 'ν'), + (0x1D6EF, 'M', 'ξ'), + (0x1D6F0, 'M', 'ο'), + (0x1D6F1, 'M', 'π'), + (0x1D6F2, 'M', 'ρ'), + (0x1D6F3, 'M', 'θ'), + (0x1D6F4, 'M', 'σ'), + (0x1D6F5, 'M', 'τ'), + (0x1D6F6, 'M', 'υ'), + (0x1D6F7, 'M', 'φ'), + (0x1D6F8, 'M', 'χ'), + (0x1D6F9, 'M', 'ψ'), + (0x1D6FA, 'M', 'ω'), + (0x1D6FB, 'M', '∇'), + (0x1D6FC, 'M', 'α'), + (0x1D6FD, 'M', 'β'), + (0x1D6FE, 'M', 'γ'), + (0x1D6FF, 'M', 'δ'), + (0x1D700, 'M', 'ε'), + (0x1D701, 'M', 'ζ'), + (0x1D702, 'M', 'η'), + (0x1D703, 'M', 'θ'), + (0x1D704, 'M', 'ι'), + (0x1D705, 'M', 'κ'), + (0x1D706, 'M', 'λ'), + (0x1D707, 'M', 'μ'), + (0x1D708, 'M', 'ν'), + (0x1D709, 'M', 'ξ'), + (0x1D70A, 'M', 'ο'), + (0x1D70B, 'M', 'π'), + (0x1D70C, 'M', 'ρ'), + (0x1D70D, 'M', 'σ'), + (0x1D70F, 'M', 'τ'), + (0x1D710, 'M', 'υ'), + (0x1D711, 'M', 'φ'), + (0x1D712, 'M', 'χ'), + (0x1D713, 'M', 'ψ'), + (0x1D714, 'M', 'ω'), + (0x1D715, 'M', '∂'), + (0x1D716, 'M', 'ε'), + (0x1D717, 'M', 'θ'), + (0x1D718, 'M', 'κ'), + (0x1D719, 'M', 'φ'), + (0x1D71A, 'M', 'ρ'), + (0x1D71B, 'M', 'π'), + (0x1D71C, 'M', 'α'), + (0x1D71D, 'M', 'β'), + (0x1D71E, 'M', 'γ'), + (0x1D71F, 'M', 'δ'), + (0x1D720, 'M', 'ε'), + (0x1D721, 'M', 'ζ'), + (0x1D722, 'M', 'η'), + (0x1D723, 'M', 'θ'), + (0x1D724, 'M', 'ι'), + (0x1D725, 'M', 'κ'), + (0x1D726, 'M', 'λ'), + (0x1D727, 'M', 'μ'), + (0x1D728, 'M', 'ν'), + (0x1D729, 'M', 'ξ'), + (0x1D72A, 'M', 'ο'), + (0x1D72B, 'M', 'π'), + (0x1D72C, 'M', 'ρ'), + (0x1D72D, 'M', 'θ'), + (0x1D72E, 'M', 'σ'), + (0x1D72F, 'M', 'τ'), + (0x1D730, 'M', 'υ'), + (0x1D731, 'M', 'φ'), + (0x1D732, 'M', 'χ'), + (0x1D733, 'M', 'ψ'), + (0x1D734, 'M', 'ω'), + (0x1D735, 'M', '∇'), + (0x1D736, 'M', 'α'), + (0x1D737, 'M', 'β'), + (0x1D738, 'M', 'γ'), + (0x1D739, 'M', 'δ'), + (0x1D73A, 'M', 'ε'), + (0x1D73B, 'M', 'ζ'), + (0x1D73C, 'M', 'η'), + (0x1D73D, 'M', 'θ'), + (0x1D73E, 'M', 'ι'), + (0x1D73F, 'M', 'κ'), + (0x1D740, 'M', 'λ'), + (0x1D741, 'M', 'μ'), + (0x1D742, 'M', 'ν'), + (0x1D743, 'M', 'ξ'), + (0x1D744, 'M', 'ο'), + (0x1D745, 'M', 'π'), + (0x1D746, 'M', 'ρ'), + (0x1D747, 'M', 'σ'), + (0x1D749, 'M', 'τ'), + (0x1D74A, 'M', 'υ'), + ] + +def _seg_69() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D74B, 'M', 'φ'), + (0x1D74C, 'M', 'χ'), + (0x1D74D, 'M', 'ψ'), + (0x1D74E, 'M', 'ω'), + (0x1D74F, 'M', '∂'), + (0x1D750, 'M', 'ε'), + (0x1D751, 'M', 'θ'), + (0x1D752, 'M', 'κ'), + (0x1D753, 'M', 'φ'), + (0x1D754, 'M', 'ρ'), + (0x1D755, 'M', 'π'), + (0x1D756, 'M', 'α'), + (0x1D757, 'M', 'β'), + (0x1D758, 'M', 'γ'), + (0x1D759, 'M', 'δ'), + (0x1D75A, 'M', 'ε'), + (0x1D75B, 'M', 'ζ'), + (0x1D75C, 'M', 'η'), + (0x1D75D, 'M', 'θ'), + (0x1D75E, 'M', 'ι'), + (0x1D75F, 'M', 'κ'), + (0x1D760, 'M', 'λ'), + (0x1D761, 'M', 'μ'), + (0x1D762, 'M', 'ν'), + (0x1D763, 'M', 'ξ'), + (0x1D764, 'M', 'ο'), + (0x1D765, 'M', 'π'), + (0x1D766, 'M', 'ρ'), + (0x1D767, 'M', 'θ'), + (0x1D768, 'M', 'σ'), + (0x1D769, 'M', 'τ'), + (0x1D76A, 'M', 'υ'), + (0x1D76B, 'M', 'φ'), + (0x1D76C, 'M', 'χ'), + (0x1D76D, 'M', 'ψ'), + (0x1D76E, 'M', 'ω'), + (0x1D76F, 'M', '∇'), + (0x1D770, 'M', 'α'), + (0x1D771, 'M', 'β'), + (0x1D772, 'M', 'γ'), + (0x1D773, 'M', 'δ'), + (0x1D774, 'M', 'ε'), + (0x1D775, 'M', 'ζ'), + (0x1D776, 'M', 'η'), + (0x1D777, 'M', 'θ'), + (0x1D778, 'M', 'ι'), + (0x1D779, 'M', 'κ'), + (0x1D77A, 'M', 'λ'), + (0x1D77B, 'M', 'μ'), + (0x1D77C, 'M', 'ν'), + (0x1D77D, 'M', 'ξ'), + (0x1D77E, 'M', 'ο'), + (0x1D77F, 'M', 'π'), + (0x1D780, 'M', 'ρ'), + (0x1D781, 'M', 'σ'), + (0x1D783, 'M', 'τ'), + (0x1D784, 'M', 'υ'), + (0x1D785, 'M', 'φ'), + (0x1D786, 'M', 'χ'), + (0x1D787, 'M', 'ψ'), + (0x1D788, 'M', 'ω'), + (0x1D789, 'M', '∂'), + (0x1D78A, 'M', 'ε'), + (0x1D78B, 'M', 'θ'), + (0x1D78C, 'M', 'κ'), + (0x1D78D, 'M', 'φ'), + (0x1D78E, 'M', 'ρ'), + (0x1D78F, 'M', 'π'), + (0x1D790, 'M', 'α'), + (0x1D791, 'M', 'β'), + (0x1D792, 'M', 'γ'), + (0x1D793, 'M', 'δ'), + (0x1D794, 'M', 'ε'), + (0x1D795, 'M', 'ζ'), + (0x1D796, 'M', 'η'), + (0x1D797, 'M', 'θ'), + (0x1D798, 'M', 'ι'), + (0x1D799, 'M', 'κ'), + (0x1D79A, 'M', 'λ'), + (0x1D79B, 'M', 'μ'), + (0x1D79C, 'M', 'ν'), + (0x1D79D, 'M', 'ξ'), + (0x1D79E, 'M', 'ο'), + (0x1D79F, 'M', 'π'), + (0x1D7A0, 'M', 'ρ'), + (0x1D7A1, 'M', 'θ'), + (0x1D7A2, 'M', 'σ'), + (0x1D7A3, 'M', 'τ'), + (0x1D7A4, 'M', 'υ'), + (0x1D7A5, 'M', 'φ'), + (0x1D7A6, 'M', 'χ'), + (0x1D7A7, 'M', 'ψ'), + (0x1D7A8, 'M', 'ω'), + (0x1D7A9, 'M', '∇'), + (0x1D7AA, 'M', 'α'), + (0x1D7AB, 'M', 'β'), + (0x1D7AC, 'M', 'γ'), + (0x1D7AD, 'M', 'δ'), + (0x1D7AE, 'M', 'ε'), + (0x1D7AF, 'M', 'ζ'), + ] + +def _seg_70() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D7B0, 'M', 'η'), + (0x1D7B1, 'M', 'θ'), + (0x1D7B2, 'M', 'ι'), + (0x1D7B3, 'M', 'κ'), + (0x1D7B4, 'M', 'λ'), + (0x1D7B5, 'M', 'μ'), + (0x1D7B6, 'M', 'ν'), + (0x1D7B7, 'M', 'ξ'), + (0x1D7B8, 'M', 'ο'), + (0x1D7B9, 'M', 'π'), + (0x1D7BA, 'M', 'ρ'), + (0x1D7BB, 'M', 'σ'), + (0x1D7BD, 'M', 'τ'), + (0x1D7BE, 'M', 'υ'), + (0x1D7BF, 'M', 'φ'), + (0x1D7C0, 'M', 'χ'), + (0x1D7C1, 'M', 'ψ'), + (0x1D7C2, 'M', 'ω'), + (0x1D7C3, 'M', '∂'), + (0x1D7C4, 'M', 'ε'), + (0x1D7C5, 'M', 'θ'), + (0x1D7C6, 'M', 'κ'), + (0x1D7C7, 'M', 'φ'), + (0x1D7C8, 'M', 'ρ'), + (0x1D7C9, 'M', 'π'), + (0x1D7CA, 'M', 'ϝ'), + (0x1D7CC, 'X'), + (0x1D7CE, 'M', '0'), + (0x1D7CF, 'M', '1'), + (0x1D7D0, 'M', '2'), + (0x1D7D1, 'M', '3'), + (0x1D7D2, 'M', '4'), + (0x1D7D3, 'M', '5'), + (0x1D7D4, 'M', '6'), + (0x1D7D5, 'M', '7'), + (0x1D7D6, 'M', '8'), + (0x1D7D7, 'M', '9'), + (0x1D7D8, 'M', '0'), + (0x1D7D9, 'M', '1'), + (0x1D7DA, 'M', '2'), + (0x1D7DB, 'M', '3'), + (0x1D7DC, 'M', '4'), + (0x1D7DD, 'M', '5'), + (0x1D7DE, 'M', '6'), + (0x1D7DF, 'M', '7'), + (0x1D7E0, 'M', '8'), + (0x1D7E1, 'M', '9'), + (0x1D7E2, 'M', '0'), + (0x1D7E3, 'M', '1'), + (0x1D7E4, 'M', '2'), + (0x1D7E5, 'M', '3'), + (0x1D7E6, 'M', '4'), + (0x1D7E7, 'M', '5'), + (0x1D7E8, 'M', '6'), + (0x1D7E9, 'M', '7'), + (0x1D7EA, 'M', '8'), + (0x1D7EB, 'M', '9'), + (0x1D7EC, 'M', '0'), + (0x1D7ED, 'M', '1'), + (0x1D7EE, 'M', '2'), + (0x1D7EF, 'M', '3'), + (0x1D7F0, 'M', '4'), + (0x1D7F1, 'M', '5'), + (0x1D7F2, 'M', '6'), + (0x1D7F3, 'M', '7'), + (0x1D7F4, 'M', '8'), + (0x1D7F5, 'M', '9'), + (0x1D7F6, 'M', '0'), + (0x1D7F7, 'M', '1'), + (0x1D7F8, 'M', '2'), + (0x1D7F9, 'M', '3'), + (0x1D7FA, 'M', '4'), + (0x1D7FB, 'M', '5'), + (0x1D7FC, 'M', '6'), + (0x1D7FD, 'M', '7'), + (0x1D7FE, 'M', '8'), + (0x1D7FF, 'M', '9'), + (0x1D800, 'V'), + (0x1DA8C, 'X'), + (0x1DA9B, 'V'), + (0x1DAA0, 'X'), + (0x1DAA1, 'V'), + (0x1DAB0, 'X'), + (0x1DF00, 'V'), + (0x1DF1F, 'X'), + (0x1DF25, 'V'), + (0x1DF2B, 'X'), + (0x1E000, 'V'), + (0x1E007, 'X'), + (0x1E008, 'V'), + (0x1E019, 'X'), + (0x1E01B, 'V'), + (0x1E022, 'X'), + (0x1E023, 'V'), + (0x1E025, 'X'), + (0x1E026, 'V'), + (0x1E02B, 'X'), + (0x1E030, 'M', 'а'), + (0x1E031, 'M', 'б'), + (0x1E032, 'M', 'в'), + ] + +def _seg_71() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1E033, 'M', 'г'), + (0x1E034, 'M', 'д'), + (0x1E035, 'M', 'е'), + (0x1E036, 'M', 'ж'), + (0x1E037, 'M', 'з'), + (0x1E038, 'M', 'и'), + (0x1E039, 'M', 'к'), + (0x1E03A, 'M', 'л'), + (0x1E03B, 'M', 'м'), + (0x1E03C, 'M', 'о'), + (0x1E03D, 'M', 'п'), + (0x1E03E, 'M', 'р'), + (0x1E03F, 'M', 'с'), + (0x1E040, 'M', 'т'), + (0x1E041, 'M', 'у'), + (0x1E042, 'M', 'ф'), + (0x1E043, 'M', 'х'), + (0x1E044, 'M', 'ц'), + (0x1E045, 'M', 'ч'), + (0x1E046, 'M', 'ш'), + (0x1E047, 'M', 'ы'), + (0x1E048, 'M', 'э'), + (0x1E049, 'M', 'ю'), + (0x1E04A, 'M', 'ꚉ'), + (0x1E04B, 'M', 'ә'), + (0x1E04C, 'M', 'і'), + (0x1E04D, 'M', 'ј'), + (0x1E04E, 'M', 'ө'), + (0x1E04F, 'M', 'ү'), + (0x1E050, 'M', 'ӏ'), + (0x1E051, 'M', 'а'), + (0x1E052, 'M', 'б'), + (0x1E053, 'M', 'в'), + (0x1E054, 'M', 'г'), + (0x1E055, 'M', 'д'), + (0x1E056, 'M', 'е'), + (0x1E057, 'M', 'ж'), + (0x1E058, 'M', 'з'), + (0x1E059, 'M', 'и'), + (0x1E05A, 'M', 'к'), + (0x1E05B, 'M', 'л'), + (0x1E05C, 'M', 'о'), + (0x1E05D, 'M', 'п'), + (0x1E05E, 'M', 'с'), + (0x1E05F, 'M', 'у'), + (0x1E060, 'M', 'ф'), + (0x1E061, 'M', 'х'), + (0x1E062, 'M', 'ц'), + (0x1E063, 'M', 'ч'), + (0x1E064, 'M', 'ш'), + (0x1E065, 'M', 'ъ'), + (0x1E066, 'M', 'ы'), + (0x1E067, 'M', 'ґ'), + (0x1E068, 'M', 'і'), + (0x1E069, 'M', 'ѕ'), + (0x1E06A, 'M', 'џ'), + (0x1E06B, 'M', 'ҫ'), + (0x1E06C, 'M', 'ꙑ'), + (0x1E06D, 'M', 'ұ'), + (0x1E06E, 'X'), + (0x1E08F, 'V'), + (0x1E090, 'X'), + (0x1E100, 'V'), + (0x1E12D, 'X'), + (0x1E130, 'V'), + (0x1E13E, 'X'), + (0x1E140, 'V'), + (0x1E14A, 'X'), + (0x1E14E, 'V'), + (0x1E150, 'X'), + (0x1E290, 'V'), + (0x1E2AF, 'X'), + (0x1E2C0, 'V'), + (0x1E2FA, 'X'), + (0x1E2FF, 'V'), + (0x1E300, 'X'), + (0x1E4D0, 'V'), + (0x1E4FA, 'X'), + (0x1E7E0, 'V'), + (0x1E7E7, 'X'), + (0x1E7E8, 'V'), + (0x1E7EC, 'X'), + (0x1E7ED, 'V'), + (0x1E7EF, 'X'), + (0x1E7F0, 'V'), + (0x1E7FF, 'X'), + (0x1E800, 'V'), + (0x1E8C5, 'X'), + (0x1E8C7, 'V'), + (0x1E8D7, 'X'), + (0x1E900, 'M', '𞤢'), + (0x1E901, 'M', '𞤣'), + (0x1E902, 'M', '𞤤'), + (0x1E903, 'M', '𞤥'), + (0x1E904, 'M', '𞤦'), + (0x1E905, 'M', '𞤧'), + (0x1E906, 'M', '𞤨'), + (0x1E907, 'M', '𞤩'), + (0x1E908, 'M', '𞤪'), + (0x1E909, 'M', '𞤫'), + ] + +def _seg_72() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1E90A, 'M', '𞤬'), + (0x1E90B, 'M', '𞤭'), + (0x1E90C, 'M', '𞤮'), + (0x1E90D, 'M', '𞤯'), + (0x1E90E, 'M', '𞤰'), + (0x1E90F, 'M', '𞤱'), + (0x1E910, 'M', '𞤲'), + (0x1E911, 'M', '𞤳'), + (0x1E912, 'M', '𞤴'), + (0x1E913, 'M', '𞤵'), + (0x1E914, 'M', '𞤶'), + (0x1E915, 'M', '𞤷'), + (0x1E916, 'M', '𞤸'), + (0x1E917, 'M', '𞤹'), + (0x1E918, 'M', '𞤺'), + (0x1E919, 'M', '𞤻'), + (0x1E91A, 'M', '𞤼'), + (0x1E91B, 'M', '𞤽'), + (0x1E91C, 'M', '𞤾'), + (0x1E91D, 'M', '𞤿'), + (0x1E91E, 'M', '𞥀'), + (0x1E91F, 'M', '𞥁'), + (0x1E920, 'M', '𞥂'), + (0x1E921, 'M', '𞥃'), + (0x1E922, 'V'), + (0x1E94C, 'X'), + (0x1E950, 'V'), + (0x1E95A, 'X'), + (0x1E95E, 'V'), + (0x1E960, 'X'), + (0x1EC71, 'V'), + (0x1ECB5, 'X'), + (0x1ED01, 'V'), + (0x1ED3E, 'X'), + (0x1EE00, 'M', 'ا'), + (0x1EE01, 'M', 'ب'), + (0x1EE02, 'M', 'ج'), + (0x1EE03, 'M', 'د'), + (0x1EE04, 'X'), + (0x1EE05, 'M', 'و'), + (0x1EE06, 'M', 'ز'), + (0x1EE07, 'M', 'ح'), + (0x1EE08, 'M', 'ط'), + (0x1EE09, 'M', 'ي'), + (0x1EE0A, 'M', 'ك'), + (0x1EE0B, 'M', 'ل'), + (0x1EE0C, 'M', 'م'), + (0x1EE0D, 'M', 'ن'), + (0x1EE0E, 'M', 'س'), + (0x1EE0F, 'M', 'ع'), + (0x1EE10, 'M', 'ف'), + (0x1EE11, 'M', 'ص'), + (0x1EE12, 'M', 'ق'), + (0x1EE13, 'M', 'ر'), + (0x1EE14, 'M', 'ش'), + (0x1EE15, 'M', 'ت'), + (0x1EE16, 'M', 'ث'), + (0x1EE17, 'M', 'خ'), + (0x1EE18, 'M', 'ذ'), + (0x1EE19, 'M', 'ض'), + (0x1EE1A, 'M', 'ظ'), + (0x1EE1B, 'M', 'غ'), + (0x1EE1C, 'M', 'ٮ'), + (0x1EE1D, 'M', 'ں'), + (0x1EE1E, 'M', 'ڡ'), + (0x1EE1F, 'M', 'ٯ'), + (0x1EE20, 'X'), + (0x1EE21, 'M', 'ب'), + (0x1EE22, 'M', 'ج'), + (0x1EE23, 'X'), + (0x1EE24, 'M', 'ه'), + (0x1EE25, 'X'), + (0x1EE27, 'M', 'ح'), + (0x1EE28, 'X'), + (0x1EE29, 'M', 'ي'), + (0x1EE2A, 'M', 'ك'), + (0x1EE2B, 'M', 'ل'), + (0x1EE2C, 'M', 'م'), + (0x1EE2D, 'M', 'ن'), + (0x1EE2E, 'M', 'س'), + (0x1EE2F, 'M', 'ع'), + (0x1EE30, 'M', 'ف'), + (0x1EE31, 'M', 'ص'), + (0x1EE32, 'M', 'ق'), + (0x1EE33, 'X'), + (0x1EE34, 'M', 'ش'), + (0x1EE35, 'M', 'ت'), + (0x1EE36, 'M', 'ث'), + (0x1EE37, 'M', 'خ'), + (0x1EE38, 'X'), + (0x1EE39, 'M', 'ض'), + (0x1EE3A, 'X'), + (0x1EE3B, 'M', 'غ'), + (0x1EE3C, 'X'), + (0x1EE42, 'M', 'ج'), + (0x1EE43, 'X'), + (0x1EE47, 'M', 'ح'), + (0x1EE48, 'X'), + (0x1EE49, 'M', 'ي'), + (0x1EE4A, 'X'), + ] + +def _seg_73() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1EE4B, 'M', 'ل'), + (0x1EE4C, 'X'), + (0x1EE4D, 'M', 'ن'), + (0x1EE4E, 'M', 'س'), + (0x1EE4F, 'M', 'ع'), + (0x1EE50, 'X'), + (0x1EE51, 'M', 'ص'), + (0x1EE52, 'M', 'ق'), + (0x1EE53, 'X'), + (0x1EE54, 'M', 'ش'), + (0x1EE55, 'X'), + (0x1EE57, 'M', 'خ'), + (0x1EE58, 'X'), + (0x1EE59, 'M', 'ض'), + (0x1EE5A, 'X'), + (0x1EE5B, 'M', 'غ'), + (0x1EE5C, 'X'), + (0x1EE5D, 'M', 'ں'), + (0x1EE5E, 'X'), + (0x1EE5F, 'M', 'ٯ'), + (0x1EE60, 'X'), + (0x1EE61, 'M', 'ب'), + (0x1EE62, 'M', 'ج'), + (0x1EE63, 'X'), + (0x1EE64, 'M', 'ه'), + (0x1EE65, 'X'), + (0x1EE67, 'M', 'ح'), + (0x1EE68, 'M', 'ط'), + (0x1EE69, 'M', 'ي'), + (0x1EE6A, 'M', 'ك'), + (0x1EE6B, 'X'), + (0x1EE6C, 'M', 'م'), + (0x1EE6D, 'M', 'ن'), + (0x1EE6E, 'M', 'س'), + (0x1EE6F, 'M', 'ع'), + (0x1EE70, 'M', 'ف'), + (0x1EE71, 'M', 'ص'), + (0x1EE72, 'M', 'ق'), + (0x1EE73, 'X'), + (0x1EE74, 'M', 'ش'), + (0x1EE75, 'M', 'ت'), + (0x1EE76, 'M', 'ث'), + (0x1EE77, 'M', 'خ'), + (0x1EE78, 'X'), + (0x1EE79, 'M', 'ض'), + (0x1EE7A, 'M', 'ظ'), + (0x1EE7B, 'M', 'غ'), + (0x1EE7C, 'M', 'ٮ'), + (0x1EE7D, 'X'), + (0x1EE7E, 'M', 'ڡ'), + (0x1EE7F, 'X'), + (0x1EE80, 'M', 'ا'), + (0x1EE81, 'M', 'ب'), + (0x1EE82, 'M', 'ج'), + (0x1EE83, 'M', 'د'), + (0x1EE84, 'M', 'ه'), + (0x1EE85, 'M', 'و'), + (0x1EE86, 'M', 'ز'), + (0x1EE87, 'M', 'ح'), + (0x1EE88, 'M', 'ط'), + (0x1EE89, 'M', 'ي'), + (0x1EE8A, 'X'), + (0x1EE8B, 'M', 'ل'), + (0x1EE8C, 'M', 'م'), + (0x1EE8D, 'M', 'ن'), + (0x1EE8E, 'M', 'س'), + (0x1EE8F, 'M', 'ع'), + (0x1EE90, 'M', 'ف'), + (0x1EE91, 'M', 'ص'), + (0x1EE92, 'M', 'ق'), + (0x1EE93, 'M', 'ر'), + (0x1EE94, 'M', 'ش'), + (0x1EE95, 'M', 'ت'), + (0x1EE96, 'M', 'ث'), + (0x1EE97, 'M', 'خ'), + (0x1EE98, 'M', 'ذ'), + (0x1EE99, 'M', 'ض'), + (0x1EE9A, 'M', 'ظ'), + (0x1EE9B, 'M', 'غ'), + (0x1EE9C, 'X'), + (0x1EEA1, 'M', 'ب'), + (0x1EEA2, 'M', 'ج'), + (0x1EEA3, 'M', 'د'), + (0x1EEA4, 'X'), + (0x1EEA5, 'M', 'و'), + (0x1EEA6, 'M', 'ز'), + (0x1EEA7, 'M', 'ح'), + (0x1EEA8, 'M', 'ط'), + (0x1EEA9, 'M', 'ي'), + (0x1EEAA, 'X'), + (0x1EEAB, 'M', 'ل'), + (0x1EEAC, 'M', 'م'), + (0x1EEAD, 'M', 'ن'), + (0x1EEAE, 'M', 'س'), + (0x1EEAF, 'M', 'ع'), + (0x1EEB0, 'M', 'ف'), + (0x1EEB1, 'M', 'ص'), + (0x1EEB2, 'M', 'ق'), + (0x1EEB3, 'M', 'ر'), + (0x1EEB4, 'M', 'ش'), + ] + +def _seg_74() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1EEB5, 'M', 'ت'), + (0x1EEB6, 'M', 'ث'), + (0x1EEB7, 'M', 'خ'), + (0x1EEB8, 'M', 'ذ'), + (0x1EEB9, 'M', 'ض'), + (0x1EEBA, 'M', 'ظ'), + (0x1EEBB, 'M', 'غ'), + (0x1EEBC, 'X'), + (0x1EEF0, 'V'), + (0x1EEF2, 'X'), + (0x1F000, 'V'), + (0x1F02C, 'X'), + (0x1F030, 'V'), + (0x1F094, 'X'), + (0x1F0A0, 'V'), + (0x1F0AF, 'X'), + (0x1F0B1, 'V'), + (0x1F0C0, 'X'), + (0x1F0C1, 'V'), + (0x1F0D0, 'X'), + (0x1F0D1, 'V'), + (0x1F0F6, 'X'), + (0x1F101, '3', '0,'), + (0x1F102, '3', '1,'), + (0x1F103, '3', '2,'), + (0x1F104, '3', '3,'), + (0x1F105, '3', '4,'), + (0x1F106, '3', '5,'), + (0x1F107, '3', '6,'), + (0x1F108, '3', '7,'), + (0x1F109, '3', '8,'), + (0x1F10A, '3', '9,'), + (0x1F10B, 'V'), + (0x1F110, '3', '(a)'), + (0x1F111, '3', '(b)'), + (0x1F112, '3', '(c)'), + (0x1F113, '3', '(d)'), + (0x1F114, '3', '(e)'), + (0x1F115, '3', '(f)'), + (0x1F116, '3', '(g)'), + (0x1F117, '3', '(h)'), + (0x1F118, '3', '(i)'), + (0x1F119, '3', '(j)'), + (0x1F11A, '3', '(k)'), + (0x1F11B, '3', '(l)'), + (0x1F11C, '3', '(m)'), + (0x1F11D, '3', '(n)'), + (0x1F11E, '3', '(o)'), + (0x1F11F, '3', '(p)'), + (0x1F120, '3', '(q)'), + (0x1F121, '3', '(r)'), + (0x1F122, '3', '(s)'), + (0x1F123, '3', '(t)'), + (0x1F124, '3', '(u)'), + (0x1F125, '3', '(v)'), + (0x1F126, '3', '(w)'), + (0x1F127, '3', '(x)'), + (0x1F128, '3', '(y)'), + (0x1F129, '3', '(z)'), + (0x1F12A, 'M', '〔s〕'), + (0x1F12B, 'M', 'c'), + (0x1F12C, 'M', 'r'), + (0x1F12D, 'M', 'cd'), + (0x1F12E, 'M', 'wz'), + (0x1F12F, 'V'), + (0x1F130, 'M', 'a'), + (0x1F131, 'M', 'b'), + (0x1F132, 'M', 'c'), + (0x1F133, 'M', 'd'), + (0x1F134, 'M', 'e'), + (0x1F135, 'M', 'f'), + (0x1F136, 'M', 'g'), + (0x1F137, 'M', 'h'), + (0x1F138, 'M', 'i'), + (0x1F139, 'M', 'j'), + (0x1F13A, 'M', 'k'), + (0x1F13B, 'M', 'l'), + (0x1F13C, 'M', 'm'), + (0x1F13D, 'M', 'n'), + (0x1F13E, 'M', 'o'), + (0x1F13F, 'M', 'p'), + (0x1F140, 'M', 'q'), + (0x1F141, 'M', 'r'), + (0x1F142, 'M', 's'), + (0x1F143, 'M', 't'), + (0x1F144, 'M', 'u'), + (0x1F145, 'M', 'v'), + (0x1F146, 'M', 'w'), + (0x1F147, 'M', 'x'), + (0x1F148, 'M', 'y'), + (0x1F149, 'M', 'z'), + (0x1F14A, 'M', 'hv'), + (0x1F14B, 'M', 'mv'), + (0x1F14C, 'M', 'sd'), + (0x1F14D, 'M', 'ss'), + (0x1F14E, 'M', 'ppv'), + (0x1F14F, 'M', 'wc'), + (0x1F150, 'V'), + (0x1F16A, 'M', 'mc'), + (0x1F16B, 'M', 'md'), + ] + +def _seg_75() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1F16C, 'M', 'mr'), + (0x1F16D, 'V'), + (0x1F190, 'M', 'dj'), + (0x1F191, 'V'), + (0x1F1AE, 'X'), + (0x1F1E6, 'V'), + (0x1F200, 'M', 'ほか'), + (0x1F201, 'M', 'ココ'), + (0x1F202, 'M', 'サ'), + (0x1F203, 'X'), + (0x1F210, 'M', '手'), + (0x1F211, 'M', '字'), + (0x1F212, 'M', '双'), + (0x1F213, 'M', 'デ'), + (0x1F214, 'M', '二'), + (0x1F215, 'M', '多'), + (0x1F216, 'M', '解'), + (0x1F217, 'M', '天'), + (0x1F218, 'M', '交'), + (0x1F219, 'M', '映'), + (0x1F21A, 'M', '無'), + (0x1F21B, 'M', '料'), + (0x1F21C, 'M', '前'), + (0x1F21D, 'M', '後'), + (0x1F21E, 'M', '再'), + (0x1F21F, 'M', '新'), + (0x1F220, 'M', '初'), + (0x1F221, 'M', '終'), + (0x1F222, 'M', '生'), + (0x1F223, 'M', '販'), + (0x1F224, 'M', '声'), + (0x1F225, 'M', '吹'), + (0x1F226, 'M', '演'), + (0x1F227, 'M', '投'), + (0x1F228, 'M', '捕'), + (0x1F229, 'M', '一'), + (0x1F22A, 'M', '三'), + (0x1F22B, 'M', '遊'), + (0x1F22C, 'M', '左'), + (0x1F22D, 'M', '中'), + (0x1F22E, 'M', '右'), + (0x1F22F, 'M', '指'), + (0x1F230, 'M', '走'), + (0x1F231, 'M', '打'), + (0x1F232, 'M', '禁'), + (0x1F233, 'M', '空'), + (0x1F234, 'M', '合'), + (0x1F235, 'M', '満'), + (0x1F236, 'M', '有'), + (0x1F237, 'M', '月'), + (0x1F238, 'M', '申'), + (0x1F239, 'M', '割'), + (0x1F23A, 'M', '営'), + (0x1F23B, 'M', '配'), + (0x1F23C, 'X'), + (0x1F240, 'M', '〔本〕'), + (0x1F241, 'M', '〔三〕'), + (0x1F242, 'M', '〔二〕'), + (0x1F243, 'M', '〔安〕'), + (0x1F244, 'M', '〔点〕'), + (0x1F245, 'M', '〔打〕'), + (0x1F246, 'M', '〔盗〕'), + (0x1F247, 'M', '〔勝〕'), + (0x1F248, 'M', '〔敗〕'), + (0x1F249, 'X'), + (0x1F250, 'M', '得'), + (0x1F251, 'M', '可'), + (0x1F252, 'X'), + (0x1F260, 'V'), + (0x1F266, 'X'), + (0x1F300, 'V'), + (0x1F6D8, 'X'), + (0x1F6DC, 'V'), + (0x1F6ED, 'X'), + (0x1F6F0, 'V'), + (0x1F6FD, 'X'), + (0x1F700, 'V'), + (0x1F777, 'X'), + (0x1F77B, 'V'), + (0x1F7DA, 'X'), + (0x1F7E0, 'V'), + (0x1F7EC, 'X'), + (0x1F7F0, 'V'), + (0x1F7F1, 'X'), + (0x1F800, 'V'), + (0x1F80C, 'X'), + (0x1F810, 'V'), + (0x1F848, 'X'), + (0x1F850, 'V'), + (0x1F85A, 'X'), + (0x1F860, 'V'), + (0x1F888, 'X'), + (0x1F890, 'V'), + (0x1F8AE, 'X'), + (0x1F8B0, 'V'), + (0x1F8B2, 'X'), + (0x1F900, 'V'), + (0x1FA54, 'X'), + (0x1FA60, 'V'), + (0x1FA6E, 'X'), + ] + +def _seg_76() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1FA70, 'V'), + (0x1FA7D, 'X'), + (0x1FA80, 'V'), + (0x1FA89, 'X'), + (0x1FA90, 'V'), + (0x1FABE, 'X'), + (0x1FABF, 'V'), + (0x1FAC6, 'X'), + (0x1FACE, 'V'), + (0x1FADC, 'X'), + (0x1FAE0, 'V'), + (0x1FAE9, 'X'), + (0x1FAF0, 'V'), + (0x1FAF9, 'X'), + (0x1FB00, 'V'), + (0x1FB93, 'X'), + (0x1FB94, 'V'), + (0x1FBCB, 'X'), + (0x1FBF0, 'M', '0'), + (0x1FBF1, 'M', '1'), + (0x1FBF2, 'M', '2'), + (0x1FBF3, 'M', '3'), + (0x1FBF4, 'M', '4'), + (0x1FBF5, 'M', '5'), + (0x1FBF6, 'M', '6'), + (0x1FBF7, 'M', '7'), + (0x1FBF8, 'M', '8'), + (0x1FBF9, 'M', '9'), + (0x1FBFA, 'X'), + (0x20000, 'V'), + (0x2A6E0, 'X'), + (0x2A700, 'V'), + (0x2B73A, 'X'), + (0x2B740, 'V'), + (0x2B81E, 'X'), + (0x2B820, 'V'), + (0x2CEA2, 'X'), + (0x2CEB0, 'V'), + (0x2EBE1, 'X'), + (0x2F800, 'M', '丽'), + (0x2F801, 'M', '丸'), + (0x2F802, 'M', '乁'), + (0x2F803, 'M', '𠄢'), + (0x2F804, 'M', '你'), + (0x2F805, 'M', '侮'), + (0x2F806, 'M', '侻'), + (0x2F807, 'M', '倂'), + (0x2F808, 'M', '偺'), + (0x2F809, 'M', '備'), + (0x2F80A, 'M', '僧'), + (0x2F80B, 'M', '像'), + (0x2F80C, 'M', '㒞'), + (0x2F80D, 'M', '𠘺'), + (0x2F80E, 'M', '免'), + (0x2F80F, 'M', '兔'), + (0x2F810, 'M', '兤'), + (0x2F811, 'M', '具'), + (0x2F812, 'M', '𠔜'), + (0x2F813, 'M', '㒹'), + (0x2F814, 'M', '內'), + (0x2F815, 'M', '再'), + (0x2F816, 'M', '𠕋'), + (0x2F817, 'M', '冗'), + (0x2F818, 'M', '冤'), + (0x2F819, 'M', '仌'), + (0x2F81A, 'M', '冬'), + (0x2F81B, 'M', '况'), + (0x2F81C, 'M', '𩇟'), + (0x2F81D, 'M', '凵'), + (0x2F81E, 'M', '刃'), + (0x2F81F, 'M', '㓟'), + (0x2F820, 'M', '刻'), + (0x2F821, 'M', '剆'), + (0x2F822, 'M', '割'), + (0x2F823, 'M', '剷'), + (0x2F824, 'M', '㔕'), + (0x2F825, 'M', '勇'), + (0x2F826, 'M', '勉'), + (0x2F827, 'M', '勤'), + (0x2F828, 'M', '勺'), + (0x2F829, 'M', '包'), + (0x2F82A, 'M', '匆'), + (0x2F82B, 'M', '北'), + (0x2F82C, 'M', '卉'), + (0x2F82D, 'M', '卑'), + (0x2F82E, 'M', '博'), + (0x2F82F, 'M', '即'), + (0x2F830, 'M', '卽'), + (0x2F831, 'M', '卿'), + (0x2F834, 'M', '𠨬'), + (0x2F835, 'M', '灰'), + (0x2F836, 'M', '及'), + (0x2F837, 'M', '叟'), + (0x2F838, 'M', '𠭣'), + (0x2F839, 'M', '叫'), + (0x2F83A, 'M', '叱'), + (0x2F83B, 'M', '吆'), + (0x2F83C, 'M', '咞'), + (0x2F83D, 'M', '吸'), + (0x2F83E, 'M', '呈'), + ] + +def _seg_77() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F83F, 'M', '周'), + (0x2F840, 'M', '咢'), + (0x2F841, 'M', '哶'), + (0x2F842, 'M', '唐'), + (0x2F843, 'M', '啓'), + (0x2F844, 'M', '啣'), + (0x2F845, 'M', '善'), + (0x2F847, 'M', '喙'), + (0x2F848, 'M', '喫'), + (0x2F849, 'M', '喳'), + (0x2F84A, 'M', '嗂'), + (0x2F84B, 'M', '圖'), + (0x2F84C, 'M', '嘆'), + (0x2F84D, 'M', '圗'), + (0x2F84E, 'M', '噑'), + (0x2F84F, 'M', '噴'), + (0x2F850, 'M', '切'), + (0x2F851, 'M', '壮'), + (0x2F852, 'M', '城'), + (0x2F853, 'M', '埴'), + (0x2F854, 'M', '堍'), + (0x2F855, 'M', '型'), + (0x2F856, 'M', '堲'), + (0x2F857, 'M', '報'), + (0x2F858, 'M', '墬'), + (0x2F859, 'M', '𡓤'), + (0x2F85A, 'M', '売'), + (0x2F85B, 'M', '壷'), + (0x2F85C, 'M', '夆'), + (0x2F85D, 'M', '多'), + (0x2F85E, 'M', '夢'), + (0x2F85F, 'M', '奢'), + (0x2F860, 'M', '𡚨'), + (0x2F861, 'M', '𡛪'), + (0x2F862, 'M', '姬'), + (0x2F863, 'M', '娛'), + (0x2F864, 'M', '娧'), + (0x2F865, 'M', '姘'), + (0x2F866, 'M', '婦'), + (0x2F867, 'M', '㛮'), + (0x2F868, 'X'), + (0x2F869, 'M', '嬈'), + (0x2F86A, 'M', '嬾'), + (0x2F86C, 'M', '𡧈'), + (0x2F86D, 'M', '寃'), + (0x2F86E, 'M', '寘'), + (0x2F86F, 'M', '寧'), + (0x2F870, 'M', '寳'), + (0x2F871, 'M', '𡬘'), + (0x2F872, 'M', '寿'), + (0x2F873, 'M', '将'), + (0x2F874, 'X'), + (0x2F875, 'M', '尢'), + (0x2F876, 'M', '㞁'), + (0x2F877, 'M', '屠'), + (0x2F878, 'M', '屮'), + (0x2F879, 'M', '峀'), + (0x2F87A, 'M', '岍'), + (0x2F87B, 'M', '𡷤'), + (0x2F87C, 'M', '嵃'), + (0x2F87D, 'M', '𡷦'), + (0x2F87E, 'M', '嵮'), + (0x2F87F, 'M', '嵫'), + (0x2F880, 'M', '嵼'), + (0x2F881, 'M', '巡'), + (0x2F882, 'M', '巢'), + (0x2F883, 'M', '㠯'), + (0x2F884, 'M', '巽'), + (0x2F885, 'M', '帨'), + (0x2F886, 'M', '帽'), + (0x2F887, 'M', '幩'), + (0x2F888, 'M', '㡢'), + (0x2F889, 'M', '𢆃'), + (0x2F88A, 'M', '㡼'), + (0x2F88B, 'M', '庰'), + (0x2F88C, 'M', '庳'), + (0x2F88D, 'M', '庶'), + (0x2F88E, 'M', '廊'), + (0x2F88F, 'M', '𪎒'), + (0x2F890, 'M', '廾'), + (0x2F891, 'M', '𢌱'), + (0x2F893, 'M', '舁'), + (0x2F894, 'M', '弢'), + (0x2F896, 'M', '㣇'), + (0x2F897, 'M', '𣊸'), + (0x2F898, 'M', '𦇚'), + (0x2F899, 'M', '形'), + (0x2F89A, 'M', '彫'), + (0x2F89B, 'M', '㣣'), + (0x2F89C, 'M', '徚'), + (0x2F89D, 'M', '忍'), + (0x2F89E, 'M', '志'), + (0x2F89F, 'M', '忹'), + (0x2F8A0, 'M', '悁'), + (0x2F8A1, 'M', '㤺'), + (0x2F8A2, 'M', '㤜'), + (0x2F8A3, 'M', '悔'), + (0x2F8A4, 'M', '𢛔'), + (0x2F8A5, 'M', '惇'), + (0x2F8A6, 'M', '慈'), + ] + +def _seg_78() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F8A7, 'M', '慌'), + (0x2F8A8, 'M', '慎'), + (0x2F8A9, 'M', '慌'), + (0x2F8AA, 'M', '慺'), + (0x2F8AB, 'M', '憎'), + (0x2F8AC, 'M', '憲'), + (0x2F8AD, 'M', '憤'), + (0x2F8AE, 'M', '憯'), + (0x2F8AF, 'M', '懞'), + (0x2F8B0, 'M', '懲'), + (0x2F8B1, 'M', '懶'), + (0x2F8B2, 'M', '成'), + (0x2F8B3, 'M', '戛'), + (0x2F8B4, 'M', '扝'), + (0x2F8B5, 'M', '抱'), + (0x2F8B6, 'M', '拔'), + (0x2F8B7, 'M', '捐'), + (0x2F8B8, 'M', '𢬌'), + (0x2F8B9, 'M', '挽'), + (0x2F8BA, 'M', '拼'), + (0x2F8BB, 'M', '捨'), + (0x2F8BC, 'M', '掃'), + (0x2F8BD, 'M', '揤'), + (0x2F8BE, 'M', '𢯱'), + (0x2F8BF, 'M', '搢'), + (0x2F8C0, 'M', '揅'), + (0x2F8C1, 'M', '掩'), + (0x2F8C2, 'M', '㨮'), + (0x2F8C3, 'M', '摩'), + (0x2F8C4, 'M', '摾'), + (0x2F8C5, 'M', '撝'), + (0x2F8C6, 'M', '摷'), + (0x2F8C7, 'M', '㩬'), + (0x2F8C8, 'M', '敏'), + (0x2F8C9, 'M', '敬'), + (0x2F8CA, 'M', '𣀊'), + (0x2F8CB, 'M', '旣'), + (0x2F8CC, 'M', '書'), + (0x2F8CD, 'M', '晉'), + (0x2F8CE, 'M', '㬙'), + (0x2F8CF, 'M', '暑'), + (0x2F8D0, 'M', '㬈'), + (0x2F8D1, 'M', '㫤'), + (0x2F8D2, 'M', '冒'), + (0x2F8D3, 'M', '冕'), + (0x2F8D4, 'M', '最'), + (0x2F8D5, 'M', '暜'), + (0x2F8D6, 'M', '肭'), + (0x2F8D7, 'M', '䏙'), + (0x2F8D8, 'M', '朗'), + (0x2F8D9, 'M', '望'), + (0x2F8DA, 'M', '朡'), + (0x2F8DB, 'M', '杞'), + (0x2F8DC, 'M', '杓'), + (0x2F8DD, 'M', '𣏃'), + (0x2F8DE, 'M', '㭉'), + (0x2F8DF, 'M', '柺'), + (0x2F8E0, 'M', '枅'), + (0x2F8E1, 'M', '桒'), + (0x2F8E2, 'M', '梅'), + (0x2F8E3, 'M', '𣑭'), + (0x2F8E4, 'M', '梎'), + (0x2F8E5, 'M', '栟'), + (0x2F8E6, 'M', '椔'), + (0x2F8E7, 'M', '㮝'), + (0x2F8E8, 'M', '楂'), + (0x2F8E9, 'M', '榣'), + (0x2F8EA, 'M', '槪'), + (0x2F8EB, 'M', '檨'), + (0x2F8EC, 'M', '𣚣'), + (0x2F8ED, 'M', '櫛'), + (0x2F8EE, 'M', '㰘'), + (0x2F8EF, 'M', '次'), + (0x2F8F0, 'M', '𣢧'), + (0x2F8F1, 'M', '歔'), + (0x2F8F2, 'M', '㱎'), + (0x2F8F3, 'M', '歲'), + (0x2F8F4, 'M', '殟'), + (0x2F8F5, 'M', '殺'), + (0x2F8F6, 'M', '殻'), + (0x2F8F7, 'M', '𣪍'), + (0x2F8F8, 'M', '𡴋'), + (0x2F8F9, 'M', '𣫺'), + (0x2F8FA, 'M', '汎'), + (0x2F8FB, 'M', '𣲼'), + (0x2F8FC, 'M', '沿'), + (0x2F8FD, 'M', '泍'), + (0x2F8FE, 'M', '汧'), + (0x2F8FF, 'M', '洖'), + (0x2F900, 'M', '派'), + (0x2F901, 'M', '海'), + (0x2F902, 'M', '流'), + (0x2F903, 'M', '浩'), + (0x2F904, 'M', '浸'), + (0x2F905, 'M', '涅'), + (0x2F906, 'M', '𣴞'), + (0x2F907, 'M', '洴'), + (0x2F908, 'M', '港'), + (0x2F909, 'M', '湮'), + (0x2F90A, 'M', '㴳'), + ] + +def _seg_79() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F90B, 'M', '滋'), + (0x2F90C, 'M', '滇'), + (0x2F90D, 'M', '𣻑'), + (0x2F90E, 'M', '淹'), + (0x2F90F, 'M', '潮'), + (0x2F910, 'M', '𣽞'), + (0x2F911, 'M', '𣾎'), + (0x2F912, 'M', '濆'), + (0x2F913, 'M', '瀹'), + (0x2F914, 'M', '瀞'), + (0x2F915, 'M', '瀛'), + (0x2F916, 'M', '㶖'), + (0x2F917, 'M', '灊'), + (0x2F918, 'M', '災'), + (0x2F919, 'M', '灷'), + (0x2F91A, 'M', '炭'), + (0x2F91B, 'M', '𠔥'), + (0x2F91C, 'M', '煅'), + (0x2F91D, 'M', '𤉣'), + (0x2F91E, 'M', '熜'), + (0x2F91F, 'X'), + (0x2F920, 'M', '爨'), + (0x2F921, 'M', '爵'), + (0x2F922, 'M', '牐'), + (0x2F923, 'M', '𤘈'), + (0x2F924, 'M', '犀'), + (0x2F925, 'M', '犕'), + (0x2F926, 'M', '𤜵'), + (0x2F927, 'M', '𤠔'), + (0x2F928, 'M', '獺'), + (0x2F929, 'M', '王'), + (0x2F92A, 'M', '㺬'), + (0x2F92B, 'M', '玥'), + (0x2F92C, 'M', '㺸'), + (0x2F92E, 'M', '瑇'), + (0x2F92F, 'M', '瑜'), + (0x2F930, 'M', '瑱'), + (0x2F931, 'M', '璅'), + (0x2F932, 'M', '瓊'), + (0x2F933, 'M', '㼛'), + (0x2F934, 'M', '甤'), + (0x2F935, 'M', '𤰶'), + (0x2F936, 'M', '甾'), + (0x2F937, 'M', '𤲒'), + (0x2F938, 'M', '異'), + (0x2F939, 'M', '𢆟'), + (0x2F93A, 'M', '瘐'), + (0x2F93B, 'M', '𤾡'), + (0x2F93C, 'M', '𤾸'), + (0x2F93D, 'M', '𥁄'), + (0x2F93E, 'M', '㿼'), + (0x2F93F, 'M', '䀈'), + (0x2F940, 'M', '直'), + (0x2F941, 'M', '𥃳'), + (0x2F942, 'M', '𥃲'), + (0x2F943, 'M', '𥄙'), + (0x2F944, 'M', '𥄳'), + (0x2F945, 'M', '眞'), + (0x2F946, 'M', '真'), + (0x2F948, 'M', '睊'), + (0x2F949, 'M', '䀹'), + (0x2F94A, 'M', '瞋'), + (0x2F94B, 'M', '䁆'), + (0x2F94C, 'M', '䂖'), + (0x2F94D, 'M', '𥐝'), + (0x2F94E, 'M', '硎'), + (0x2F94F, 'M', '碌'), + (0x2F950, 'M', '磌'), + (0x2F951, 'M', '䃣'), + (0x2F952, 'M', '𥘦'), + (0x2F953, 'M', '祖'), + (0x2F954, 'M', '𥚚'), + (0x2F955, 'M', '𥛅'), + (0x2F956, 'M', '福'), + (0x2F957, 'M', '秫'), + (0x2F958, 'M', '䄯'), + (0x2F959, 'M', '穀'), + (0x2F95A, 'M', '穊'), + (0x2F95B, 'M', '穏'), + (0x2F95C, 'M', '𥥼'), + (0x2F95D, 'M', '𥪧'), + (0x2F95F, 'X'), + (0x2F960, 'M', '䈂'), + (0x2F961, 'M', '𥮫'), + (0x2F962, 'M', '篆'), + (0x2F963, 'M', '築'), + (0x2F964, 'M', '䈧'), + (0x2F965, 'M', '𥲀'), + (0x2F966, 'M', '糒'), + (0x2F967, 'M', '䊠'), + (0x2F968, 'M', '糨'), + (0x2F969, 'M', '糣'), + (0x2F96A, 'M', '紀'), + (0x2F96B, 'M', '𥾆'), + (0x2F96C, 'M', '絣'), + (0x2F96D, 'M', '䌁'), + (0x2F96E, 'M', '緇'), + (0x2F96F, 'M', '縂'), + (0x2F970, 'M', '繅'), + (0x2F971, 'M', '䌴'), + ] + +def _seg_80() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F972, 'M', '𦈨'), + (0x2F973, 'M', '𦉇'), + (0x2F974, 'M', '䍙'), + (0x2F975, 'M', '𦋙'), + (0x2F976, 'M', '罺'), + (0x2F977, 'M', '𦌾'), + (0x2F978, 'M', '羕'), + (0x2F979, 'M', '翺'), + (0x2F97A, 'M', '者'), + (0x2F97B, 'M', '𦓚'), + (0x2F97C, 'M', '𦔣'), + (0x2F97D, 'M', '聠'), + (0x2F97E, 'M', '𦖨'), + (0x2F97F, 'M', '聰'), + (0x2F980, 'M', '𣍟'), + (0x2F981, 'M', '䏕'), + (0x2F982, 'M', '育'), + (0x2F983, 'M', '脃'), + (0x2F984, 'M', '䐋'), + (0x2F985, 'M', '脾'), + (0x2F986, 'M', '媵'), + (0x2F987, 'M', '𦞧'), + (0x2F988, 'M', '𦞵'), + (0x2F989, 'M', '𣎓'), + (0x2F98A, 'M', '𣎜'), + (0x2F98B, 'M', '舁'), + (0x2F98C, 'M', '舄'), + (0x2F98D, 'M', '辞'), + (0x2F98E, 'M', '䑫'), + (0x2F98F, 'M', '芑'), + (0x2F990, 'M', '芋'), + (0x2F991, 'M', '芝'), + (0x2F992, 'M', '劳'), + (0x2F993, 'M', '花'), + (0x2F994, 'M', '芳'), + (0x2F995, 'M', '芽'), + (0x2F996, 'M', '苦'), + (0x2F997, 'M', '𦬼'), + (0x2F998, 'M', '若'), + (0x2F999, 'M', '茝'), + (0x2F99A, 'M', '荣'), + (0x2F99B, 'M', '莭'), + (0x2F99C, 'M', '茣'), + (0x2F99D, 'M', '莽'), + (0x2F99E, 'M', '菧'), + (0x2F99F, 'M', '著'), + (0x2F9A0, 'M', '荓'), + (0x2F9A1, 'M', '菊'), + (0x2F9A2, 'M', '菌'), + (0x2F9A3, 'M', '菜'), + (0x2F9A4, 'M', '𦰶'), + (0x2F9A5, 'M', '𦵫'), + (0x2F9A6, 'M', '𦳕'), + (0x2F9A7, 'M', '䔫'), + (0x2F9A8, 'M', '蓱'), + (0x2F9A9, 'M', '蓳'), + (0x2F9AA, 'M', '蔖'), + (0x2F9AB, 'M', '𧏊'), + (0x2F9AC, 'M', '蕤'), + (0x2F9AD, 'M', '𦼬'), + (0x2F9AE, 'M', '䕝'), + (0x2F9AF, 'M', '䕡'), + (0x2F9B0, 'M', '𦾱'), + (0x2F9B1, 'M', '𧃒'), + (0x2F9B2, 'M', '䕫'), + (0x2F9B3, 'M', '虐'), + (0x2F9B4, 'M', '虜'), + (0x2F9B5, 'M', '虧'), + (0x2F9B6, 'M', '虩'), + (0x2F9B7, 'M', '蚩'), + (0x2F9B8, 'M', '蚈'), + (0x2F9B9, 'M', '蜎'), + (0x2F9BA, 'M', '蛢'), + (0x2F9BB, 'M', '蝹'), + (0x2F9BC, 'M', '蜨'), + (0x2F9BD, 'M', '蝫'), + (0x2F9BE, 'M', '螆'), + (0x2F9BF, 'X'), + (0x2F9C0, 'M', '蟡'), + (0x2F9C1, 'M', '蠁'), + (0x2F9C2, 'M', '䗹'), + (0x2F9C3, 'M', '衠'), + (0x2F9C4, 'M', '衣'), + (0x2F9C5, 'M', '𧙧'), + (0x2F9C6, 'M', '裗'), + (0x2F9C7, 'M', '裞'), + (0x2F9C8, 'M', '䘵'), + (0x2F9C9, 'M', '裺'), + (0x2F9CA, 'M', '㒻'), + (0x2F9CB, 'M', '𧢮'), + (0x2F9CC, 'M', '𧥦'), + (0x2F9CD, 'M', '䚾'), + (0x2F9CE, 'M', '䛇'), + (0x2F9CF, 'M', '誠'), + (0x2F9D0, 'M', '諭'), + (0x2F9D1, 'M', '變'), + (0x2F9D2, 'M', '豕'), + (0x2F9D3, 'M', '𧲨'), + (0x2F9D4, 'M', '貫'), + (0x2F9D5, 'M', '賁'), + ] + +def _seg_81() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F9D6, 'M', '贛'), + (0x2F9D7, 'M', '起'), + (0x2F9D8, 'M', '𧼯'), + (0x2F9D9, 'M', '𠠄'), + (0x2F9DA, 'M', '跋'), + (0x2F9DB, 'M', '趼'), + (0x2F9DC, 'M', '跰'), + (0x2F9DD, 'M', '𠣞'), + (0x2F9DE, 'M', '軔'), + (0x2F9DF, 'M', '輸'), + (0x2F9E0, 'M', '𨗒'), + (0x2F9E1, 'M', '𨗭'), + (0x2F9E2, 'M', '邔'), + (0x2F9E3, 'M', '郱'), + (0x2F9E4, 'M', '鄑'), + (0x2F9E5, 'M', '𨜮'), + (0x2F9E6, 'M', '鄛'), + (0x2F9E7, 'M', '鈸'), + (0x2F9E8, 'M', '鋗'), + (0x2F9E9, 'M', '鋘'), + (0x2F9EA, 'M', '鉼'), + (0x2F9EB, 'M', '鏹'), + (0x2F9EC, 'M', '鐕'), + (0x2F9ED, 'M', '𨯺'), + (0x2F9EE, 'M', '開'), + (0x2F9EF, 'M', '䦕'), + (0x2F9F0, 'M', '閷'), + (0x2F9F1, 'M', '𨵷'), + (0x2F9F2, 'M', '䧦'), + (0x2F9F3, 'M', '雃'), + (0x2F9F4, 'M', '嶲'), + (0x2F9F5, 'M', '霣'), + (0x2F9F6, 'M', '𩅅'), + (0x2F9F7, 'M', '𩈚'), + (0x2F9F8, 'M', '䩮'), + (0x2F9F9, 'M', '䩶'), + (0x2F9FA, 'M', '韠'), + (0x2F9FB, 'M', '𩐊'), + (0x2F9FC, 'M', '䪲'), + (0x2F9FD, 'M', '𩒖'), + (0x2F9FE, 'M', '頋'), + (0x2FA00, 'M', '頩'), + (0x2FA01, 'M', '𩖶'), + (0x2FA02, 'M', '飢'), + (0x2FA03, 'M', '䬳'), + (0x2FA04, 'M', '餩'), + (0x2FA05, 'M', '馧'), + (0x2FA06, 'M', '駂'), + (0x2FA07, 'M', '駾'), + (0x2FA08, 'M', '䯎'), + (0x2FA09, 'M', '𩬰'), + (0x2FA0A, 'M', '鬒'), + (0x2FA0B, 'M', '鱀'), + (0x2FA0C, 'M', '鳽'), + (0x2FA0D, 'M', '䳎'), + (0x2FA0E, 'M', '䳭'), + (0x2FA0F, 'M', '鵧'), + (0x2FA10, 'M', '𪃎'), + (0x2FA11, 'M', '䳸'), + (0x2FA12, 'M', '𪄅'), + (0x2FA13, 'M', '𪈎'), + (0x2FA14, 'M', '𪊑'), + (0x2FA15, 'M', '麻'), + (0x2FA16, 'M', '䵖'), + (0x2FA17, 'M', '黹'), + (0x2FA18, 'M', '黾'), + (0x2FA19, 'M', '鼅'), + (0x2FA1A, 'M', '鼏'), + (0x2FA1B, 'M', '鼖'), + (0x2FA1C, 'M', '鼻'), + (0x2FA1D, 'M', '𪘀'), + (0x2FA1E, 'X'), + (0x30000, 'V'), + (0x3134B, 'X'), + (0x31350, 'V'), + (0x323B0, 'X'), + (0xE0100, 'I'), + (0xE01F0, 'X'), + ] + +uts46data = tuple( + _seg_0() + + _seg_1() + + _seg_2() + + _seg_3() + + _seg_4() + + _seg_5() + + _seg_6() + + _seg_7() + + _seg_8() + + _seg_9() + + _seg_10() + + _seg_11() + + _seg_12() + + _seg_13() + + _seg_14() + + _seg_15() + + _seg_16() + + _seg_17() + + _seg_18() + + _seg_19() + + _seg_20() + + _seg_21() + + _seg_22() + + _seg_23() + + _seg_24() + + _seg_25() + + _seg_26() + + _seg_27() + + _seg_28() + + _seg_29() + + _seg_30() + + _seg_31() + + _seg_32() + + _seg_33() + + _seg_34() + + _seg_35() + + _seg_36() + + _seg_37() + + _seg_38() + + _seg_39() + + _seg_40() + + _seg_41() + + _seg_42() + + _seg_43() + + _seg_44() + + _seg_45() + + _seg_46() + + _seg_47() + + _seg_48() + + _seg_49() + + _seg_50() + + _seg_51() + + _seg_52() + + _seg_53() + + _seg_54() + + _seg_55() + + _seg_56() + + _seg_57() + + _seg_58() + + _seg_59() + + _seg_60() + + _seg_61() + + _seg_62() + + _seg_63() + + _seg_64() + + _seg_65() + + _seg_66() + + _seg_67() + + _seg_68() + + _seg_69() + + _seg_70() + + _seg_71() + + _seg_72() + + _seg_73() + + _seg_74() + + _seg_75() + + _seg_76() + + _seg_77() + + _seg_78() + + _seg_79() + + _seg_80() + + _seg_81() +) # type: Tuple[Union[Tuple[int, str], Tuple[int, str, str]], ...] diff --git a/virt/lib/python3.9/site-packages/pip/_vendor/pkg_resources/__init__ 3.py b/virt/lib/python3.9/site-packages/pip/_vendor/pkg_resources/__init__ 3.py new file mode 100644 index 00000000..a254c688 --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_vendor/pkg_resources/__init__ 3.py @@ -0,0 +1,3361 @@ +""" +Package resource API +-------------------- + +A resource is a logical file contained within a package, or a logical +subdirectory thereof. The package resource API expects resource names +to have their path parts separated with ``/``, *not* whatever the local +path separator is. Do not use os.path operations to manipulate resource +names being passed into the API. + +The package resource API is designed to work with normal filesystem packages, +.egg files, and unpacked .egg files. It can also work in a limited way with +.zip files and with custom PEP 302 loaders that support the ``get_data()`` +method. + +This module is deprecated. Users are directed to :mod:`importlib.resources`, +:mod:`importlib.metadata` and :pypi:`packaging` instead. +""" + +import sys +import os +import io +import time +import re +import types +import zipfile +import zipimport +import warnings +import stat +import functools +import pkgutil +import operator +import platform +import collections +import plistlib +import email.parser +import errno +import tempfile +import textwrap +import inspect +import ntpath +import posixpath +import importlib +from pkgutil import get_importer + +try: + import _imp +except ImportError: + # Python 3.2 compatibility + import imp as _imp + +try: + FileExistsError +except NameError: + FileExistsError = OSError + +# capture these to bypass sandboxing +from os import utime + +try: + from os import mkdir, rename, unlink + + WRITE_SUPPORT = True +except ImportError: + # no write support, probably under GAE + WRITE_SUPPORT = False + +from os import open as os_open +from os.path import isdir, split + +try: + import importlib.machinery as importlib_machinery + + # access attribute to force import under delayed import mechanisms. + importlib_machinery.__name__ +except ImportError: + importlib_machinery = None + +from pip._internal.utils._jaraco_text import ( + yield_lines, + drop_comment, + join_continuation, +) + +from pip._vendor import platformdirs +from pip._vendor import packaging + +__import__('pip._vendor.packaging.version') +__import__('pip._vendor.packaging.specifiers') +__import__('pip._vendor.packaging.requirements') +__import__('pip._vendor.packaging.markers') +__import__('pip._vendor.packaging.utils') + +if sys.version_info < (3, 5): + raise RuntimeError("Python 3.5 or later is required") + +# declare some globals that will be defined later to +# satisfy the linters. +require = None +working_set = None +add_activation_listener = None +resources_stream = None +cleanup_resources = None +resource_dir = None +resource_stream = None +set_extraction_path = None +resource_isdir = None +resource_string = None +iter_entry_points = None +resource_listdir = None +resource_filename = None +resource_exists = None +_distribution_finders = None +_namespace_handlers = None +_namespace_packages = None + + +warnings.warn( + "pkg_resources is deprecated as an API. " + "See https://setuptools.pypa.io/en/latest/pkg_resources.html", + DeprecationWarning, + stacklevel=2 +) + + +_PEP440_FALLBACK = re.compile(r"^v?(?P<safe>(?:[0-9]+!)?[0-9]+(?:\.[0-9]+)*)", re.I) + + +class PEP440Warning(RuntimeWarning): + """ + Used when there is an issue with a version or specifier not complying with + PEP 440. + """ + + +parse_version = packaging.version.Version + + +_state_vars = {} + + +def _declare_state(vartype, **kw): + globals().update(kw) + _state_vars.update(dict.fromkeys(kw, vartype)) + + +def __getstate__(): + state = {} + g = globals() + for k, v in _state_vars.items(): + state[k] = g['_sget_' + v](g[k]) + return state + + +def __setstate__(state): + g = globals() + for k, v in state.items(): + g['_sset_' + _state_vars[k]](k, g[k], v) + return state + + +def _sget_dict(val): + return val.copy() + + +def _sset_dict(key, ob, state): + ob.clear() + ob.update(state) + + +def _sget_object(val): + return val.__getstate__() + + +def _sset_object(key, ob, state): + ob.__setstate__(state) + + +_sget_none = _sset_none = lambda *args: None + + +def get_supported_platform(): + """Return this platform's maximum compatible version. + + distutils.util.get_platform() normally reports the minimum version + of macOS that would be required to *use* extensions produced by + distutils. But what we want when checking compatibility is to know the + version of macOS that we are *running*. To allow usage of packages that + explicitly require a newer version of macOS, we must also know the + current version of the OS. + + If this condition occurs for any other platform with a version in its + platform strings, this function should be extended accordingly. + """ + plat = get_build_platform() + m = macosVersionString.match(plat) + if m is not None and sys.platform == "darwin": + try: + plat = 'macosx-%s-%s' % ('.'.join(_macos_vers()[:2]), m.group(3)) + except ValueError: + # not macOS + pass + return plat + + +__all__ = [ + # Basic resource access and distribution/entry point discovery + 'require', + 'run_script', + 'get_provider', + 'get_distribution', + 'load_entry_point', + 'get_entry_map', + 'get_entry_info', + 'iter_entry_points', + 'resource_string', + 'resource_stream', + 'resource_filename', + 'resource_listdir', + 'resource_exists', + 'resource_isdir', + # Environmental control + 'declare_namespace', + 'working_set', + 'add_activation_listener', + 'find_distributions', + 'set_extraction_path', + 'cleanup_resources', + 'get_default_cache', + # Primary implementation classes + 'Environment', + 'WorkingSet', + 'ResourceManager', + 'Distribution', + 'Requirement', + 'EntryPoint', + # Exceptions + 'ResolutionError', + 'VersionConflict', + 'DistributionNotFound', + 'UnknownExtra', + 'ExtractionError', + # Warnings + 'PEP440Warning', + # Parsing functions and string utilities + 'parse_requirements', + 'parse_version', + 'safe_name', + 'safe_version', + 'get_platform', + 'compatible_platforms', + 'yield_lines', + 'split_sections', + 'safe_extra', + 'to_filename', + 'invalid_marker', + 'evaluate_marker', + # filesystem utilities + 'ensure_directory', + 'normalize_path', + # Distribution "precedence" constants + 'EGG_DIST', + 'BINARY_DIST', + 'SOURCE_DIST', + 'CHECKOUT_DIST', + 'DEVELOP_DIST', + # "Provider" interfaces, implementations, and registration/lookup APIs + 'IMetadataProvider', + 'IResourceProvider', + 'FileMetadata', + 'PathMetadata', + 'EggMetadata', + 'EmptyProvider', + 'empty_provider', + 'NullProvider', + 'EggProvider', + 'DefaultProvider', + 'ZipProvider', + 'register_finder', + 'register_namespace_handler', + 'register_loader_type', + 'fixup_namespace_packages', + 'get_importer', + # Warnings + 'PkgResourcesDeprecationWarning', + # Deprecated/backward compatibility only + 'run_main', + 'AvailableDistributions', +] + + +class ResolutionError(Exception): + """Abstract base for dependency resolution errors""" + + def __repr__(self): + return self.__class__.__name__ + repr(self.args) + + +class VersionConflict(ResolutionError): + """ + An already-installed version conflicts with the requested version. + + Should be initialized with the installed Distribution and the requested + Requirement. + """ + + _template = "{self.dist} is installed but {self.req} is required" + + @property + def dist(self): + return self.args[0] + + @property + def req(self): + return self.args[1] + + def report(self): + return self._template.format(**locals()) + + def with_context(self, required_by): + """ + If required_by is non-empty, return a version of self that is a + ContextualVersionConflict. + """ + if not required_by: + return self + args = self.args + (required_by,) + return ContextualVersionConflict(*args) + + +class ContextualVersionConflict(VersionConflict): + """ + A VersionConflict that accepts a third parameter, the set of the + requirements that required the installed Distribution. + """ + + _template = VersionConflict._template + ' by {self.required_by}' + + @property + def required_by(self): + return self.args[2] + + +class DistributionNotFound(ResolutionError): + """A requested distribution was not found""" + + _template = ( + "The '{self.req}' distribution was not found " + "and is required by {self.requirers_str}" + ) + + @property + def req(self): + return self.args[0] + + @property + def requirers(self): + return self.args[1] + + @property + def requirers_str(self): + if not self.requirers: + return 'the application' + return ', '.join(self.requirers) + + def report(self): + return self._template.format(**locals()) + + def __str__(self): + return self.report() + + +class UnknownExtra(ResolutionError): + """Distribution doesn't have an "extra feature" of the given name""" + + +_provider_factories = {} + +PY_MAJOR = '{}.{}'.format(*sys.version_info) +EGG_DIST = 3 +BINARY_DIST = 2 +SOURCE_DIST = 1 +CHECKOUT_DIST = 0 +DEVELOP_DIST = -1 + + +def register_loader_type(loader_type, provider_factory): + """Register `provider_factory` to make providers for `loader_type` + + `loader_type` is the type or class of a PEP 302 ``module.__loader__``, + and `provider_factory` is a function that, passed a *module* object, + returns an ``IResourceProvider`` for that module. + """ + _provider_factories[loader_type] = provider_factory + + +def get_provider(moduleOrReq): + """Return an IResourceProvider for the named module or requirement""" + if isinstance(moduleOrReq, Requirement): + return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0] + try: + module = sys.modules[moduleOrReq] + except KeyError: + __import__(moduleOrReq) + module = sys.modules[moduleOrReq] + loader = getattr(module, '__loader__', None) + return _find_adapter(_provider_factories, loader)(module) + + +def _macos_vers(_cache=[]): + if not _cache: + version = platform.mac_ver()[0] + # fallback for MacPorts + if version == '': + plist = '/System/Library/CoreServices/SystemVersion.plist' + if os.path.exists(plist): + if hasattr(plistlib, 'readPlist'): + plist_content = plistlib.readPlist(plist) + if 'ProductVersion' in plist_content: + version = plist_content['ProductVersion'] + + _cache.append(version.split('.')) + return _cache[0] + + +def _macos_arch(machine): + return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine) + + +def get_build_platform(): + """Return this platform's string for platform-specific distributions + + XXX Currently this is the same as ``distutils.util.get_platform()``, but it + needs some hacks for Linux and macOS. + """ + from sysconfig import get_platform + + plat = get_platform() + if sys.platform == "darwin" and not plat.startswith('macosx-'): + try: + version = _macos_vers() + machine = os.uname()[4].replace(" ", "_") + return "macosx-%d.%d-%s" % ( + int(version[0]), + int(version[1]), + _macos_arch(machine), + ) + except ValueError: + # if someone is running a non-Mac darwin system, this will fall + # through to the default implementation + pass + return plat + + +macosVersionString = re.compile(r"macosx-(\d+)\.(\d+)-(.*)") +darwinVersionString = re.compile(r"darwin-(\d+)\.(\d+)\.(\d+)-(.*)") +# XXX backward compat +get_platform = get_build_platform + + +def compatible_platforms(provided, required): + """Can code for the `provided` platform run on the `required` platform? + + Returns true if either platform is ``None``, or the platforms are equal. + + XXX Needs compatibility checks for Linux and other unixy OSes. + """ + if provided is None or required is None or provided == required: + # easy case + return True + + # macOS special cases + reqMac = macosVersionString.match(required) + if reqMac: + provMac = macosVersionString.match(provided) + + # is this a Mac package? + if not provMac: + # this is backwards compatibility for packages built before + # setuptools 0.6. All packages built after this point will + # use the new macOS designation. + provDarwin = darwinVersionString.match(provided) + if provDarwin: + dversion = int(provDarwin.group(1)) + macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2)) + if ( + dversion == 7 + and macosversion >= "10.3" + or dversion == 8 + and macosversion >= "10.4" + ): + return True + # egg isn't macOS or legacy darwin + return False + + # are they the same major version and machine type? + if provMac.group(1) != reqMac.group(1) or provMac.group(3) != reqMac.group(3): + return False + + # is the required OS major update >= the provided one? + if int(provMac.group(2)) > int(reqMac.group(2)): + return False + + return True + + # XXX Linux and other platforms' special cases should go here + return False + + +def run_script(dist_spec, script_name): + """Locate distribution `dist_spec` and run its `script_name` script""" + ns = sys._getframe(1).f_globals + name = ns['__name__'] + ns.clear() + ns['__name__'] = name + require(dist_spec)[0].run_script(script_name, ns) + + +# backward compatibility +run_main = run_script + + +def get_distribution(dist): + """Return a current distribution object for a Requirement or string""" + if isinstance(dist, str): + dist = Requirement.parse(dist) + if isinstance(dist, Requirement): + dist = get_provider(dist) + if not isinstance(dist, Distribution): + raise TypeError("Expected string, Requirement, or Distribution", dist) + return dist + + +def load_entry_point(dist, group, name): + """Return `name` entry point of `group` for `dist` or raise ImportError""" + return get_distribution(dist).load_entry_point(group, name) + + +def get_entry_map(dist, group=None): + """Return the entry point map for `group`, or the full entry map""" + return get_distribution(dist).get_entry_map(group) + + +def get_entry_info(dist, group, name): + """Return the EntryPoint object for `group`+`name`, or ``None``""" + return get_distribution(dist).get_entry_info(group, name) + + +class IMetadataProvider: + def has_metadata(name): + """Does the package's distribution contain the named metadata?""" + + def get_metadata(name): + """The named metadata resource as a string""" + + def get_metadata_lines(name): + """Yield named metadata resource as list of non-blank non-comment lines + + Leading and trailing whitespace is stripped from each line, and lines + with ``#`` as the first non-blank character are omitted.""" + + def metadata_isdir(name): + """Is the named metadata a directory? (like ``os.path.isdir()``)""" + + def metadata_listdir(name): + """List of metadata names in the directory (like ``os.listdir()``)""" + + def run_script(script_name, namespace): + """Execute the named script in the supplied namespace dictionary""" + + +class IResourceProvider(IMetadataProvider): + """An object that provides access to package resources""" + + def get_resource_filename(manager, resource_name): + """Return a true filesystem path for `resource_name` + + `manager` must be an ``IResourceManager``""" + + def get_resource_stream(manager, resource_name): + """Return a readable file-like object for `resource_name` + + `manager` must be an ``IResourceManager``""" + + def get_resource_string(manager, resource_name): + """Return a string containing the contents of `resource_name` + + `manager` must be an ``IResourceManager``""" + + def has_resource(resource_name): + """Does the package contain the named resource?""" + + def resource_isdir(resource_name): + """Is the named resource a directory? (like ``os.path.isdir()``)""" + + def resource_listdir(resource_name): + """List of resource names in the directory (like ``os.listdir()``)""" + + +class WorkingSet: + """A collection of active distributions on sys.path (or a similar list)""" + + def __init__(self, entries=None): + """Create working set from list of path entries (default=sys.path)""" + self.entries = [] + self.entry_keys = {} + self.by_key = {} + self.normalized_to_canonical_keys = {} + self.callbacks = [] + + if entries is None: + entries = sys.path + + for entry in entries: + self.add_entry(entry) + + @classmethod + def _build_master(cls): + """ + Prepare the master working set. + """ + ws = cls() + try: + from __main__ import __requires__ + except ImportError: + # The main program does not list any requirements + return ws + + # ensure the requirements are met + try: + ws.require(__requires__) + except VersionConflict: + return cls._build_from_requirements(__requires__) + + return ws + + @classmethod + def _build_from_requirements(cls, req_spec): + """ + Build a working set from a requirement spec. Rewrites sys.path. + """ + # try it without defaults already on sys.path + # by starting with an empty path + ws = cls([]) + reqs = parse_requirements(req_spec) + dists = ws.resolve(reqs, Environment()) + for dist in dists: + ws.add(dist) + + # add any missing entries from sys.path + for entry in sys.path: + if entry not in ws.entries: + ws.add_entry(entry) + + # then copy back to sys.path + sys.path[:] = ws.entries + return ws + + def add_entry(self, entry): + """Add a path item to ``.entries``, finding any distributions on it + + ``find_distributions(entry, True)`` is used to find distributions + corresponding to the path entry, and they are added. `entry` is + always appended to ``.entries``, even if it is already present. + (This is because ``sys.path`` can contain the same value more than + once, and the ``.entries`` of the ``sys.path`` WorkingSet should always + equal ``sys.path``.) + """ + self.entry_keys.setdefault(entry, []) + self.entries.append(entry) + for dist in find_distributions(entry, True): + self.add(dist, entry, False) + + def __contains__(self, dist): + """True if `dist` is the active distribution for its project""" + return self.by_key.get(dist.key) == dist + + def find(self, req): + """Find a distribution matching requirement `req` + + If there is an active distribution for the requested project, this + returns it as long as it meets the version requirement specified by + `req`. But, if there is an active distribution for the project and it + does *not* meet the `req` requirement, ``VersionConflict`` is raised. + If there is no active distribution for the requested project, ``None`` + is returned. + """ + dist = self.by_key.get(req.key) + + if dist is None: + canonical_key = self.normalized_to_canonical_keys.get(req.key) + + if canonical_key is not None: + req.key = canonical_key + dist = self.by_key.get(canonical_key) + + if dist is not None and dist not in req: + # XXX add more info + raise VersionConflict(dist, req) + return dist + + def iter_entry_points(self, group, name=None): + """Yield entry point objects from `group` matching `name` + + If `name` is None, yields all entry points in `group` from all + distributions in the working set, otherwise only ones matching + both `group` and `name` are yielded (in distribution order). + """ + return ( + entry + for dist in self + for entry in dist.get_entry_map(group).values() + if name is None or name == entry.name + ) + + def run_script(self, requires, script_name): + """Locate distribution for `requires` and run `script_name` script""" + ns = sys._getframe(1).f_globals + name = ns['__name__'] + ns.clear() + ns['__name__'] = name + self.require(requires)[0].run_script(script_name, ns) + + def __iter__(self): + """Yield distributions for non-duplicate projects in the working set + + The yield order is the order in which the items' path entries were + added to the working set. + """ + seen = {} + for item in self.entries: + if item not in self.entry_keys: + # workaround a cache issue + continue + + for key in self.entry_keys[item]: + if key not in seen: + seen[key] = 1 + yield self.by_key[key] + + def add(self, dist, entry=None, insert=True, replace=False): + """Add `dist` to working set, associated with `entry` + + If `entry` is unspecified, it defaults to the ``.location`` of `dist`. + On exit from this routine, `entry` is added to the end of the working + set's ``.entries`` (if it wasn't already present). + + `dist` is only added to the working set if it's for a project that + doesn't already have a distribution in the set, unless `replace=True`. + If it's added, any callbacks registered with the ``subscribe()`` method + will be called. + """ + if insert: + dist.insert_on(self.entries, entry, replace=replace) + + if entry is None: + entry = dist.location + keys = self.entry_keys.setdefault(entry, []) + keys2 = self.entry_keys.setdefault(dist.location, []) + if not replace and dist.key in self.by_key: + # ignore hidden distros + return + + self.by_key[dist.key] = dist + normalized_name = packaging.utils.canonicalize_name(dist.key) + self.normalized_to_canonical_keys[normalized_name] = dist.key + if dist.key not in keys: + keys.append(dist.key) + if dist.key not in keys2: + keys2.append(dist.key) + self._added_new(dist) + + def resolve( + self, + requirements, + env=None, + installer=None, + replace_conflicting=False, + extras=None, + ): + """List all distributions needed to (recursively) meet `requirements` + + `requirements` must be a sequence of ``Requirement`` objects. `env`, + if supplied, should be an ``Environment`` instance. If + not supplied, it defaults to all distributions available within any + entry or distribution in the working set. `installer`, if supplied, + will be invoked with each requirement that cannot be met by an + already-installed distribution; it should return a ``Distribution`` or + ``None``. + + Unless `replace_conflicting=True`, raises a VersionConflict exception + if + any requirements are found on the path that have the correct name but + the wrong version. Otherwise, if an `installer` is supplied it will be + invoked to obtain the correct version of the requirement and activate + it. + + `extras` is a list of the extras to be used with these requirements. + This is important because extra requirements may look like `my_req; + extra = "my_extra"`, which would otherwise be interpreted as a purely + optional requirement. Instead, we want to be able to assert that these + requirements are truly required. + """ + + # set up the stack + requirements = list(requirements)[::-1] + # set of processed requirements + processed = {} + # key -> dist + best = {} + to_activate = [] + + req_extras = _ReqExtras() + + # Mapping of requirement to set of distributions that required it; + # useful for reporting info about conflicts. + required_by = collections.defaultdict(set) + + while requirements: + # process dependencies breadth-first + req = requirements.pop(0) + if req in processed: + # Ignore cyclic or redundant dependencies + continue + + if not req_extras.markers_pass(req, extras): + continue + + dist = self._resolve_dist( + req, best, replace_conflicting, env, installer, required_by, to_activate + ) + + # push the new requirements onto the stack + new_requirements = dist.requires(req.extras)[::-1] + requirements.extend(new_requirements) + + # Register the new requirements needed by req + for new_requirement in new_requirements: + required_by[new_requirement].add(req.project_name) + req_extras[new_requirement] = req.extras + + processed[req] = True + + # return list of distros to activate + return to_activate + + def _resolve_dist( + self, req, best, replace_conflicting, env, installer, required_by, to_activate + ): + dist = best.get(req.key) + if dist is None: + # Find the best distribution and add it to the map + dist = self.by_key.get(req.key) + if dist is None or (dist not in req and replace_conflicting): + ws = self + if env is None: + if dist is None: + env = Environment(self.entries) + else: + # Use an empty environment and workingset to avoid + # any further conflicts with the conflicting + # distribution + env = Environment([]) + ws = WorkingSet([]) + dist = best[req.key] = env.best_match( + req, ws, installer, replace_conflicting=replace_conflicting + ) + if dist is None: + requirers = required_by.get(req, None) + raise DistributionNotFound(req, requirers) + to_activate.append(dist) + if dist not in req: + # Oops, the "best" so far conflicts with a dependency + dependent_req = required_by[req] + raise VersionConflict(dist, req).with_context(dependent_req) + return dist + + def find_plugins(self, plugin_env, full_env=None, installer=None, fallback=True): + """Find all activatable distributions in `plugin_env` + + Example usage:: + + distributions, errors = working_set.find_plugins( + Environment(plugin_dirlist) + ) + # add plugins+libs to sys.path + map(working_set.add, distributions) + # display errors + print('Could not load', errors) + + The `plugin_env` should be an ``Environment`` instance that contains + only distributions that are in the project's "plugin directory" or + directories. The `full_env`, if supplied, should be an ``Environment`` + contains all currently-available distributions. If `full_env` is not + supplied, one is created automatically from the ``WorkingSet`` this + method is called on, which will typically mean that every directory on + ``sys.path`` will be scanned for distributions. + + `installer` is a standard installer callback as used by the + ``resolve()`` method. The `fallback` flag indicates whether we should + attempt to resolve older versions of a plugin if the newest version + cannot be resolved. + + This method returns a 2-tuple: (`distributions`, `error_info`), where + `distributions` is a list of the distributions found in `plugin_env` + that were loadable, along with any other distributions that are needed + to resolve their dependencies. `error_info` is a dictionary mapping + unloadable plugin distributions to an exception instance describing the + error that occurred. Usually this will be a ``DistributionNotFound`` or + ``VersionConflict`` instance. + """ + + plugin_projects = list(plugin_env) + # scan project names in alphabetic order + plugin_projects.sort() + + error_info = {} + distributions = {} + + if full_env is None: + env = Environment(self.entries) + env += plugin_env + else: + env = full_env + plugin_env + + shadow_set = self.__class__([]) + # put all our entries in shadow_set + list(map(shadow_set.add, self)) + + for project_name in plugin_projects: + for dist in plugin_env[project_name]: + req = [dist.as_requirement()] + + try: + resolvees = shadow_set.resolve(req, env, installer) + + except ResolutionError as v: + # save error info + error_info[dist] = v + if fallback: + # try the next older version of project + continue + else: + # give up on this project, keep going + break + + else: + list(map(shadow_set.add, resolvees)) + distributions.update(dict.fromkeys(resolvees)) + + # success, no need to try any more versions of this project + break + + distributions = list(distributions) + distributions.sort() + + return distributions, error_info + + def require(self, *requirements): + """Ensure that distributions matching `requirements` are activated + + `requirements` must be a string or a (possibly-nested) sequence + thereof, specifying the distributions and versions required. The + return value is a sequence of the distributions that needed to be + activated to fulfill the requirements; all relevant distributions are + included, even if they were already activated in this working set. + """ + needed = self.resolve(parse_requirements(requirements)) + + for dist in needed: + self.add(dist) + + return needed + + def subscribe(self, callback, existing=True): + """Invoke `callback` for all distributions + + If `existing=True` (default), + call on all existing ones, as well. + """ + if callback in self.callbacks: + return + self.callbacks.append(callback) + if not existing: + return + for dist in self: + callback(dist) + + def _added_new(self, dist): + for callback in self.callbacks: + callback(dist) + + def __getstate__(self): + return ( + self.entries[:], + self.entry_keys.copy(), + self.by_key.copy(), + self.normalized_to_canonical_keys.copy(), + self.callbacks[:], + ) + + def __setstate__(self, e_k_b_n_c): + entries, keys, by_key, normalized_to_canonical_keys, callbacks = e_k_b_n_c + self.entries = entries[:] + self.entry_keys = keys.copy() + self.by_key = by_key.copy() + self.normalized_to_canonical_keys = normalized_to_canonical_keys.copy() + self.callbacks = callbacks[:] + + +class _ReqExtras(dict): + """ + Map each requirement to the extras that demanded it. + """ + + def markers_pass(self, req, extras=None): + """ + Evaluate markers for req against each extra that + demanded it. + + Return False if the req has a marker and fails + evaluation. Otherwise, return True. + """ + extra_evals = ( + req.marker.evaluate({'extra': extra}) + for extra in self.get(req, ()) + (extras or (None,)) + ) + return not req.marker or any(extra_evals) + + +class Environment: + """Searchable snapshot of distributions on a search path""" + + def __init__( + self, search_path=None, platform=get_supported_platform(), python=PY_MAJOR + ): + """Snapshot distributions available on a search path + + Any distributions found on `search_path` are added to the environment. + `search_path` should be a sequence of ``sys.path`` items. If not + supplied, ``sys.path`` is used. + + `platform` is an optional string specifying the name of the platform + that platform-specific distributions must be compatible with. If + unspecified, it defaults to the current platform. `python` is an + optional string naming the desired version of Python (e.g. ``'3.6'``); + it defaults to the current version. + + You may explicitly set `platform` (and/or `python`) to ``None`` if you + wish to map *all* distributions, not just those compatible with the + running platform or Python version. + """ + self._distmap = {} + self.platform = platform + self.python = python + self.scan(search_path) + + def can_add(self, dist): + """Is distribution `dist` acceptable for this environment? + + The distribution must match the platform and python version + requirements specified when this environment was created, or False + is returned. + """ + py_compat = ( + self.python is None + or dist.py_version is None + or dist.py_version == self.python + ) + return py_compat and compatible_platforms(dist.platform, self.platform) + + def remove(self, dist): + """Remove `dist` from the environment""" + self._distmap[dist.key].remove(dist) + + def scan(self, search_path=None): + """Scan `search_path` for distributions usable in this environment + + Any distributions found are added to the environment. + `search_path` should be a sequence of ``sys.path`` items. If not + supplied, ``sys.path`` is used. Only distributions conforming to + the platform/python version defined at initialization are added. + """ + if search_path is None: + search_path = sys.path + + for item in search_path: + for dist in find_distributions(item): + self.add(dist) + + def __getitem__(self, project_name): + """Return a newest-to-oldest list of distributions for `project_name` + + Uses case-insensitive `project_name` comparison, assuming all the + project's distributions use their project's name converted to all + lowercase as their key. + + """ + distribution_key = project_name.lower() + return self._distmap.get(distribution_key, []) + + def add(self, dist): + """Add `dist` if we ``can_add()`` it and it has not already been added""" + if self.can_add(dist) and dist.has_version(): + dists = self._distmap.setdefault(dist.key, []) + if dist not in dists: + dists.append(dist) + dists.sort(key=operator.attrgetter('hashcmp'), reverse=True) + + def best_match(self, req, working_set, installer=None, replace_conflicting=False): + """Find distribution best matching `req` and usable on `working_set` + + This calls the ``find(req)`` method of the `working_set` to see if a + suitable distribution is already active. (This may raise + ``VersionConflict`` if an unsuitable version of the project is already + active in the specified `working_set`.) If a suitable distribution + isn't active, this method returns the newest distribution in the + environment that meets the ``Requirement`` in `req`. If no suitable + distribution is found, and `installer` is supplied, then the result of + calling the environment's ``obtain(req, installer)`` method will be + returned. + """ + try: + dist = working_set.find(req) + except VersionConflict: + if not replace_conflicting: + raise + dist = None + if dist is not None: + return dist + for dist in self[req.key]: + if dist in req: + return dist + # try to download/install + return self.obtain(req, installer) + + def obtain(self, requirement, installer=None): + """Obtain a distribution matching `requirement` (e.g. via download) + + Obtain a distro that matches requirement (e.g. via download). In the + base ``Environment`` class, this routine just returns + ``installer(requirement)``, unless `installer` is None, in which case + None is returned instead. This method is a hook that allows subclasses + to attempt other ways of obtaining a distribution before falling back + to the `installer` argument.""" + if installer is not None: + return installer(requirement) + + def __iter__(self): + """Yield the unique project names of the available distributions""" + for key in self._distmap.keys(): + if self[key]: + yield key + + def __iadd__(self, other): + """In-place addition of a distribution or environment""" + if isinstance(other, Distribution): + self.add(other) + elif isinstance(other, Environment): + for project in other: + for dist in other[project]: + self.add(dist) + else: + raise TypeError("Can't add %r to environment" % (other,)) + return self + + def __add__(self, other): + """Add an environment or distribution to an environment""" + new = self.__class__([], platform=None, python=None) + for env in self, other: + new += env + return new + + +# XXX backward compatibility +AvailableDistributions = Environment + + +class ExtractionError(RuntimeError): + """An error occurred extracting a resource + + The following attributes are available from instances of this exception: + + manager + The resource manager that raised this exception + + cache_path + The base directory for resource extraction + + original_error + The exception instance that caused extraction to fail + """ + + +class ResourceManager: + """Manage resource extraction and packages""" + + extraction_path = None + + def __init__(self): + self.cached_files = {} + + def resource_exists(self, package_or_requirement, resource_name): + """Does the named resource exist?""" + return get_provider(package_or_requirement).has_resource(resource_name) + + def resource_isdir(self, package_or_requirement, resource_name): + """Is the named resource an existing directory?""" + return get_provider(package_or_requirement).resource_isdir(resource_name) + + def resource_filename(self, package_or_requirement, resource_name): + """Return a true filesystem path for specified resource""" + return get_provider(package_or_requirement).get_resource_filename( + self, resource_name + ) + + def resource_stream(self, package_or_requirement, resource_name): + """Return a readable file-like object for specified resource""" + return get_provider(package_or_requirement).get_resource_stream( + self, resource_name + ) + + def resource_string(self, package_or_requirement, resource_name): + """Return specified resource as a string""" + return get_provider(package_or_requirement).get_resource_string( + self, resource_name + ) + + def resource_listdir(self, package_or_requirement, resource_name): + """List the contents of the named resource directory""" + return get_provider(package_or_requirement).resource_listdir(resource_name) + + def extraction_error(self): + """Give an error message for problems extracting file(s)""" + + old_exc = sys.exc_info()[1] + cache_path = self.extraction_path or get_default_cache() + + tmpl = textwrap.dedent( + """ + Can't extract file(s) to egg cache + + The following error occurred while trying to extract file(s) + to the Python egg cache: + + {old_exc} + + The Python egg cache directory is currently set to: + + {cache_path} + + Perhaps your account does not have write access to this directory? + You can change the cache directory by setting the PYTHON_EGG_CACHE + environment variable to point to an accessible directory. + """ + ).lstrip() + err = ExtractionError(tmpl.format(**locals())) + err.manager = self + err.cache_path = cache_path + err.original_error = old_exc + raise err + + def get_cache_path(self, archive_name, names=()): + """Return absolute location in cache for `archive_name` and `names` + + The parent directory of the resulting path will be created if it does + not already exist. `archive_name` should be the base filename of the + enclosing egg (which may not be the name of the enclosing zipfile!), + including its ".egg" extension. `names`, if provided, should be a + sequence of path name parts "under" the egg's extraction location. + + This method should only be called by resource providers that need to + obtain an extraction location, and only for names they intend to + extract, as it tracks the generated names for possible cleanup later. + """ + extract_path = self.extraction_path or get_default_cache() + target_path = os.path.join(extract_path, archive_name + '-tmp', *names) + try: + _bypass_ensure_directory(target_path) + except Exception: + self.extraction_error() + + self._warn_unsafe_extraction_path(extract_path) + + self.cached_files[target_path] = 1 + return target_path + + @staticmethod + def _warn_unsafe_extraction_path(path): + """ + If the default extraction path is overridden and set to an insecure + location, such as /tmp, it opens up an opportunity for an attacker to + replace an extracted file with an unauthorized payload. Warn the user + if a known insecure location is used. + + See Distribute #375 for more details. + """ + if os.name == 'nt' and not path.startswith(os.environ['windir']): + # On Windows, permissions are generally restrictive by default + # and temp directories are not writable by other users, so + # bypass the warning. + return + mode = os.stat(path).st_mode + if mode & stat.S_IWOTH or mode & stat.S_IWGRP: + msg = ( + "Extraction path is writable by group/others " + "and vulnerable to attack when " + "used with get_resource_filename ({path}). " + "Consider a more secure " + "location (set with .set_extraction_path or the " + "PYTHON_EGG_CACHE environment variable)." + ).format(**locals()) + warnings.warn(msg, UserWarning) + + def postprocess(self, tempname, filename): + """Perform any platform-specific postprocessing of `tempname` + + This is where Mac header rewrites should be done; other platforms don't + have anything special they should do. + + Resource providers should call this method ONLY after successfully + extracting a compressed resource. They must NOT call it on resources + that are already in the filesystem. + + `tempname` is the current (temporary) name of the file, and `filename` + is the name it will be renamed to by the caller after this routine + returns. + """ + + if os.name == 'posix': + # Make the resource executable + mode = ((os.stat(tempname).st_mode) | 0o555) & 0o7777 + os.chmod(tempname, mode) + + def set_extraction_path(self, path): + """Set the base path where resources will be extracted to, if needed. + + If you do not call this routine before any extractions take place, the + path defaults to the return value of ``get_default_cache()``. (Which + is based on the ``PYTHON_EGG_CACHE`` environment variable, with various + platform-specific fallbacks. See that routine's documentation for more + details.) + + Resources are extracted to subdirectories of this path based upon + information given by the ``IResourceProvider``. You may set this to a + temporary directory, but then you must call ``cleanup_resources()`` to + delete the extracted files when done. There is no guarantee that + ``cleanup_resources()`` will be able to remove all extracted files. + + (Note: you may not change the extraction path for a given resource + manager once resources have been extracted, unless you first call + ``cleanup_resources()``.) + """ + if self.cached_files: + raise ValueError("Can't change extraction path, files already extracted") + + self.extraction_path = path + + def cleanup_resources(self, force=False): + """ + Delete all extracted resource files and directories, returning a list + of the file and directory names that could not be successfully removed. + This function does not have any concurrency protection, so it should + generally only be called when the extraction path is a temporary + directory exclusive to a single process. This method is not + automatically called; you must call it explicitly or register it as an + ``atexit`` function if you wish to ensure cleanup of a temporary + directory used for extractions. + """ + # XXX + + +def get_default_cache(): + """ + Return the ``PYTHON_EGG_CACHE`` environment variable + or a platform-relevant user cache dir for an app + named "Python-Eggs". + """ + return os.environ.get('PYTHON_EGG_CACHE') or platformdirs.user_cache_dir( + appname='Python-Eggs' + ) + + +def safe_name(name): + """Convert an arbitrary string to a standard distribution name + + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + """ + return re.sub('[^A-Za-z0-9.]+', '-', name) + + +def safe_version(version): + """ + Convert an arbitrary string to a standard version string + """ + try: + # normalize the version + return str(packaging.version.Version(version)) + except packaging.version.InvalidVersion: + version = version.replace(' ', '.') + return re.sub('[^A-Za-z0-9.]+', '-', version) + + +def _forgiving_version(version): + """Fallback when ``safe_version`` is not safe enough + >>> parse_version(_forgiving_version('0.23ubuntu1')) + <Version('0.23.dev0+sanitized.ubuntu1')> + >>> parse_version(_forgiving_version('0.23-')) + <Version('0.23.dev0+sanitized')> + >>> parse_version(_forgiving_version('0.-_')) + <Version('0.dev0+sanitized')> + >>> parse_version(_forgiving_version('42.+?1')) + <Version('42.dev0+sanitized.1')> + >>> parse_version(_forgiving_version('hello world')) + <Version('0.dev0+sanitized.hello.world')> + """ + version = version.replace(' ', '.') + match = _PEP440_FALLBACK.search(version) + if match: + safe = match["safe"] + rest = version[len(safe):] + else: + safe = "0" + rest = version + local = f"sanitized.{_safe_segment(rest)}".strip(".") + return f"{safe}.dev0+{local}" + + +def _safe_segment(segment): + """Convert an arbitrary string into a safe segment""" + segment = re.sub('[^A-Za-z0-9.]+', '-', segment) + segment = re.sub('-[^A-Za-z0-9]+', '-', segment) + return re.sub(r'\.[^A-Za-z0-9]+', '.', segment).strip(".-") + + +def safe_extra(extra): + """Convert an arbitrary string to a standard 'extra' name + + Any runs of non-alphanumeric characters are replaced with a single '_', + and the result is always lowercased. + """ + return re.sub('[^A-Za-z0-9.-]+', '_', extra).lower() + + +def to_filename(name): + """Convert a project or version name to its filename-escaped form + + Any '-' characters are currently replaced with '_'. + """ + return name.replace('-', '_') + + +def invalid_marker(text): + """ + Validate text as a PEP 508 environment marker; return an exception + if invalid or False otherwise. + """ + try: + evaluate_marker(text) + except SyntaxError as e: + e.filename = None + e.lineno = None + return e + return False + + +def evaluate_marker(text, extra=None): + """ + Evaluate a PEP 508 environment marker. + Return a boolean indicating the marker result in this environment. + Raise SyntaxError if marker is invalid. + + This implementation uses the 'pyparsing' module. + """ + try: + marker = packaging.markers.Marker(text) + return marker.evaluate() + except packaging.markers.InvalidMarker as e: + raise SyntaxError(e) from e + + +class NullProvider: + """Try to implement resources and metadata for arbitrary PEP 302 loaders""" + + egg_name = None + egg_info = None + loader = None + + def __init__(self, module): + self.loader = getattr(module, '__loader__', None) + self.module_path = os.path.dirname(getattr(module, '__file__', '')) + + def get_resource_filename(self, manager, resource_name): + return self._fn(self.module_path, resource_name) + + def get_resource_stream(self, manager, resource_name): + return io.BytesIO(self.get_resource_string(manager, resource_name)) + + def get_resource_string(self, manager, resource_name): + return self._get(self._fn(self.module_path, resource_name)) + + def has_resource(self, resource_name): + return self._has(self._fn(self.module_path, resource_name)) + + def _get_metadata_path(self, name): + return self._fn(self.egg_info, name) + + def has_metadata(self, name): + if not self.egg_info: + return self.egg_info + + path = self._get_metadata_path(name) + return self._has(path) + + def get_metadata(self, name): + if not self.egg_info: + return "" + path = self._get_metadata_path(name) + value = self._get(path) + try: + return value.decode('utf-8') + except UnicodeDecodeError as exc: + # Include the path in the error message to simplify + # troubleshooting, and without changing the exception type. + exc.reason += ' in {} file at path: {}'.format(name, path) + raise + + def get_metadata_lines(self, name): + return yield_lines(self.get_metadata(name)) + + def resource_isdir(self, resource_name): + return self._isdir(self._fn(self.module_path, resource_name)) + + def metadata_isdir(self, name): + return self.egg_info and self._isdir(self._fn(self.egg_info, name)) + + def resource_listdir(self, resource_name): + return self._listdir(self._fn(self.module_path, resource_name)) + + def metadata_listdir(self, name): + if self.egg_info: + return self._listdir(self._fn(self.egg_info, name)) + return [] + + def run_script(self, script_name, namespace): + script = 'scripts/' + script_name + if not self.has_metadata(script): + raise ResolutionError( + "Script {script!r} not found in metadata at {self.egg_info!r}".format( + **locals() + ), + ) + script_text = self.get_metadata(script).replace('\r\n', '\n') + script_text = script_text.replace('\r', '\n') + script_filename = self._fn(self.egg_info, script) + namespace['__file__'] = script_filename + if os.path.exists(script_filename): + with open(script_filename) as fid: + source = fid.read() + code = compile(source, script_filename, 'exec') + exec(code, namespace, namespace) + else: + from linecache import cache + + cache[script_filename] = ( + len(script_text), + 0, + script_text.split('\n'), + script_filename, + ) + script_code = compile(script_text, script_filename, 'exec') + exec(script_code, namespace, namespace) + + def _has(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _isdir(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _listdir(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _fn(self, base, resource_name): + self._validate_resource_path(resource_name) + if resource_name: + return os.path.join(base, *resource_name.split('/')) + return base + + @staticmethod + def _validate_resource_path(path): + """ + Validate the resource paths according to the docs. + https://setuptools.pypa.io/en/latest/pkg_resources.html#basic-resource-access + + >>> warned = getfixture('recwarn') + >>> warnings.simplefilter('always') + >>> vrp = NullProvider._validate_resource_path + >>> vrp('foo/bar.txt') + >>> bool(warned) + False + >>> vrp('../foo/bar.txt') + >>> bool(warned) + True + >>> warned.clear() + >>> vrp('/foo/bar.txt') + >>> bool(warned) + True + >>> vrp('foo/../../bar.txt') + >>> bool(warned) + True + >>> warned.clear() + >>> vrp('foo/f../bar.txt') + >>> bool(warned) + False + + Windows path separators are straight-up disallowed. + >>> vrp(r'\\foo/bar.txt') + Traceback (most recent call last): + ... + ValueError: Use of .. or absolute path in a resource path \ +is not allowed. + + >>> vrp(r'C:\\foo/bar.txt') + Traceback (most recent call last): + ... + ValueError: Use of .. or absolute path in a resource path \ +is not allowed. + + Blank values are allowed + + >>> vrp('') + >>> bool(warned) + False + + Non-string values are not. + + >>> vrp(None) + Traceback (most recent call last): + ... + AttributeError: ... + """ + invalid = ( + os.path.pardir in path.split(posixpath.sep) + or posixpath.isabs(path) + or ntpath.isabs(path) + ) + if not invalid: + return + + msg = "Use of .. or absolute path in a resource path is not allowed." + + # Aggressively disallow Windows absolute paths + if ntpath.isabs(path) and not posixpath.isabs(path): + raise ValueError(msg) + + # for compatibility, warn; in future + # raise ValueError(msg) + issue_warning( + msg[:-1] + " and will raise exceptions in a future release.", + DeprecationWarning, + ) + + def _get(self, path): + if hasattr(self.loader, 'get_data'): + return self.loader.get_data(path) + raise NotImplementedError( + "Can't perform this operation for loaders without 'get_data()'" + ) + + +register_loader_type(object, NullProvider) + + +def _parents(path): + """ + yield all parents of path including path + """ + last = None + while path != last: + yield path + last = path + path, _ = os.path.split(path) + + +class EggProvider(NullProvider): + """Provider based on a virtual filesystem""" + + def __init__(self, module): + super().__init__(module) + self._setup_prefix() + + def _setup_prefix(self): + # Assume that metadata may be nested inside a "basket" + # of multiple eggs and use module_path instead of .archive. + eggs = filter(_is_egg_path, _parents(self.module_path)) + egg = next(eggs, None) + egg and self._set_egg(egg) + + def _set_egg(self, path): + self.egg_name = os.path.basename(path) + self.egg_info = os.path.join(path, 'EGG-INFO') + self.egg_root = path + + +class DefaultProvider(EggProvider): + """Provides access to package resources in the filesystem""" + + def _has(self, path): + return os.path.exists(path) + + def _isdir(self, path): + return os.path.isdir(path) + + def _listdir(self, path): + return os.listdir(path) + + def get_resource_stream(self, manager, resource_name): + return open(self._fn(self.module_path, resource_name), 'rb') + + def _get(self, path): + with open(path, 'rb') as stream: + return stream.read() + + @classmethod + def _register(cls): + loader_names = ( + 'SourceFileLoader', + 'SourcelessFileLoader', + ) + for name in loader_names: + loader_cls = getattr(importlib_machinery, name, type(None)) + register_loader_type(loader_cls, cls) + + +DefaultProvider._register() + + +class EmptyProvider(NullProvider): + """Provider that returns nothing for all requests""" + + module_path = None + + _isdir = _has = lambda self, path: False + + def _get(self, path): + return '' + + def _listdir(self, path): + return [] + + def __init__(self): + pass + + +empty_provider = EmptyProvider() + + +class ZipManifests(dict): + """ + zip manifest builder + """ + + @classmethod + def build(cls, path): + """ + Build a dictionary similar to the zipimport directory + caches, except instead of tuples, store ZipInfo objects. + + Use a platform-specific path separator (os.sep) for the path keys + for compatibility with pypy on Windows. + """ + with zipfile.ZipFile(path) as zfile: + items = ( + ( + name.replace('/', os.sep), + zfile.getinfo(name), + ) + for name in zfile.namelist() + ) + return dict(items) + + load = build + + +class MemoizedZipManifests(ZipManifests): + """ + Memoized zipfile manifests. + """ + + manifest_mod = collections.namedtuple('manifest_mod', 'manifest mtime') + + def load(self, path): + """ + Load a manifest at path or return a suitable manifest already loaded. + """ + path = os.path.normpath(path) + mtime = os.stat(path).st_mtime + + if path not in self or self[path].mtime != mtime: + manifest = self.build(path) + self[path] = self.manifest_mod(manifest, mtime) + + return self[path].manifest + + +class ZipProvider(EggProvider): + """Resource support for zips and eggs""" + + eagers = None + _zip_manifests = MemoizedZipManifests() + + def __init__(self, module): + super().__init__(module) + self.zip_pre = self.loader.archive + os.sep + + def _zipinfo_name(self, fspath): + # Convert a virtual filename (full path to file) into a zipfile subpath + # usable with the zipimport directory cache for our target archive + fspath = fspath.rstrip(os.sep) + if fspath == self.loader.archive: + return '' + if fspath.startswith(self.zip_pre): + return fspath[len(self.zip_pre) :] + raise AssertionError("%s is not a subpath of %s" % (fspath, self.zip_pre)) + + def _parts(self, zip_path): + # Convert a zipfile subpath into an egg-relative path part list. + # pseudo-fs path + fspath = self.zip_pre + zip_path + if fspath.startswith(self.egg_root + os.sep): + return fspath[len(self.egg_root) + 1 :].split(os.sep) + raise AssertionError("%s is not a subpath of %s" % (fspath, self.egg_root)) + + @property + def zipinfo(self): + return self._zip_manifests.load(self.loader.archive) + + def get_resource_filename(self, manager, resource_name): + if not self.egg_name: + raise NotImplementedError( + "resource_filename() only supported for .egg, not .zip" + ) + # no need to lock for extraction, since we use temp names + zip_path = self._resource_to_zip(resource_name) + eagers = self._get_eager_resources() + if '/'.join(self._parts(zip_path)) in eagers: + for name in eagers: + self._extract_resource(manager, self._eager_to_zip(name)) + return self._extract_resource(manager, zip_path) + + @staticmethod + def _get_date_and_size(zip_stat): + size = zip_stat.file_size + # ymdhms+wday, yday, dst + date_time = zip_stat.date_time + (0, 0, -1) + # 1980 offset already done + timestamp = time.mktime(date_time) + return timestamp, size + + # FIXME: 'ZipProvider._extract_resource' is too complex (12) + def _extract_resource(self, manager, zip_path): # noqa: C901 + if zip_path in self._index(): + for name in self._index()[zip_path]: + last = self._extract_resource(manager, os.path.join(zip_path, name)) + # return the extracted directory name + return os.path.dirname(last) + + timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) + + if not WRITE_SUPPORT: + raise IOError( + '"os.rename" and "os.unlink" are not supported ' 'on this platform' + ) + try: + real_path = manager.get_cache_path(self.egg_name, self._parts(zip_path)) + + if self._is_current(real_path, zip_path): + return real_path + + outf, tmpnam = _mkstemp( + ".$extract", + dir=os.path.dirname(real_path), + ) + os.write(outf, self.loader.get_data(zip_path)) + os.close(outf) + utime(tmpnam, (timestamp, timestamp)) + manager.postprocess(tmpnam, real_path) + + try: + rename(tmpnam, real_path) + + except os.error: + if os.path.isfile(real_path): + if self._is_current(real_path, zip_path): + # the file became current since it was checked above, + # so proceed. + return real_path + # Windows, del old file and retry + elif os.name == 'nt': + unlink(real_path) + rename(tmpnam, real_path) + return real_path + raise + + except os.error: + # report a user-friendly error + manager.extraction_error() + + return real_path + + def _is_current(self, file_path, zip_path): + """ + Return True if the file_path is current for this zip_path + """ + timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) + if not os.path.isfile(file_path): + return False + stat = os.stat(file_path) + if stat.st_size != size or stat.st_mtime != timestamp: + return False + # check that the contents match + zip_contents = self.loader.get_data(zip_path) + with open(file_path, 'rb') as f: + file_contents = f.read() + return zip_contents == file_contents + + def _get_eager_resources(self): + if self.eagers is None: + eagers = [] + for name in ('native_libs.txt', 'eager_resources.txt'): + if self.has_metadata(name): + eagers.extend(self.get_metadata_lines(name)) + self.eagers = eagers + return self.eagers + + def _index(self): + try: + return self._dirindex + except AttributeError: + ind = {} + for path in self.zipinfo: + parts = path.split(os.sep) + while parts: + parent = os.sep.join(parts[:-1]) + if parent in ind: + ind[parent].append(parts[-1]) + break + else: + ind[parent] = [parts.pop()] + self._dirindex = ind + return ind + + def _has(self, fspath): + zip_path = self._zipinfo_name(fspath) + return zip_path in self.zipinfo or zip_path in self._index() + + def _isdir(self, fspath): + return self._zipinfo_name(fspath) in self._index() + + def _listdir(self, fspath): + return list(self._index().get(self._zipinfo_name(fspath), ())) + + def _eager_to_zip(self, resource_name): + return self._zipinfo_name(self._fn(self.egg_root, resource_name)) + + def _resource_to_zip(self, resource_name): + return self._zipinfo_name(self._fn(self.module_path, resource_name)) + + +register_loader_type(zipimport.zipimporter, ZipProvider) + + +class FileMetadata(EmptyProvider): + """Metadata handler for standalone PKG-INFO files + + Usage:: + + metadata = FileMetadata("/path/to/PKG-INFO") + + This provider rejects all data and metadata requests except for PKG-INFO, + which is treated as existing, and will be the contents of the file at + the provided location. + """ + + def __init__(self, path): + self.path = path + + def _get_metadata_path(self, name): + return self.path + + def has_metadata(self, name): + return name == 'PKG-INFO' and os.path.isfile(self.path) + + def get_metadata(self, name): + if name != 'PKG-INFO': + raise KeyError("No metadata except PKG-INFO is available") + + with io.open(self.path, encoding='utf-8', errors="replace") as f: + metadata = f.read() + self._warn_on_replacement(metadata) + return metadata + + def _warn_on_replacement(self, metadata): + replacement_char = '�' + if replacement_char in metadata: + tmpl = "{self.path} could not be properly decoded in UTF-8" + msg = tmpl.format(**locals()) + warnings.warn(msg) + + def get_metadata_lines(self, name): + return yield_lines(self.get_metadata(name)) + + +class PathMetadata(DefaultProvider): + """Metadata provider for egg directories + + Usage:: + + # Development eggs: + + egg_info = "/path/to/PackageName.egg-info" + base_dir = os.path.dirname(egg_info) + metadata = PathMetadata(base_dir, egg_info) + dist_name = os.path.splitext(os.path.basename(egg_info))[0] + dist = Distribution(basedir, project_name=dist_name, metadata=metadata) + + # Unpacked egg directories: + + egg_path = "/path/to/PackageName-ver-pyver-etc.egg" + metadata = PathMetadata(egg_path, os.path.join(egg_path,'EGG-INFO')) + dist = Distribution.from_filename(egg_path, metadata=metadata) + """ + + def __init__(self, path, egg_info): + self.module_path = path + self.egg_info = egg_info + + +class EggMetadata(ZipProvider): + """Metadata provider for .egg files""" + + def __init__(self, importer): + """Create a metadata provider from a zipimporter""" + + self.zip_pre = importer.archive + os.sep + self.loader = importer + if importer.prefix: + self.module_path = os.path.join(importer.archive, importer.prefix) + else: + self.module_path = importer.archive + self._setup_prefix() + + +_declare_state('dict', _distribution_finders={}) + + +def register_finder(importer_type, distribution_finder): + """Register `distribution_finder` to find distributions in sys.path items + + `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item + handler), and `distribution_finder` is a callable that, passed a path + item and the importer instance, yields ``Distribution`` instances found on + that path item. See ``pkg_resources.find_on_path`` for an example.""" + _distribution_finders[importer_type] = distribution_finder + + +def find_distributions(path_item, only=False): + """Yield distributions accessible via `path_item`""" + importer = get_importer(path_item) + finder = _find_adapter(_distribution_finders, importer) + return finder(importer, path_item, only) + + +def find_eggs_in_zip(importer, path_item, only=False): + """ + Find eggs in zip files; possibly multiple nested eggs. + """ + if importer.archive.endswith('.whl'): + # wheels are not supported with this finder + # they don't have PKG-INFO metadata, and won't ever contain eggs + return + metadata = EggMetadata(importer) + if metadata.has_metadata('PKG-INFO'): + yield Distribution.from_filename(path_item, metadata=metadata) + if only: + # don't yield nested distros + return + for subitem in metadata.resource_listdir(''): + if _is_egg_path(subitem): + subpath = os.path.join(path_item, subitem) + dists = find_eggs_in_zip(zipimport.zipimporter(subpath), subpath) + for dist in dists: + yield dist + elif subitem.lower().endswith(('.dist-info', '.egg-info')): + subpath = os.path.join(path_item, subitem) + submeta = EggMetadata(zipimport.zipimporter(subpath)) + submeta.egg_info = subpath + yield Distribution.from_location(path_item, subitem, submeta) + + +register_finder(zipimport.zipimporter, find_eggs_in_zip) + + +def find_nothing(importer, path_item, only=False): + return () + + +register_finder(object, find_nothing) + + +def find_on_path(importer, path_item, only=False): + """Yield distributions accessible on a sys.path directory""" + path_item = _normalize_cached(path_item) + + if _is_unpacked_egg(path_item): + yield Distribution.from_filename( + path_item, + metadata=PathMetadata(path_item, os.path.join(path_item, 'EGG-INFO')), + ) + return + + entries = (os.path.join(path_item, child) for child in safe_listdir(path_item)) + + # scan for .egg and .egg-info in directory + for entry in sorted(entries): + fullpath = os.path.join(path_item, entry) + factory = dist_factory(path_item, entry, only) + for dist in factory(fullpath): + yield dist + + +def dist_factory(path_item, entry, only): + """Return a dist_factory for the given entry.""" + lower = entry.lower() + is_egg_info = lower.endswith('.egg-info') + is_dist_info = lower.endswith('.dist-info') and os.path.isdir( + os.path.join(path_item, entry) + ) + is_meta = is_egg_info or is_dist_info + return ( + distributions_from_metadata + if is_meta + else find_distributions + if not only and _is_egg_path(entry) + else resolve_egg_link + if not only and lower.endswith('.egg-link') + else NoDists() + ) + + +class NoDists: + """ + >>> bool(NoDists()) + False + + >>> list(NoDists()('anything')) + [] + """ + + def __bool__(self): + return False + + def __call__(self, fullpath): + return iter(()) + + +def safe_listdir(path): + """ + Attempt to list contents of path, but suppress some exceptions. + """ + try: + return os.listdir(path) + except (PermissionError, NotADirectoryError): + pass + except OSError as e: + # Ignore the directory if does not exist, not a directory or + # permission denied + if e.errno not in (errno.ENOTDIR, errno.EACCES, errno.ENOENT): + raise + return () + + +def distributions_from_metadata(path): + root = os.path.dirname(path) + if os.path.isdir(path): + if len(os.listdir(path)) == 0: + # empty metadata dir; skip + return + metadata = PathMetadata(root, path) + else: + metadata = FileMetadata(path) + entry = os.path.basename(path) + yield Distribution.from_location( + root, + entry, + metadata, + precedence=DEVELOP_DIST, + ) + + +def non_empty_lines(path): + """ + Yield non-empty lines from file at path + """ + with open(path) as f: + for line in f: + line = line.strip() + if line: + yield line + + +def resolve_egg_link(path): + """ + Given a path to an .egg-link, resolve distributions + present in the referenced path. + """ + referenced_paths = non_empty_lines(path) + resolved_paths = ( + os.path.join(os.path.dirname(path), ref) for ref in referenced_paths + ) + dist_groups = map(find_distributions, resolved_paths) + return next(dist_groups, ()) + + +if hasattr(pkgutil, 'ImpImporter'): + register_finder(pkgutil.ImpImporter, find_on_path) + +register_finder(importlib_machinery.FileFinder, find_on_path) + +_declare_state('dict', _namespace_handlers={}) +_declare_state('dict', _namespace_packages={}) + + +def register_namespace_handler(importer_type, namespace_handler): + """Register `namespace_handler` to declare namespace packages + + `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item + handler), and `namespace_handler` is a callable like this:: + + def namespace_handler(importer, path_entry, moduleName, module): + # return a path_entry to use for child packages + + Namespace handlers are only called if the importer object has already + agreed that it can handle the relevant path item, and they should only + return a subpath if the module __path__ does not already contain an + equivalent subpath. For an example namespace handler, see + ``pkg_resources.file_ns_handler``. + """ + _namespace_handlers[importer_type] = namespace_handler + + +def _handle_ns(packageName, path_item): + """Ensure that named package includes a subpath of path_item (if needed)""" + + importer = get_importer(path_item) + if importer is None: + return None + + # use find_spec (PEP 451) and fall-back to find_module (PEP 302) + try: + spec = importer.find_spec(packageName) + except AttributeError: + # capture warnings due to #1111 + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + loader = importer.find_module(packageName) + else: + loader = spec.loader if spec else None + + if loader is None: + return None + module = sys.modules.get(packageName) + if module is None: + module = sys.modules[packageName] = types.ModuleType(packageName) + module.__path__ = [] + _set_parent_ns(packageName) + elif not hasattr(module, '__path__'): + raise TypeError("Not a package:", packageName) + handler = _find_adapter(_namespace_handlers, importer) + subpath = handler(importer, path_item, packageName, module) + if subpath is not None: + path = module.__path__ + path.append(subpath) + importlib.import_module(packageName) + _rebuild_mod_path(path, packageName, module) + return subpath + + +def _rebuild_mod_path(orig_path, package_name, module): + """ + Rebuild module.__path__ ensuring that all entries are ordered + corresponding to their sys.path order + """ + sys_path = [_normalize_cached(p) for p in sys.path] + + def safe_sys_path_index(entry): + """ + Workaround for #520 and #513. + """ + try: + return sys_path.index(entry) + except ValueError: + return float('inf') + + def position_in_sys_path(path): + """ + Return the ordinal of the path based on its position in sys.path + """ + path_parts = path.split(os.sep) + module_parts = package_name.count('.') + 1 + parts = path_parts[:-module_parts] + return safe_sys_path_index(_normalize_cached(os.sep.join(parts))) + + new_path = sorted(orig_path, key=position_in_sys_path) + new_path = [_normalize_cached(p) for p in new_path] + + if isinstance(module.__path__, list): + module.__path__[:] = new_path + else: + module.__path__ = new_path + + +def declare_namespace(packageName): + """Declare that package 'packageName' is a namespace package""" + + msg = ( + f"Deprecated call to `pkg_resources.declare_namespace({packageName!r})`.\n" + "Implementing implicit namespace packages (as specified in PEP 420) " + "is preferred to `pkg_resources.declare_namespace`. " + "See https://setuptools.pypa.io/en/latest/references/" + "keywords.html#keyword-namespace-packages" + ) + warnings.warn(msg, DeprecationWarning, stacklevel=2) + + _imp.acquire_lock() + try: + if packageName in _namespace_packages: + return + + path = sys.path + parent, _, _ = packageName.rpartition('.') + + if parent: + declare_namespace(parent) + if parent not in _namespace_packages: + __import__(parent) + try: + path = sys.modules[parent].__path__ + except AttributeError as e: + raise TypeError("Not a package:", parent) from e + + # Track what packages are namespaces, so when new path items are added, + # they can be updated + _namespace_packages.setdefault(parent or None, []).append(packageName) + _namespace_packages.setdefault(packageName, []) + + for path_item in path: + # Ensure all the parent's path items are reflected in the child, + # if they apply + _handle_ns(packageName, path_item) + + finally: + _imp.release_lock() + + +def fixup_namespace_packages(path_item, parent=None): + """Ensure that previously-declared namespace packages include path_item""" + _imp.acquire_lock() + try: + for package in _namespace_packages.get(parent, ()): + subpath = _handle_ns(package, path_item) + if subpath: + fixup_namespace_packages(subpath, package) + finally: + _imp.release_lock() + + +def file_ns_handler(importer, path_item, packageName, module): + """Compute an ns-package subpath for a filesystem or zipfile importer""" + + subpath = os.path.join(path_item, packageName.split('.')[-1]) + normalized = _normalize_cached(subpath) + for item in module.__path__: + if _normalize_cached(item) == normalized: + break + else: + # Only return the path if it's not already there + return subpath + + +if hasattr(pkgutil, 'ImpImporter'): + register_namespace_handler(pkgutil.ImpImporter, file_ns_handler) + +register_namespace_handler(zipimport.zipimporter, file_ns_handler) +register_namespace_handler(importlib_machinery.FileFinder, file_ns_handler) + + +def null_ns_handler(importer, path_item, packageName, module): + return None + + +register_namespace_handler(object, null_ns_handler) + + +def normalize_path(filename): + """Normalize a file/dir name for comparison purposes""" + return os.path.normcase(os.path.realpath(os.path.normpath(_cygwin_patch(filename)))) + + +def _cygwin_patch(filename): # pragma: nocover + """ + Contrary to POSIX 2008, on Cygwin, getcwd (3) contains + symlink components. Using + os.path.abspath() works around this limitation. A fix in os.getcwd() + would probably better, in Cygwin even more so, except + that this seems to be by design... + """ + return os.path.abspath(filename) if sys.platform == 'cygwin' else filename + + +def _normalize_cached(filename, _cache={}): + try: + return _cache[filename] + except KeyError: + _cache[filename] = result = normalize_path(filename) + return result + + +def _is_egg_path(path): + """ + Determine if given path appears to be an egg. + """ + return _is_zip_egg(path) or _is_unpacked_egg(path) + + +def _is_zip_egg(path): + return ( + path.lower().endswith('.egg') + and os.path.isfile(path) + and zipfile.is_zipfile(path) + ) + + +def _is_unpacked_egg(path): + """ + Determine if given path appears to be an unpacked egg. + """ + return path.lower().endswith('.egg') and os.path.isfile( + os.path.join(path, 'EGG-INFO', 'PKG-INFO') + ) + + +def _set_parent_ns(packageName): + parts = packageName.split('.') + name = parts.pop() + if parts: + parent = '.'.join(parts) + setattr(sys.modules[parent], name, sys.modules[packageName]) + + +MODULE = re.compile(r"\w+(\.\w+)*$").match +EGG_NAME = re.compile( + r""" + (?P<name>[^-]+) ( + -(?P<ver>[^-]+) ( + -py(?P<pyver>[^-]+) ( + -(?P<plat>.+) + )? + )? + )? + """, + re.VERBOSE | re.IGNORECASE, +).match + + +class EntryPoint: + """Object representing an advertised importable object""" + + def __init__(self, name, module_name, attrs=(), extras=(), dist=None): + if not MODULE(module_name): + raise ValueError("Invalid module name", module_name) + self.name = name + self.module_name = module_name + self.attrs = tuple(attrs) + self.extras = tuple(extras) + self.dist = dist + + def __str__(self): + s = "%s = %s" % (self.name, self.module_name) + if self.attrs: + s += ':' + '.'.join(self.attrs) + if self.extras: + s += ' [%s]' % ','.join(self.extras) + return s + + def __repr__(self): + return "EntryPoint.parse(%r)" % str(self) + + def load(self, require=True, *args, **kwargs): + """ + Require packages for this EntryPoint, then resolve it. + """ + if not require or args or kwargs: + warnings.warn( + "Parameters to load are deprecated. Call .resolve and " + ".require separately.", + PkgResourcesDeprecationWarning, + stacklevel=2, + ) + if require: + self.require(*args, **kwargs) + return self.resolve() + + def resolve(self): + """ + Resolve the entry point from its module and attrs. + """ + module = __import__(self.module_name, fromlist=['__name__'], level=0) + try: + return functools.reduce(getattr, self.attrs, module) + except AttributeError as exc: + raise ImportError(str(exc)) from exc + + def require(self, env=None, installer=None): + if self.extras and not self.dist: + raise UnknownExtra("Can't require() without a distribution", self) + + # Get the requirements for this entry point with all its extras and + # then resolve them. We have to pass `extras` along when resolving so + # that the working set knows what extras we want. Otherwise, for + # dist-info distributions, the working set will assume that the + # requirements for that extra are purely optional and skip over them. + reqs = self.dist.requires(self.extras) + items = working_set.resolve(reqs, env, installer, extras=self.extras) + list(map(working_set.add, items)) + + pattern = re.compile( + r'\s*' + r'(?P<name>.+?)\s*' + r'=\s*' + r'(?P<module>[\w.]+)\s*' + r'(:\s*(?P<attr>[\w.]+))?\s*' + r'(?P<extras>\[.*\])?\s*$' + ) + + @classmethod + def parse(cls, src, dist=None): + """Parse a single entry point from string `src` + + Entry point syntax follows the form:: + + name = some.module:some.attr [extra1, extra2] + + The entry name and module name are required, but the ``:attrs`` and + ``[extras]`` parts are optional + """ + m = cls.pattern.match(src) + if not m: + msg = "EntryPoint must be in 'name=module:attrs [extras]' format" + raise ValueError(msg, src) + res = m.groupdict() + extras = cls._parse_extras(res['extras']) + attrs = res['attr'].split('.') if res['attr'] else () + return cls(res['name'], res['module'], attrs, extras, dist) + + @classmethod + def _parse_extras(cls, extras_spec): + if not extras_spec: + return () + req = Requirement.parse('x' + extras_spec) + if req.specs: + raise ValueError() + return req.extras + + @classmethod + def parse_group(cls, group, lines, dist=None): + """Parse an entry point group""" + if not MODULE(group): + raise ValueError("Invalid group name", group) + this = {} + for line in yield_lines(lines): + ep = cls.parse(line, dist) + if ep.name in this: + raise ValueError("Duplicate entry point", group, ep.name) + this[ep.name] = ep + return this + + @classmethod + def parse_map(cls, data, dist=None): + """Parse a map of entry point groups""" + if isinstance(data, dict): + data = data.items() + else: + data = split_sections(data) + maps = {} + for group, lines in data: + if group is None: + if not lines: + continue + raise ValueError("Entry points must be listed in groups") + group = group.strip() + if group in maps: + raise ValueError("Duplicate group name", group) + maps[group] = cls.parse_group(group, lines, dist) + return maps + + +def _version_from_file(lines): + """ + Given an iterable of lines from a Metadata file, return + the value of the Version field, if present, or None otherwise. + """ + + def is_version_line(line): + return line.lower().startswith('version:') + + version_lines = filter(is_version_line, lines) + line = next(iter(version_lines), '') + _, _, value = line.partition(':') + return safe_version(value.strip()) or None + + +class Distribution: + """Wrap an actual or potential sys.path entry w/metadata""" + + PKG_INFO = 'PKG-INFO' + + def __init__( + self, + location=None, + metadata=None, + project_name=None, + version=None, + py_version=PY_MAJOR, + platform=None, + precedence=EGG_DIST, + ): + self.project_name = safe_name(project_name or 'Unknown') + if version is not None: + self._version = safe_version(version) + self.py_version = py_version + self.platform = platform + self.location = location + self.precedence = precedence + self._provider = metadata or empty_provider + + @classmethod + def from_location(cls, location, basename, metadata=None, **kw): + project_name, version, py_version, platform = [None] * 4 + basename, ext = os.path.splitext(basename) + if ext.lower() in _distributionImpl: + cls = _distributionImpl[ext.lower()] + + match = EGG_NAME(basename) + if match: + project_name, version, py_version, platform = match.group( + 'name', 'ver', 'pyver', 'plat' + ) + return cls( + location, + metadata, + project_name=project_name, + version=version, + py_version=py_version, + platform=platform, + **kw, + )._reload_version() + + def _reload_version(self): + return self + + @property + def hashcmp(self): + return ( + self._forgiving_parsed_version, + self.precedence, + self.key, + self.location, + self.py_version or '', + self.platform or '', + ) + + def __hash__(self): + return hash(self.hashcmp) + + def __lt__(self, other): + return self.hashcmp < other.hashcmp + + def __le__(self, other): + return self.hashcmp <= other.hashcmp + + def __gt__(self, other): + return self.hashcmp > other.hashcmp + + def __ge__(self, other): + return self.hashcmp >= other.hashcmp + + def __eq__(self, other): + if not isinstance(other, self.__class__): + # It's not a Distribution, so they are not equal + return False + return self.hashcmp == other.hashcmp + + def __ne__(self, other): + return not self == other + + # These properties have to be lazy so that we don't have to load any + # metadata until/unless it's actually needed. (i.e., some distributions + # may not know their name or version without loading PKG-INFO) + + @property + def key(self): + try: + return self._key + except AttributeError: + self._key = key = self.project_name.lower() + return key + + @property + def parsed_version(self): + if not hasattr(self, "_parsed_version"): + try: + self._parsed_version = parse_version(self.version) + except packaging.version.InvalidVersion as ex: + info = f"(package: {self.project_name})" + if hasattr(ex, "add_note"): + ex.add_note(info) # PEP 678 + raise + raise packaging.version.InvalidVersion(f"{str(ex)} {info}") from None + + return self._parsed_version + + @property + def _forgiving_parsed_version(self): + try: + return self.parsed_version + except packaging.version.InvalidVersion as ex: + self._parsed_version = parse_version(_forgiving_version(self.version)) + + notes = "\n".join(getattr(ex, "__notes__", [])) # PEP 678 + msg = f"""!!\n\n + ************************************************************************* + {str(ex)}\n{notes} + + This is a long overdue deprecation. + For the time being, `pkg_resources` will use `{self._parsed_version}` + as a replacement to avoid breaking existing environments, + but no future compatibility is guaranteed. + + If you maintain package {self.project_name} you should implement + the relevant changes to adequate the project to PEP 440 immediately. + ************************************************************************* + \n\n!! + """ + warnings.warn(msg, DeprecationWarning) + + return self._parsed_version + + @property + def version(self): + try: + return self._version + except AttributeError as e: + version = self._get_version() + if version is None: + path = self._get_metadata_path_for_display(self.PKG_INFO) + msg = ("Missing 'Version:' header and/or {} file at path: {}").format( + self.PKG_INFO, path + ) + raise ValueError(msg, self) from e + + return version + + @property + def _dep_map(self): + """ + A map of extra to its list of (direct) requirements + for this distribution, including the null extra. + """ + try: + return self.__dep_map + except AttributeError: + self.__dep_map = self._filter_extras(self._build_dep_map()) + return self.__dep_map + + @staticmethod + def _filter_extras(dm): + """ + Given a mapping of extras to dependencies, strip off + environment markers and filter out any dependencies + not matching the markers. + """ + for extra in list(filter(None, dm)): + new_extra = extra + reqs = dm.pop(extra) + new_extra, _, marker = extra.partition(':') + fails_marker = marker and ( + invalid_marker(marker) or not evaluate_marker(marker) + ) + if fails_marker: + reqs = [] + new_extra = safe_extra(new_extra) or None + + dm.setdefault(new_extra, []).extend(reqs) + return dm + + def _build_dep_map(self): + dm = {} + for name in 'requires.txt', 'depends.txt': + for extra, reqs in split_sections(self._get_metadata(name)): + dm.setdefault(extra, []).extend(parse_requirements(reqs)) + return dm + + def requires(self, extras=()): + """List of Requirements needed for this distro if `extras` are used""" + dm = self._dep_map + deps = [] + deps.extend(dm.get(None, ())) + for ext in extras: + try: + deps.extend(dm[safe_extra(ext)]) + except KeyError as e: + raise UnknownExtra( + "%s has no such extra feature %r" % (self, ext) + ) from e + return deps + + def _get_metadata_path_for_display(self, name): + """ + Return the path to the given metadata file, if available. + """ + try: + # We need to access _get_metadata_path() on the provider object + # directly rather than through this class's __getattr__() + # since _get_metadata_path() is marked private. + path = self._provider._get_metadata_path(name) + + # Handle exceptions e.g. in case the distribution's metadata + # provider doesn't support _get_metadata_path(). + except Exception: + return '[could not detect]' + + return path + + def _get_metadata(self, name): + if self.has_metadata(name): + for line in self.get_metadata_lines(name): + yield line + + def _get_version(self): + lines = self._get_metadata(self.PKG_INFO) + version = _version_from_file(lines) + + return version + + def activate(self, path=None, replace=False): + """Ensure distribution is importable on `path` (default=sys.path)""" + if path is None: + path = sys.path + self.insert_on(path, replace=replace) + if path is sys.path: + fixup_namespace_packages(self.location) + for pkg in self._get_metadata('namespace_packages.txt'): + if pkg in sys.modules: + declare_namespace(pkg) + + def egg_name(self): + """Return what this distribution's standard .egg filename should be""" + filename = "%s-%s-py%s" % ( + to_filename(self.project_name), + to_filename(self.version), + self.py_version or PY_MAJOR, + ) + + if self.platform: + filename += '-' + self.platform + return filename + + def __repr__(self): + if self.location: + return "%s (%s)" % (self, self.location) + else: + return str(self) + + def __str__(self): + try: + version = getattr(self, 'version', None) + except ValueError: + version = None + version = version or "[unknown version]" + return "%s %s" % (self.project_name, version) + + def __getattr__(self, attr): + """Delegate all unrecognized public attributes to .metadata provider""" + if attr.startswith('_'): + raise AttributeError(attr) + return getattr(self._provider, attr) + + def __dir__(self): + return list( + set(super(Distribution, self).__dir__()) + | set(attr for attr in self._provider.__dir__() if not attr.startswith('_')) + ) + + @classmethod + def from_filename(cls, filename, metadata=None, **kw): + return cls.from_location( + _normalize_cached(filename), os.path.basename(filename), metadata, **kw + ) + + def as_requirement(self): + """Return a ``Requirement`` that matches this distribution exactly""" + if isinstance(self.parsed_version, packaging.version.Version): + spec = "%s==%s" % (self.project_name, self.parsed_version) + else: + spec = "%s===%s" % (self.project_name, self.parsed_version) + + return Requirement.parse(spec) + + def load_entry_point(self, group, name): + """Return the `name` entry point of `group` or raise ImportError""" + ep = self.get_entry_info(group, name) + if ep is None: + raise ImportError("Entry point %r not found" % ((group, name),)) + return ep.load() + + def get_entry_map(self, group=None): + """Return the entry point map for `group`, or the full entry map""" + try: + ep_map = self._ep_map + except AttributeError: + ep_map = self._ep_map = EntryPoint.parse_map( + self._get_metadata('entry_points.txt'), self + ) + if group is not None: + return ep_map.get(group, {}) + return ep_map + + def get_entry_info(self, group, name): + """Return the EntryPoint object for `group`+`name`, or ``None``""" + return self.get_entry_map(group).get(name) + + # FIXME: 'Distribution.insert_on' is too complex (13) + def insert_on(self, path, loc=None, replace=False): # noqa: C901 + """Ensure self.location is on path + + If replace=False (default): + - If location is already in path anywhere, do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent. + - Else: add to the end of path. + If replace=True: + - If location is already on path anywhere (not eggs) + or higher priority than its parent (eggs) + do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent, + removing any lower-priority entries. + - Else: add it to the front of path. + """ + + loc = loc or self.location + if not loc: + return + + nloc = _normalize_cached(loc) + bdir = os.path.dirname(nloc) + npath = [(p and _normalize_cached(p) or p) for p in path] + + for p, item in enumerate(npath): + if item == nloc: + if replace: + break + else: + # don't modify path (even removing duplicates) if + # found and not replace + return + elif item == bdir and self.precedence == EGG_DIST: + # if it's an .egg, give it precedence over its directory + # UNLESS it's already been added to sys.path and replace=False + if (not replace) and nloc in npath[p:]: + return + if path is sys.path: + self.check_version_conflict() + path.insert(p, loc) + npath.insert(p, nloc) + break + else: + if path is sys.path: + self.check_version_conflict() + if replace: + path.insert(0, loc) + else: + path.append(loc) + return + + # p is the spot where we found or inserted loc; now remove duplicates + while True: + try: + np = npath.index(nloc, p + 1) + except ValueError: + break + else: + del npath[np], path[np] + # ha! + p = np + + return + + def check_version_conflict(self): + if self.key == 'setuptools': + # ignore the inevitable setuptools self-conflicts :( + return + + nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt')) + loc = normalize_path(self.location) + for modname in self._get_metadata('top_level.txt'): + if ( + modname not in sys.modules + or modname in nsp + or modname in _namespace_packages + ): + continue + if modname in ('pkg_resources', 'setuptools', 'site'): + continue + fn = getattr(sys.modules[modname], '__file__', None) + if fn and ( + normalize_path(fn).startswith(loc) or fn.startswith(self.location) + ): + continue + issue_warning( + "Module %s was already imported from %s, but %s is being added" + " to sys.path" % (modname, fn, self.location), + ) + + def has_version(self): + try: + self.version + except ValueError: + issue_warning("Unbuilt egg for " + repr(self)) + return False + except SystemError: + # TODO: remove this except clause when python/cpython#103632 is fixed. + return False + return True + + def clone(self, **kw): + """Copy this distribution, substituting in any changed keyword args""" + names = 'project_name version py_version platform location precedence' + for attr in names.split(): + kw.setdefault(attr, getattr(self, attr, None)) + kw.setdefault('metadata', self._provider) + return self.__class__(**kw) + + @property + def extras(self): + return [dep for dep in self._dep_map if dep] + + +class EggInfoDistribution(Distribution): + def _reload_version(self): + """ + Packages installed by distutils (e.g. numpy or scipy), + which uses an old safe_version, and so + their version numbers can get mangled when + converted to filenames (e.g., 1.11.0.dev0+2329eae to + 1.11.0.dev0_2329eae). These distributions will not be + parsed properly + downstream by Distribution and safe_version, so + take an extra step and try to get the version number from + the metadata file itself instead of the filename. + """ + md_version = self._get_version() + if md_version: + self._version = md_version + return self + + +class DistInfoDistribution(Distribution): + """ + Wrap an actual or potential sys.path entry + w/metadata, .dist-info style. + """ + + PKG_INFO = 'METADATA' + EQEQ = re.compile(r"([\(,])\s*(\d.*?)\s*([,\)])") + + @property + def _parsed_pkg_info(self): + """Parse and cache metadata""" + try: + return self._pkg_info + except AttributeError: + metadata = self.get_metadata(self.PKG_INFO) + self._pkg_info = email.parser.Parser().parsestr(metadata) + return self._pkg_info + + @property + def _dep_map(self): + try: + return self.__dep_map + except AttributeError: + self.__dep_map = self._compute_dependencies() + return self.__dep_map + + def _compute_dependencies(self): + """Recompute this distribution's dependencies.""" + dm = self.__dep_map = {None: []} + + reqs = [] + # Including any condition expressions + for req in self._parsed_pkg_info.get_all('Requires-Dist') or []: + reqs.extend(parse_requirements(req)) + + def reqs_for_extra(extra): + for req in reqs: + if not req.marker or req.marker.evaluate({'extra': extra}): + yield req + + common = types.MappingProxyType(dict.fromkeys(reqs_for_extra(None))) + dm[None].extend(common) + + for extra in self._parsed_pkg_info.get_all('Provides-Extra') or []: + s_extra = safe_extra(extra.strip()) + dm[s_extra] = [r for r in reqs_for_extra(extra) if r not in common] + + return dm + + +_distributionImpl = { + '.egg': Distribution, + '.egg-info': EggInfoDistribution, + '.dist-info': DistInfoDistribution, +} + + +def issue_warning(*args, **kw): + level = 1 + g = globals() + try: + # find the first stack frame that is *not* code in + # the pkg_resources module, to use for the warning + while sys._getframe(level).f_globals is g: + level += 1 + except ValueError: + pass + warnings.warn(stacklevel=level + 1, *args, **kw) + + +def parse_requirements(strs): + """ + Yield ``Requirement`` objects for each specification in `strs`. + + `strs` must be a string, or a (possibly-nested) iterable thereof. + """ + return map(Requirement, join_continuation(map(drop_comment, yield_lines(strs)))) + + +class RequirementParseError(packaging.requirements.InvalidRequirement): + "Compatibility wrapper for InvalidRequirement" + + +class Requirement(packaging.requirements.Requirement): + def __init__(self, requirement_string): + """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!""" + super(Requirement, self).__init__(requirement_string) + self.unsafe_name = self.name + project_name = safe_name(self.name) + self.project_name, self.key = project_name, project_name.lower() + self.specs = [(spec.operator, spec.version) for spec in self.specifier] + self.extras = tuple(map(safe_extra, self.extras)) + self.hashCmp = ( + self.key, + self.url, + self.specifier, + frozenset(self.extras), + str(self.marker) if self.marker else None, + ) + self.__hash = hash(self.hashCmp) + + def __eq__(self, other): + return isinstance(other, Requirement) and self.hashCmp == other.hashCmp + + def __ne__(self, other): + return not self == other + + def __contains__(self, item): + if isinstance(item, Distribution): + if item.key != self.key: + return False + + item = item.version + + # Allow prereleases always in order to match the previous behavior of + # this method. In the future this should be smarter and follow PEP 440 + # more accurately. + return self.specifier.contains(item, prereleases=True) + + def __hash__(self): + return self.__hash + + def __repr__(self): + return "Requirement.parse(%r)" % str(self) + + @staticmethod + def parse(s): + (req,) = parse_requirements(s) + return req + + +def _always_object(classes): + """ + Ensure object appears in the mro even + for old-style classes. + """ + if object not in classes: + return classes + (object,) + return classes + + +def _find_adapter(registry, ob): + """Return an adapter factory for `ob` from `registry`""" + types = _always_object(inspect.getmro(getattr(ob, '__class__', type(ob)))) + for t in types: + if t in registry: + return registry[t] + + +def ensure_directory(path): + """Ensure that the parent directory of `path` exists""" + dirname = os.path.dirname(path) + os.makedirs(dirname, exist_ok=True) + + +def _bypass_ensure_directory(path): + """Sandbox-bypassing version of ensure_directory()""" + if not WRITE_SUPPORT: + raise IOError('"os.mkdir" not supported on this platform.') + dirname, filename = split(path) + if dirname and filename and not isdir(dirname): + _bypass_ensure_directory(dirname) + try: + mkdir(dirname, 0o755) + except FileExistsError: + pass + + +def split_sections(s): + """Split a string or iterable thereof into (section, content) pairs + + Each ``section`` is a stripped version of the section header ("[section]") + and each ``content`` is a list of stripped lines excluding blank lines and + comment-only lines. If there are any such lines before the first section + header, they're returned in a first ``section`` of ``None``. + """ + section = None + content = [] + for line in yield_lines(s): + if line.startswith("["): + if line.endswith("]"): + if section or content: + yield section, content + section = line[1:-1].strip() + content = [] + else: + raise ValueError("Invalid section heading", line) + else: + content.append(line) + + # wrap up last segment + yield section, content + + +def _mkstemp(*args, **kw): + old_open = os.open + try: + # temporarily bypass sandboxing + os.open = os_open + return tempfile.mkstemp(*args, **kw) + finally: + # and then put it back + os.open = old_open + + +# Silence the PEP440Warning by default, so that end users don't get hit by it +# randomly just because they use pkg_resources. We want to append the rule +# because we want earlier uses of filterwarnings to take precedence over this +# one. +warnings.filterwarnings("ignore", category=PEP440Warning, append=True) + + +# from jaraco.functools 1.3 +def _call_aside(f, *args, **kwargs): + f(*args, **kwargs) + return f + + +@_call_aside +def _initialize(g=globals()): + "Set up global resource manager (deliberately not state-saved)" + manager = ResourceManager() + g['_manager'] = manager + g.update( + (name, getattr(manager, name)) + for name in dir(manager) + if not name.startswith('_') + ) + + +class PkgResourcesDeprecationWarning(Warning): + """ + Base class for warning about deprecations in ``pkg_resources`` + + This class is not derived from ``DeprecationWarning``, and as such is + visible by default. + """ + + +@_call_aside +def _initialize_master_working_set(): + """ + Prepare the master working set and make the ``require()`` + API available. + + This function has explicit effects on the global state + of pkg_resources. It is intended to be invoked once at + the initialization of this module. + + Invocation by other packages is unsupported and done + at their own risk. + """ + working_set = WorkingSet._build_master() + _declare_state('object', working_set=working_set) + + require = working_set.require + iter_entry_points = working_set.iter_entry_points + add_activation_listener = working_set.subscribe + run_script = working_set.run_script + # backward compatibility + run_main = run_script + # Activate all distributions already on sys.path with replace=False and + # ensure that all distributions added to the working set in the future + # (e.g. by calling ``require()``) will get activated as well, + # with higher priority (replace=True). + tuple(dist.activate(replace=False) for dist in working_set) + add_activation_listener( + lambda dist: dist.activate(replace=True), + existing=False, + ) + working_set.entries = [] + # match order + list(map(working_set.add_entry, sys.path)) + globals().update(locals()) diff --git a/virt/lib/python3.9/site-packages/pip/_vendor/pygments/filters/__init__ 3.py b/virt/lib/python3.9/site-packages/pip/_vendor/pygments/filters/__init__ 3.py new file mode 100644 index 00000000..02ae6248 --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_vendor/pygments/filters/__init__ 3.py @@ -0,0 +1,940 @@ +""" + pygments.filters + ~~~~~~~~~~~~~~~~ + + Module containing filter lookup functions and default + filters. + + :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pip._vendor.pygments.token import String, Comment, Keyword, Name, Error, Whitespace, \ + string_to_tokentype +from pip._vendor.pygments.filter import Filter +from pip._vendor.pygments.util import get_list_opt, get_int_opt, get_bool_opt, \ + get_choice_opt, ClassNotFound, OptionError +from pip._vendor.pygments.plugin import find_plugin_filters + + +def find_filter_class(filtername): + """Lookup a filter by name. Return None if not found.""" + if filtername in FILTERS: + return FILTERS[filtername] + for name, cls in find_plugin_filters(): + if name == filtername: + return cls + return None + + +def get_filter_by_name(filtername, **options): + """Return an instantiated filter. + + Options are passed to the filter initializer if wanted. + Raise a ClassNotFound if not found. + """ + cls = find_filter_class(filtername) + if cls: + return cls(**options) + else: + raise ClassNotFound('filter %r not found' % filtername) + + +def get_all_filters(): + """Return a generator of all filter names.""" + yield from FILTERS + for name, _ in find_plugin_filters(): + yield name + + +def _replace_special(ttype, value, regex, specialttype, + replacefunc=lambda x: x): + last = 0 + for match in regex.finditer(value): + start, end = match.start(), match.end() + if start != last: + yield ttype, value[last:start] + yield specialttype, replacefunc(value[start:end]) + last = end + if last != len(value): + yield ttype, value[last:] + + +class CodeTagFilter(Filter): + """Highlight special code tags in comments and docstrings. + + Options accepted: + + `codetags` : list of strings + A list of strings that are flagged as code tags. The default is to + highlight ``XXX``, ``TODO``, ``FIXME``, ``BUG`` and ``NOTE``. + + .. versionchanged:: 2.13 + Now recognizes ``FIXME`` by default. + """ + + def __init__(self, **options): + Filter.__init__(self, **options) + tags = get_list_opt(options, 'codetags', + ['XXX', 'TODO', 'FIXME', 'BUG', 'NOTE']) + self.tag_re = re.compile(r'\b(%s)\b' % '|'.join([ + re.escape(tag) for tag in tags if tag + ])) + + def filter(self, lexer, stream): + regex = self.tag_re + for ttype, value in stream: + if ttype in String.Doc or \ + ttype in Comment and \ + ttype not in Comment.Preproc: + yield from _replace_special(ttype, value, regex, Comment.Special) + else: + yield ttype, value + + +class SymbolFilter(Filter): + """Convert mathematical symbols such as \\<longrightarrow> in Isabelle + or \\longrightarrow in LaTeX into Unicode characters. + + This is mostly useful for HTML or console output when you want to + approximate the source rendering you'd see in an IDE. + + Options accepted: + + `lang` : string + The symbol language. Must be one of ``'isabelle'`` or + ``'latex'``. The default is ``'isabelle'``. + """ + + latex_symbols = { + '\\alpha' : '\U000003b1', + '\\beta' : '\U000003b2', + '\\gamma' : '\U000003b3', + '\\delta' : '\U000003b4', + '\\varepsilon' : '\U000003b5', + '\\zeta' : '\U000003b6', + '\\eta' : '\U000003b7', + '\\vartheta' : '\U000003b8', + '\\iota' : '\U000003b9', + '\\kappa' : '\U000003ba', + '\\lambda' : '\U000003bb', + '\\mu' : '\U000003bc', + '\\nu' : '\U000003bd', + '\\xi' : '\U000003be', + '\\pi' : '\U000003c0', + '\\varrho' : '\U000003c1', + '\\sigma' : '\U000003c3', + '\\tau' : '\U000003c4', + '\\upsilon' : '\U000003c5', + '\\varphi' : '\U000003c6', + '\\chi' : '\U000003c7', + '\\psi' : '\U000003c8', + '\\omega' : '\U000003c9', + '\\Gamma' : '\U00000393', + '\\Delta' : '\U00000394', + '\\Theta' : '\U00000398', + '\\Lambda' : '\U0000039b', + '\\Xi' : '\U0000039e', + '\\Pi' : '\U000003a0', + '\\Sigma' : '\U000003a3', + '\\Upsilon' : '\U000003a5', + '\\Phi' : '\U000003a6', + '\\Psi' : '\U000003a8', + '\\Omega' : '\U000003a9', + '\\leftarrow' : '\U00002190', + '\\longleftarrow' : '\U000027f5', + '\\rightarrow' : '\U00002192', + '\\longrightarrow' : '\U000027f6', + '\\Leftarrow' : '\U000021d0', + '\\Longleftarrow' : '\U000027f8', + '\\Rightarrow' : '\U000021d2', + '\\Longrightarrow' : '\U000027f9', + '\\leftrightarrow' : '\U00002194', + '\\longleftrightarrow' : '\U000027f7', + '\\Leftrightarrow' : '\U000021d4', + '\\Longleftrightarrow' : '\U000027fa', + '\\mapsto' : '\U000021a6', + '\\longmapsto' : '\U000027fc', + '\\relbar' : '\U00002500', + '\\Relbar' : '\U00002550', + '\\hookleftarrow' : '\U000021a9', + '\\hookrightarrow' : '\U000021aa', + '\\leftharpoondown' : '\U000021bd', + '\\rightharpoondown' : '\U000021c1', + '\\leftharpoonup' : '\U000021bc', + '\\rightharpoonup' : '\U000021c0', + '\\rightleftharpoons' : '\U000021cc', + '\\leadsto' : '\U0000219d', + '\\downharpoonleft' : '\U000021c3', + '\\downharpoonright' : '\U000021c2', + '\\upharpoonleft' : '\U000021bf', + '\\upharpoonright' : '\U000021be', + '\\restriction' : '\U000021be', + '\\uparrow' : '\U00002191', + '\\Uparrow' : '\U000021d1', + '\\downarrow' : '\U00002193', + '\\Downarrow' : '\U000021d3', + '\\updownarrow' : '\U00002195', + '\\Updownarrow' : '\U000021d5', + '\\langle' : '\U000027e8', + '\\rangle' : '\U000027e9', + '\\lceil' : '\U00002308', + '\\rceil' : '\U00002309', + '\\lfloor' : '\U0000230a', + '\\rfloor' : '\U0000230b', + '\\flqq' : '\U000000ab', + '\\frqq' : '\U000000bb', + '\\bot' : '\U000022a5', + '\\top' : '\U000022a4', + '\\wedge' : '\U00002227', + '\\bigwedge' : '\U000022c0', + '\\vee' : '\U00002228', + '\\bigvee' : '\U000022c1', + '\\forall' : '\U00002200', + '\\exists' : '\U00002203', + '\\nexists' : '\U00002204', + '\\neg' : '\U000000ac', + '\\Box' : '\U000025a1', + '\\Diamond' : '\U000025c7', + '\\vdash' : '\U000022a2', + '\\models' : '\U000022a8', + '\\dashv' : '\U000022a3', + '\\surd' : '\U0000221a', + '\\le' : '\U00002264', + '\\ge' : '\U00002265', + '\\ll' : '\U0000226a', + '\\gg' : '\U0000226b', + '\\lesssim' : '\U00002272', + '\\gtrsim' : '\U00002273', + '\\lessapprox' : '\U00002a85', + '\\gtrapprox' : '\U00002a86', + '\\in' : '\U00002208', + '\\notin' : '\U00002209', + '\\subset' : '\U00002282', + '\\supset' : '\U00002283', + '\\subseteq' : '\U00002286', + '\\supseteq' : '\U00002287', + '\\sqsubset' : '\U0000228f', + '\\sqsupset' : '\U00002290', + '\\sqsubseteq' : '\U00002291', + '\\sqsupseteq' : '\U00002292', + '\\cap' : '\U00002229', + '\\bigcap' : '\U000022c2', + '\\cup' : '\U0000222a', + '\\bigcup' : '\U000022c3', + '\\sqcup' : '\U00002294', + '\\bigsqcup' : '\U00002a06', + '\\sqcap' : '\U00002293', + '\\Bigsqcap' : '\U00002a05', + '\\setminus' : '\U00002216', + '\\propto' : '\U0000221d', + '\\uplus' : '\U0000228e', + '\\bigplus' : '\U00002a04', + '\\sim' : '\U0000223c', + '\\doteq' : '\U00002250', + '\\simeq' : '\U00002243', + '\\approx' : '\U00002248', + '\\asymp' : '\U0000224d', + '\\cong' : '\U00002245', + '\\equiv' : '\U00002261', + '\\Join' : '\U000022c8', + '\\bowtie' : '\U00002a1d', + '\\prec' : '\U0000227a', + '\\succ' : '\U0000227b', + '\\preceq' : '\U0000227c', + '\\succeq' : '\U0000227d', + '\\parallel' : '\U00002225', + '\\mid' : '\U000000a6', + '\\pm' : '\U000000b1', + '\\mp' : '\U00002213', + '\\times' : '\U000000d7', + '\\div' : '\U000000f7', + '\\cdot' : '\U000022c5', + '\\star' : '\U000022c6', + '\\circ' : '\U00002218', + '\\dagger' : '\U00002020', + '\\ddagger' : '\U00002021', + '\\lhd' : '\U000022b2', + '\\rhd' : '\U000022b3', + '\\unlhd' : '\U000022b4', + '\\unrhd' : '\U000022b5', + '\\triangleleft' : '\U000025c3', + '\\triangleright' : '\U000025b9', + '\\triangle' : '\U000025b3', + '\\triangleq' : '\U0000225c', + '\\oplus' : '\U00002295', + '\\bigoplus' : '\U00002a01', + '\\otimes' : '\U00002297', + '\\bigotimes' : '\U00002a02', + '\\odot' : '\U00002299', + '\\bigodot' : '\U00002a00', + '\\ominus' : '\U00002296', + '\\oslash' : '\U00002298', + '\\dots' : '\U00002026', + '\\cdots' : '\U000022ef', + '\\sum' : '\U00002211', + '\\prod' : '\U0000220f', + '\\coprod' : '\U00002210', + '\\infty' : '\U0000221e', + '\\int' : '\U0000222b', + '\\oint' : '\U0000222e', + '\\clubsuit' : '\U00002663', + '\\diamondsuit' : '\U00002662', + '\\heartsuit' : '\U00002661', + '\\spadesuit' : '\U00002660', + '\\aleph' : '\U00002135', + '\\emptyset' : '\U00002205', + '\\nabla' : '\U00002207', + '\\partial' : '\U00002202', + '\\flat' : '\U0000266d', + '\\natural' : '\U0000266e', + '\\sharp' : '\U0000266f', + '\\angle' : '\U00002220', + '\\copyright' : '\U000000a9', + '\\textregistered' : '\U000000ae', + '\\textonequarter' : '\U000000bc', + '\\textonehalf' : '\U000000bd', + '\\textthreequarters' : '\U000000be', + '\\textordfeminine' : '\U000000aa', + '\\textordmasculine' : '\U000000ba', + '\\euro' : '\U000020ac', + '\\pounds' : '\U000000a3', + '\\yen' : '\U000000a5', + '\\textcent' : '\U000000a2', + '\\textcurrency' : '\U000000a4', + '\\textdegree' : '\U000000b0', + } + + isabelle_symbols = { + '\\<zero>' : '\U0001d7ec', + '\\<one>' : '\U0001d7ed', + '\\<two>' : '\U0001d7ee', + '\\<three>' : '\U0001d7ef', + '\\<four>' : '\U0001d7f0', + '\\<five>' : '\U0001d7f1', + '\\<six>' : '\U0001d7f2', + '\\<seven>' : '\U0001d7f3', + '\\<eight>' : '\U0001d7f4', + '\\<nine>' : '\U0001d7f5', + '\\<A>' : '\U0001d49c', + '\\<B>' : '\U0000212c', + '\\<C>' : '\U0001d49e', + '\\<D>' : '\U0001d49f', + '\\<E>' : '\U00002130', + '\\<F>' : '\U00002131', + '\\<G>' : '\U0001d4a2', + '\\<H>' : '\U0000210b', + '\\<I>' : '\U00002110', + '\\<J>' : '\U0001d4a5', + '\\<K>' : '\U0001d4a6', + '\\<L>' : '\U00002112', + '\\<M>' : '\U00002133', + '\\<N>' : '\U0001d4a9', + '\\<O>' : '\U0001d4aa', + '\\<P>' : '\U0001d4ab', + '\\<Q>' : '\U0001d4ac', + '\\<R>' : '\U0000211b', + '\\<S>' : '\U0001d4ae', + '\\<T>' : '\U0001d4af', + '\\<U>' : '\U0001d4b0', + '\\<V>' : '\U0001d4b1', + '\\<W>' : '\U0001d4b2', + '\\<X>' : '\U0001d4b3', + '\\<Y>' : '\U0001d4b4', + '\\<Z>' : '\U0001d4b5', + '\\<a>' : '\U0001d5ba', + '\\<b>' : '\U0001d5bb', + '\\<c>' : '\U0001d5bc', + '\\<d>' : '\U0001d5bd', + '\\<e>' : '\U0001d5be', + '\\<f>' : '\U0001d5bf', + '\\<g>' : '\U0001d5c0', + '\\<h>' : '\U0001d5c1', + '\\<i>' : '\U0001d5c2', + '\\<j>' : '\U0001d5c3', + '\\<k>' : '\U0001d5c4', + '\\<l>' : '\U0001d5c5', + '\\<m>' : '\U0001d5c6', + '\\<n>' : '\U0001d5c7', + '\\<o>' : '\U0001d5c8', + '\\<p>' : '\U0001d5c9', + '\\<q>' : '\U0001d5ca', + '\\<r>' : '\U0001d5cb', + '\\<s>' : '\U0001d5cc', + '\\<t>' : '\U0001d5cd', + '\\<u>' : '\U0001d5ce', + '\\<v>' : '\U0001d5cf', + '\\<w>' : '\U0001d5d0', + '\\<x>' : '\U0001d5d1', + '\\<y>' : '\U0001d5d2', + '\\<z>' : '\U0001d5d3', + '\\<AA>' : '\U0001d504', + '\\<BB>' : '\U0001d505', + '\\<CC>' : '\U0000212d', + '\\<DD>' : '\U0001d507', + '\\<EE>' : '\U0001d508', + '\\<FF>' : '\U0001d509', + '\\<GG>' : '\U0001d50a', + '\\<HH>' : '\U0000210c', + '\\<II>' : '\U00002111', + '\\<JJ>' : '\U0001d50d', + '\\<KK>' : '\U0001d50e', + '\\<LL>' : '\U0001d50f', + '\\<MM>' : '\U0001d510', + '\\<NN>' : '\U0001d511', + '\\<OO>' : '\U0001d512', + '\\<PP>' : '\U0001d513', + '\\<QQ>' : '\U0001d514', + '\\<RR>' : '\U0000211c', + '\\<SS>' : '\U0001d516', + '\\<TT>' : '\U0001d517', + '\\<UU>' : '\U0001d518', + '\\<VV>' : '\U0001d519', + '\\<WW>' : '\U0001d51a', + '\\<XX>' : '\U0001d51b', + '\\<YY>' : '\U0001d51c', + '\\<ZZ>' : '\U00002128', + '\\<aa>' : '\U0001d51e', + '\\<bb>' : '\U0001d51f', + '\\<cc>' : '\U0001d520', + '\\<dd>' : '\U0001d521', + '\\<ee>' : '\U0001d522', + '\\<ff>' : '\U0001d523', + '\\<gg>' : '\U0001d524', + '\\<hh>' : '\U0001d525', + '\\<ii>' : '\U0001d526', + '\\<jj>' : '\U0001d527', + '\\<kk>' : '\U0001d528', + '\\<ll>' : '\U0001d529', + '\\<mm>' : '\U0001d52a', + '\\<nn>' : '\U0001d52b', + '\\<oo>' : '\U0001d52c', + '\\<pp>' : '\U0001d52d', + '\\<qq>' : '\U0001d52e', + '\\<rr>' : '\U0001d52f', + '\\<ss>' : '\U0001d530', + '\\<tt>' : '\U0001d531', + '\\<uu>' : '\U0001d532', + '\\<vv>' : '\U0001d533', + '\\<ww>' : '\U0001d534', + '\\<xx>' : '\U0001d535', + '\\<yy>' : '\U0001d536', + '\\<zz>' : '\U0001d537', + '\\<alpha>' : '\U000003b1', + '\\<beta>' : '\U000003b2', + '\\<gamma>' : '\U000003b3', + '\\<delta>' : '\U000003b4', + '\\<epsilon>' : '\U000003b5', + '\\<zeta>' : '\U000003b6', + '\\<eta>' : '\U000003b7', + '\\<theta>' : '\U000003b8', + '\\<iota>' : '\U000003b9', + '\\<kappa>' : '\U000003ba', + '\\<lambda>' : '\U000003bb', + '\\<mu>' : '\U000003bc', + '\\<nu>' : '\U000003bd', + '\\<xi>' : '\U000003be', + '\\<pi>' : '\U000003c0', + '\\<rho>' : '\U000003c1', + '\\<sigma>' : '\U000003c3', + '\\<tau>' : '\U000003c4', + '\\<upsilon>' : '\U000003c5', + '\\<phi>' : '\U000003c6', + '\\<chi>' : '\U000003c7', + '\\<psi>' : '\U000003c8', + '\\<omega>' : '\U000003c9', + '\\<Gamma>' : '\U00000393', + '\\<Delta>' : '\U00000394', + '\\<Theta>' : '\U00000398', + '\\<Lambda>' : '\U0000039b', + '\\<Xi>' : '\U0000039e', + '\\<Pi>' : '\U000003a0', + '\\<Sigma>' : '\U000003a3', + '\\<Upsilon>' : '\U000003a5', + '\\<Phi>' : '\U000003a6', + '\\<Psi>' : '\U000003a8', + '\\<Omega>' : '\U000003a9', + '\\<bool>' : '\U0001d539', + '\\<complex>' : '\U00002102', + '\\<nat>' : '\U00002115', + '\\<rat>' : '\U0000211a', + '\\<real>' : '\U0000211d', + '\\<int>' : '\U00002124', + '\\<leftarrow>' : '\U00002190', + '\\<longleftarrow>' : '\U000027f5', + '\\<rightarrow>' : '\U00002192', + '\\<longrightarrow>' : '\U000027f6', + '\\<Leftarrow>' : '\U000021d0', + '\\<Longleftarrow>' : '\U000027f8', + '\\<Rightarrow>' : '\U000021d2', + '\\<Longrightarrow>' : '\U000027f9', + '\\<leftrightarrow>' : '\U00002194', + '\\<longleftrightarrow>' : '\U000027f7', + '\\<Leftrightarrow>' : '\U000021d4', + '\\<Longleftrightarrow>' : '\U000027fa', + '\\<mapsto>' : '\U000021a6', + '\\<longmapsto>' : '\U000027fc', + '\\<midarrow>' : '\U00002500', + '\\<Midarrow>' : '\U00002550', + '\\<hookleftarrow>' : '\U000021a9', + '\\<hookrightarrow>' : '\U000021aa', + '\\<leftharpoondown>' : '\U000021bd', + '\\<rightharpoondown>' : '\U000021c1', + '\\<leftharpoonup>' : '\U000021bc', + '\\<rightharpoonup>' : '\U000021c0', + '\\<rightleftharpoons>' : '\U000021cc', + '\\<leadsto>' : '\U0000219d', + '\\<downharpoonleft>' : '\U000021c3', + '\\<downharpoonright>' : '\U000021c2', + '\\<upharpoonleft>' : '\U000021bf', + '\\<upharpoonright>' : '\U000021be', + '\\<restriction>' : '\U000021be', + '\\<Colon>' : '\U00002237', + '\\<up>' : '\U00002191', + '\\<Up>' : '\U000021d1', + '\\<down>' : '\U00002193', + '\\<Down>' : '\U000021d3', + '\\<updown>' : '\U00002195', + '\\<Updown>' : '\U000021d5', + '\\<langle>' : '\U000027e8', + '\\<rangle>' : '\U000027e9', + '\\<lceil>' : '\U00002308', + '\\<rceil>' : '\U00002309', + '\\<lfloor>' : '\U0000230a', + '\\<rfloor>' : '\U0000230b', + '\\<lparr>' : '\U00002987', + '\\<rparr>' : '\U00002988', + '\\<lbrakk>' : '\U000027e6', + '\\<rbrakk>' : '\U000027e7', + '\\<lbrace>' : '\U00002983', + '\\<rbrace>' : '\U00002984', + '\\<guillemotleft>' : '\U000000ab', + '\\<guillemotright>' : '\U000000bb', + '\\<bottom>' : '\U000022a5', + '\\<top>' : '\U000022a4', + '\\<and>' : '\U00002227', + '\\<And>' : '\U000022c0', + '\\<or>' : '\U00002228', + '\\<Or>' : '\U000022c1', + '\\<forall>' : '\U00002200', + '\\<exists>' : '\U00002203', + '\\<nexists>' : '\U00002204', + '\\<not>' : '\U000000ac', + '\\<box>' : '\U000025a1', + '\\<diamond>' : '\U000025c7', + '\\<turnstile>' : '\U000022a2', + '\\<Turnstile>' : '\U000022a8', + '\\<tturnstile>' : '\U000022a9', + '\\<TTurnstile>' : '\U000022ab', + '\\<stileturn>' : '\U000022a3', + '\\<surd>' : '\U0000221a', + '\\<le>' : '\U00002264', + '\\<ge>' : '\U00002265', + '\\<lless>' : '\U0000226a', + '\\<ggreater>' : '\U0000226b', + '\\<lesssim>' : '\U00002272', + '\\<greatersim>' : '\U00002273', + '\\<lessapprox>' : '\U00002a85', + '\\<greaterapprox>' : '\U00002a86', + '\\<in>' : '\U00002208', + '\\<notin>' : '\U00002209', + '\\<subset>' : '\U00002282', + '\\<supset>' : '\U00002283', + '\\<subseteq>' : '\U00002286', + '\\<supseteq>' : '\U00002287', + '\\<sqsubset>' : '\U0000228f', + '\\<sqsupset>' : '\U00002290', + '\\<sqsubseteq>' : '\U00002291', + '\\<sqsupseteq>' : '\U00002292', + '\\<inter>' : '\U00002229', + '\\<Inter>' : '\U000022c2', + '\\<union>' : '\U0000222a', + '\\<Union>' : '\U000022c3', + '\\<squnion>' : '\U00002294', + '\\<Squnion>' : '\U00002a06', + '\\<sqinter>' : '\U00002293', + '\\<Sqinter>' : '\U00002a05', + '\\<setminus>' : '\U00002216', + '\\<propto>' : '\U0000221d', + '\\<uplus>' : '\U0000228e', + '\\<Uplus>' : '\U00002a04', + '\\<noteq>' : '\U00002260', + '\\<sim>' : '\U0000223c', + '\\<doteq>' : '\U00002250', + '\\<simeq>' : '\U00002243', + '\\<approx>' : '\U00002248', + '\\<asymp>' : '\U0000224d', + '\\<cong>' : '\U00002245', + '\\<smile>' : '\U00002323', + '\\<equiv>' : '\U00002261', + '\\<frown>' : '\U00002322', + '\\<Join>' : '\U000022c8', + '\\<bowtie>' : '\U00002a1d', + '\\<prec>' : '\U0000227a', + '\\<succ>' : '\U0000227b', + '\\<preceq>' : '\U0000227c', + '\\<succeq>' : '\U0000227d', + '\\<parallel>' : '\U00002225', + '\\<bar>' : '\U000000a6', + '\\<plusminus>' : '\U000000b1', + '\\<minusplus>' : '\U00002213', + '\\<times>' : '\U000000d7', + '\\<div>' : '\U000000f7', + '\\<cdot>' : '\U000022c5', + '\\<star>' : '\U000022c6', + '\\<bullet>' : '\U00002219', + '\\<circ>' : '\U00002218', + '\\<dagger>' : '\U00002020', + '\\<ddagger>' : '\U00002021', + '\\<lhd>' : '\U000022b2', + '\\<rhd>' : '\U000022b3', + '\\<unlhd>' : '\U000022b4', + '\\<unrhd>' : '\U000022b5', + '\\<triangleleft>' : '\U000025c3', + '\\<triangleright>' : '\U000025b9', + '\\<triangle>' : '\U000025b3', + '\\<triangleq>' : '\U0000225c', + '\\<oplus>' : '\U00002295', + '\\<Oplus>' : '\U00002a01', + '\\<otimes>' : '\U00002297', + '\\<Otimes>' : '\U00002a02', + '\\<odot>' : '\U00002299', + '\\<Odot>' : '\U00002a00', + '\\<ominus>' : '\U00002296', + '\\<oslash>' : '\U00002298', + '\\<dots>' : '\U00002026', + '\\<cdots>' : '\U000022ef', + '\\<Sum>' : '\U00002211', + '\\<Prod>' : '\U0000220f', + '\\<Coprod>' : '\U00002210', + '\\<infinity>' : '\U0000221e', + '\\<integral>' : '\U0000222b', + '\\<ointegral>' : '\U0000222e', + '\\<clubsuit>' : '\U00002663', + '\\<diamondsuit>' : '\U00002662', + '\\<heartsuit>' : '\U00002661', + '\\<spadesuit>' : '\U00002660', + '\\<aleph>' : '\U00002135', + '\\<emptyset>' : '\U00002205', + '\\<nabla>' : '\U00002207', + '\\<partial>' : '\U00002202', + '\\<flat>' : '\U0000266d', + '\\<natural>' : '\U0000266e', + '\\<sharp>' : '\U0000266f', + '\\<angle>' : '\U00002220', + '\\<copyright>' : '\U000000a9', + '\\<registered>' : '\U000000ae', + '\\<hyphen>' : '\U000000ad', + '\\<inverse>' : '\U000000af', + '\\<onequarter>' : '\U000000bc', + '\\<onehalf>' : '\U000000bd', + '\\<threequarters>' : '\U000000be', + '\\<ordfeminine>' : '\U000000aa', + '\\<ordmasculine>' : '\U000000ba', + '\\<section>' : '\U000000a7', + '\\<paragraph>' : '\U000000b6', + '\\<exclamdown>' : '\U000000a1', + '\\<questiondown>' : '\U000000bf', + '\\<euro>' : '\U000020ac', + '\\<pounds>' : '\U000000a3', + '\\<yen>' : '\U000000a5', + '\\<cent>' : '\U000000a2', + '\\<currency>' : '\U000000a4', + '\\<degree>' : '\U000000b0', + '\\<amalg>' : '\U00002a3f', + '\\<mho>' : '\U00002127', + '\\<lozenge>' : '\U000025ca', + '\\<wp>' : '\U00002118', + '\\<wrong>' : '\U00002240', + '\\<struct>' : '\U000022c4', + '\\<acute>' : '\U000000b4', + '\\<index>' : '\U00000131', + '\\<dieresis>' : '\U000000a8', + '\\<cedilla>' : '\U000000b8', + '\\<hungarumlaut>' : '\U000002dd', + '\\<some>' : '\U000003f5', + '\\<newline>' : '\U000023ce', + '\\<open>' : '\U00002039', + '\\<close>' : '\U0000203a', + '\\<here>' : '\U00002302', + '\\<^sub>' : '\U000021e9', + '\\<^sup>' : '\U000021e7', + '\\<^bold>' : '\U00002759', + '\\<^bsub>' : '\U000021d8', + '\\<^esub>' : '\U000021d9', + '\\<^bsup>' : '\U000021d7', + '\\<^esup>' : '\U000021d6', + } + + lang_map = {'isabelle' : isabelle_symbols, 'latex' : latex_symbols} + + def __init__(self, **options): + Filter.__init__(self, **options) + lang = get_choice_opt(options, 'lang', + ['isabelle', 'latex'], 'isabelle') + self.symbols = self.lang_map[lang] + + def filter(self, lexer, stream): + for ttype, value in stream: + if value in self.symbols: + yield ttype, self.symbols[value] + else: + yield ttype, value + + +class KeywordCaseFilter(Filter): + """Convert keywords to lowercase or uppercase or capitalize them, which + means first letter uppercase, rest lowercase. + + This can be useful e.g. if you highlight Pascal code and want to adapt the + code to your styleguide. + + Options accepted: + + `case` : string + The casing to convert keywords to. Must be one of ``'lower'``, + ``'upper'`` or ``'capitalize'``. The default is ``'lower'``. + """ + + def __init__(self, **options): + Filter.__init__(self, **options) + case = get_choice_opt(options, 'case', + ['lower', 'upper', 'capitalize'], 'lower') + self.convert = getattr(str, case) + + def filter(self, lexer, stream): + for ttype, value in stream: + if ttype in Keyword: + yield ttype, self.convert(value) + else: + yield ttype, value + + +class NameHighlightFilter(Filter): + """Highlight a normal Name (and Name.*) token with a different token type. + + Example:: + + filter = NameHighlightFilter( + names=['foo', 'bar', 'baz'], + tokentype=Name.Function, + ) + + This would highlight the names "foo", "bar" and "baz" + as functions. `Name.Function` is the default token type. + + Options accepted: + + `names` : list of strings + A list of names that should be given the different token type. + There is no default. + `tokentype` : TokenType or string + A token type or a string containing a token type name that is + used for highlighting the strings in `names`. The default is + `Name.Function`. + """ + + def __init__(self, **options): + Filter.__init__(self, **options) + self.names = set(get_list_opt(options, 'names', [])) + tokentype = options.get('tokentype') + if tokentype: + self.tokentype = string_to_tokentype(tokentype) + else: + self.tokentype = Name.Function + + def filter(self, lexer, stream): + for ttype, value in stream: + if ttype in Name and value in self.names: + yield self.tokentype, value + else: + yield ttype, value + + +class ErrorToken(Exception): + pass + + +class RaiseOnErrorTokenFilter(Filter): + """Raise an exception when the lexer generates an error token. + + Options accepted: + + `excclass` : Exception class + The exception class to raise. + The default is `pygments.filters.ErrorToken`. + + .. versionadded:: 0.8 + """ + + def __init__(self, **options): + Filter.__init__(self, **options) + self.exception = options.get('excclass', ErrorToken) + try: + # issubclass() will raise TypeError if first argument is not a class + if not issubclass(self.exception, Exception): + raise TypeError + except TypeError: + raise OptionError('excclass option is not an exception class') + + def filter(self, lexer, stream): + for ttype, value in stream: + if ttype is Error: + raise self.exception(value) + yield ttype, value + + +class VisibleWhitespaceFilter(Filter): + """Convert tabs, newlines and/or spaces to visible characters. + + Options accepted: + + `spaces` : string or bool + If this is a one-character string, spaces will be replaces by this string. + If it is another true value, spaces will be replaced by ``·`` (unicode + MIDDLE DOT). If it is a false value, spaces will not be replaced. The + default is ``False``. + `tabs` : string or bool + The same as for `spaces`, but the default replacement character is ``»`` + (unicode RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK). The default value + is ``False``. Note: this will not work if the `tabsize` option for the + lexer is nonzero, as tabs will already have been expanded then. + `tabsize` : int + If tabs are to be replaced by this filter (see the `tabs` option), this + is the total number of characters that a tab should be expanded to. + The default is ``8``. + `newlines` : string or bool + The same as for `spaces`, but the default replacement character is ``¶`` + (unicode PILCROW SIGN). The default value is ``False``. + `wstokentype` : bool + If true, give whitespace the special `Whitespace` token type. This allows + styling the visible whitespace differently (e.g. greyed out), but it can + disrupt background colors. The default is ``True``. + + .. versionadded:: 0.8 + """ + + def __init__(self, **options): + Filter.__init__(self, **options) + for name, default in [('spaces', '·'), + ('tabs', '»'), + ('newlines', '¶')]: + opt = options.get(name, False) + if isinstance(opt, str) and len(opt) == 1: + setattr(self, name, opt) + else: + setattr(self, name, (opt and default or '')) + tabsize = get_int_opt(options, 'tabsize', 8) + if self.tabs: + self.tabs += ' ' * (tabsize - 1) + if self.newlines: + self.newlines += '\n' + self.wstt = get_bool_opt(options, 'wstokentype', True) + + def filter(self, lexer, stream): + if self.wstt: + spaces = self.spaces or ' ' + tabs = self.tabs or '\t' + newlines = self.newlines or '\n' + regex = re.compile(r'\s') + + def replacefunc(wschar): + if wschar == ' ': + return spaces + elif wschar == '\t': + return tabs + elif wschar == '\n': + return newlines + return wschar + + for ttype, value in stream: + yield from _replace_special(ttype, value, regex, Whitespace, + replacefunc) + else: + spaces, tabs, newlines = self.spaces, self.tabs, self.newlines + # simpler processing + for ttype, value in stream: + if spaces: + value = value.replace(' ', spaces) + if tabs: + value = value.replace('\t', tabs) + if newlines: + value = value.replace('\n', newlines) + yield ttype, value + + +class GobbleFilter(Filter): + """Gobbles source code lines (eats initial characters). + + This filter drops the first ``n`` characters off every line of code. This + may be useful when the source code fed to the lexer is indented by a fixed + amount of space that isn't desired in the output. + + Options accepted: + + `n` : int + The number of characters to gobble. + + .. versionadded:: 1.2 + """ + def __init__(self, **options): + Filter.__init__(self, **options) + self.n = get_int_opt(options, 'n', 0) + + def gobble(self, value, left): + if left < len(value): + return value[left:], 0 + else: + return '', left - len(value) + + def filter(self, lexer, stream): + n = self.n + left = n # How many characters left to gobble. + for ttype, value in stream: + # Remove ``left`` tokens from first line, ``n`` from all others. + parts = value.split('\n') + (parts[0], left) = self.gobble(parts[0], left) + for i in range(1, len(parts)): + (parts[i], left) = self.gobble(parts[i], n) + value = '\n'.join(parts) + + if value != '': + yield ttype, value + + +class TokenMergeFilter(Filter): + """Merges consecutive tokens with the same token type in the output + stream of a lexer. + + .. versionadded:: 1.2 + """ + def __init__(self, **options): + Filter.__init__(self, **options) + + def filter(self, lexer, stream): + current_type = None + current_value = None + for ttype, value in stream: + if ttype is current_type: + current_value += value + else: + if current_type is not None: + yield current_type, current_value + current_type = ttype + current_value = value + if current_type is not None: + yield current_type, current_value + + +FILTERS = { + 'codetagify': CodeTagFilter, + 'keywordcase': KeywordCaseFilter, + 'highlight': NameHighlightFilter, + 'raiseonerror': RaiseOnErrorTokenFilter, + 'whitespace': VisibleWhitespaceFilter, + 'gobble': GobbleFilter, + 'tokenmerge': TokenMergeFilter, + 'symbols': SymbolFilter, +} diff --git a/virt/lib/python3.9/site-packages/pip/_vendor/pygments/formatters/html 3.py b/virt/lib/python3.9/site-packages/pip/_vendor/pygments/formatters/html 3.py new file mode 100644 index 00000000..d22d50f6 --- /dev/null +++ b/virt/lib/python3.9/site-packages/pip/_vendor/pygments/formatters/html 3.py @@ -0,0 +1,989 @@ +""" + pygments.formatters.html + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Formatter for HTML output. + + :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import functools +import os +import sys +import os.path +from io import StringIO + +from pip._vendor.pygments.formatter import Formatter +from pip._vendor.pygments.token import Token, Text, STANDARD_TYPES +from pip._vendor.pygments.util import get_bool_opt, get_int_opt, get_list_opt + +try: + import ctags +except ImportError: + ctags = None + +__all__ = ['HtmlFormatter'] + + +_escape_html_table = { + ord('&'): '&', + ord('<'): '<', + ord('>'): '>', + ord('"'): '"', + ord("'"): ''', +} + + +def escape_html(text, table=_escape_html_table): + """Escape &, <, > as well as single and double quotes for HTML.""" + return text.translate(table) + + +def webify(color): + if color.startswith('calc') or color.startswith('var'): + return color + else: + return '#' + color + + +def _get_ttype_class(ttype): + fname = STANDARD_TYPES.get(ttype) + if fname: + return fname + aname = '' + while fname is None: + aname = '-' + ttype[-1] + aname + ttype = ttype.parent + fname = STANDARD_TYPES.get(ttype) + return fname + aname + + +CSSFILE_TEMPLATE = '''\ +/* +generated by Pygments <https://pygments.org/> +Copyright 2006-2023 by the Pygments team. +Licensed under the BSD license, see LICENSE for details. +*/ +%(styledefs)s +''' + +DOC_HEADER = '''\ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<!-- +generated by Pygments <https://pygments.org/> +Copyright 2006-2023 by the Pygments team. +Licensed under the BSD license, see LICENSE for details. +--> +<html> +<head> + <title>%(title)s + + + + +

%(title)s

+ +''' + +DOC_HEADER_EXTERNALCSS = '''\ + + + + + %(title)s + + + + +

%(title)s

+ +''' + +DOC_FOOTER = '''\ + + +''' + + +class HtmlFormatter(Formatter): + r""" + Format tokens as HTML 4 ```` tags. By default, the content is enclosed + in a ``
`` tag, itself wrapped in a ``
`` tag (but see the `nowrap` option). + The ``
``'s CSS class can be set by the `cssclass` option. + + If the `linenos` option is set to ``"table"``, the ``
`` is
+    additionally wrapped inside a ```` which has one row and two
+    cells: one containing the line numbers and one containing the code.
+    Example:
+
+    .. sourcecode:: html
+
+        
+
+ + +
+
1
+            2
+
+
def foo(bar):
+              pass
+            
+
+ + (whitespace added to improve clarity). + + A list of lines can be specified using the `hl_lines` option to make these + lines highlighted (as of Pygments 0.11). + + With the `full` option, a complete HTML 4 document is output, including + the style definitions inside a ``