diff --git a/.gitignore b/.gitignore index 115a395..8eee4e7 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ __pycache__/ # Distribution / packaging .Python -env/ build/ develop-eggs/ dist/ @@ -20,9 +19,12 @@ lib64/ parts/ sdist/ var/ +wheels/ +share/python-wheels/ *.egg-info/ .installed.cfg *.egg +MANIFEST # PyInstaller # Usually these files are written by a python script from a template @@ -37,12 +39,17 @@ pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ +.nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml -*,cover +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ # Translations *.mo @@ -50,12 +57,112 @@ coverage.xml # Django stuff: *.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy # Sphinx documentation docs/_build/ # PyBuilder +.pybuilder/ target/ -# VS Code -.vscode/ +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ + +# Visual Studio Code +.vscode + +# ruff (linter) stuff +.ruff_cache diff --git a/.travis.yml b/.travis.yml index c0d488a..67dd3d3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,12 @@ language: python sudo: false python: -- 2.7 -- 3.4 -- 3.5 -- 3.6 -- 3.7 -- 3.8 - 3.9 -- 3.10-dev +- 3.10 +- 3.11 +- 3.12 install: -- pip install python-dateutil>=2.4.0 six>=1.9.0 -- if [[ $TRAVIS_PYTHON_VERSION == 2.6 ]]; then pip install unittest2; fi +- pip install python-dateutil>=2.4.0 +- pip install pytest>=8 script: -- python test.py +- python3 -m pytest diff --git a/bson/codec.py b/bson/codec.py index 6aa7e07..d9e17d4 100644 --- a/bson/codec.py +++ b/bson/codec.py @@ -6,40 +6,40 @@ """ Base codec functions for bson. """ + import struct import warnings -from datetime import datetime from abc import ABCMeta, abstractmethod -from uuid import UUID +from datetime import datetime from decimal import Decimal +from io import BytesIO as StringIO +from uuid import UUID from bson.types import UInt64, Int64, Int32 -try: - from io import BytesIO as StringIO -except ImportError: - from cStringIO import StringIO - import calendar from dateutil.tz import tzutc from binascii import b2a_hex from six import integer_types, iterkeys, text_type, PY3 -from six.moves import xrange utc = tzutc() + class MissingClassDefinition(ValueError): def __init__(self, class_name): - super(MissingClassDefinition, - self).__init__("No class definition for class %s" % (class_name,)) + super(MissingClassDefinition, self).__init__( + "No class definition for class %s" % (class_name,) + ) class UnknownSerializerError(ValueError): def __init__(self, key, value): - super(UnknownSerializerError, - self).__init__("Unable to serialize: key '%s' value: %s type: %s" % (key,value, type(value))) + super(UnknownSerializerError, self).__init__( + "Unable to serialize: key '%s' value: %s type: %s" + % (key, value, type(value)) + ) class MissingTimezoneWarning(RuntimeWarning): @@ -95,15 +95,24 @@ def encode_object(obj, traversal_stack, generator_func, on_unknown=None): values = obj.bson_encode() class_name = obj.__class__.__name__ values["$$__CLASS_NAME__$$"] = class_name - return encode_document(values, traversal_stack, obj, - generator_func, on_unknown) - - -def encode_object_element(name, value, traversal_stack, - generator_func, on_unknown): - return b"\x03" + encode_cstring(name) + \ - encode_object(value, traversal_stack, - generator_func=generator_func, on_unknown=on_unknown) + return encode_document( + values, traversal_stack, obj, generator_func, on_unknown + ) + + +def encode_object_element( + name, value, traversal_stack, generator_func, on_unknown +): + return ( + b"\x03" + + encode_cstring(name) + + encode_object( + value, + traversal_stack, + generator_func=generator_func, + on_unknown=on_unknown, + ) + ) class _EmptyClass(object): @@ -161,7 +170,7 @@ def encode_double(value): 0x0A: "none", 0x10: "int32", 0x11: "uint64", - 0x12: "int64" + 0x12: "int64", } @@ -173,28 +182,20 @@ def encode_string_element(name, value): return b"\x02" + encode_cstring(name) + encode_string(value) -def _is_string(value): - if isinstance(value, text_type): - return True - elif isinstance(value, str) or isinstance(value, bytes): - try: - unicode(value, errors='strict') - return True - except: - pass - return False - - -def encode_value(name, value, buf, traversal_stack, - generator_func, on_unknown=None): +def encode_value( + name, value, buf, traversal_stack, generator_func, on_unknown=None +): if isinstance(value, bool): buf.write(encode_boolean_element(name, value)) elif isinstance(value, integer_types): - if value < -0x80000000 or 0x7FFFFFFFFFFFFFFF >= value > 0x7fffffff: + if value < -0x80000000 or 0x7FFFFFFFFFFFFFFF >= value > 0x7FFFFFFF: buf.write(encode_int64_element(name, value)) elif value > 0x7FFFFFFFFFFFFFFF: if value > 0xFFFFFFFFFFFFFFFF: - raise Exception("BSON format supports only int value < %s" % 0xFFFFFFFFFFFFFFFF) + raise Exception( + "BSON format supports only int value < %s" + % 0xFFFFFFFFFFFFFFFF + ) buf.write(encode_uint64_element(name, value)) else: buf.write(encode_int32_element(name, value)) @@ -206,7 +207,7 @@ def encode_value(name, value, buf, traversal_stack, buf.write(encode_uint64_element(name, value.get_value())) elif isinstance(value, float): buf.write(encode_double_element(name, value)) - elif _is_string(value): + elif isinstance(value, str): buf.write(encode_string_element(name, value)) elif isinstance(value, str) or isinstance(value, bytes): buf.write(encode_binary_element(name, value)) @@ -217,26 +218,46 @@ def encode_value(name, value, buf, traversal_stack, elif value is None: buf.write(encode_none_element(name, value)) elif isinstance(value, dict): - buf.write(encode_document_element(name, value, traversal_stack, - generator_func, on_unknown)) + buf.write( + encode_document_element( + name, value, traversal_stack, generator_func, on_unknown + ) + ) elif isinstance(value, list) or isinstance(value, tuple): - buf.write(encode_array_element(name, value, traversal_stack, - generator_func, on_unknown)) + buf.write( + encode_array_element( + name, value, traversal_stack, generator_func, on_unknown + ) + ) elif isinstance(value, BSONCoding): - buf.write(encode_object_element(name, value, traversal_stack, - generator_func, on_unknown)) + buf.write( + encode_object_element( + name, value, traversal_stack, generator_func, on_unknown + ) + ) elif isinstance(value, Decimal): buf.write(encode_double_element(name, float(value))) else: if on_unknown is not None: - encode_value(name, on_unknown(value), buf, traversal_stack, - generator_func, on_unknown) + encode_value( + name, + on_unknown(value), + buf, + traversal_stack, + generator_func, + on_unknown, + ) else: raise UnknownSerializerError(name, value) -def encode_document(obj, traversal_stack, traversal_parent=None, - generator_func=None, on_unknown=None): +def encode_document( + obj, + traversal_stack, + traversal_parent=None, + generator_func=None, + on_unknown=None, +): buf = StringIO() key_iter = iterkeys(obj) if generator_func is not None: @@ -244,28 +265,37 @@ def encode_document(obj, traversal_stack, traversal_parent=None, for name in key_iter: value = obj[name] traversal_stack.append(TraversalStep(traversal_parent or obj, name)) - encode_value(name, value, buf, traversal_stack, - generator_func, on_unknown) + encode_value( + name, value, buf, traversal_stack, generator_func, on_unknown + ) traversal_stack.pop() e_list = buf.getvalue() e_list_length = len(e_list) - return struct.pack(" 2 ** 31 - 1: - raise ValueError('value {} cannot be represented in int32'.format(value)) + if value < -(2**31) or value > 2**31 - 1: + raise ValueError(f"{value} cannot be represented in int32") self._value = value def get_value(self): @@ -16,13 +14,11 @@ def __str__(self): class Int64: - """ - A signed integer with a 64-bit fixed width. - """ + """A signed integer with a 64-bit fixed width.""" def __init__(self, value): - if value < -2 ** 63 or value > 2 ** 63 - 1: - raise ValueError('value {} cannot be represented in int32'.format(value)) + if value < -(2**63) or value > 2**63 - 1: + raise ValueError(f"{value} cannot be represented in int64") self._value = value def get_value(self): @@ -33,13 +29,11 @@ def __str__(self): class UInt64: - """ - An unsigned integer with a 64-bit fixed width. - """ + """An unsigned integer with a 64-bit fixed width.""" def __init__(self, value): - if value < 0 or value > 2 ** 64 - 1: - raise ValueError('value {} cannot be represented in uint32'.format(value)) + if value < 0 or value > 2**64 - 1: + raise ValueError(f"{value} cannot be represented in uint64") self._value = value def get_value(self): diff --git a/pdm.lock b/pdm.lock new file mode 100644 index 0000000..2dc88ae --- /dev/null +++ b/pdm.lock @@ -0,0 +1,236 @@ +# This file is @generated by PDM. +# It is not intended for manual editing. + +[metadata] +groups = ["default", "lint", "test"] +strategy = ["inherit_metadata"] +lock_version = "4.5.0" +content_hash = "sha256:203aa87d8afd8bbafdb7f9d8061a703c179d3d706cce38cf1110e61535a6aacd" + +[[metadata.targets]] +requires_python = ">=3.9" + +[[package]] +name = "colorama" +version = "0.4.6" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +summary = "Cross-platform colored terminal text." +groups = ["test"] +marker = "sys_platform == \"win32\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +requires_python = ">=3.7" +summary = "Backport of PEP 654 (exception groups)" +groups = ["test"] +marker = "python_version < \"3.11\"" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +requires_python = ">=3.7" +summary = "brain-dead simple config-ini parsing" +groups = ["test"] +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "mypy" +version = "1.13.0" +requires_python = ">=3.8" +summary = "Optional static typing for Python" +groups = ["lint"] +dependencies = [ + "mypy-extensions>=1.0.0", + "tomli>=1.1.0; python_version < \"3.11\"", + "typing-extensions>=4.6.0", +] +files = [ + {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, + {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, + {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, + {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, + {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, + {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, + {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, + {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, + {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, + {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, + {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, + {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, + {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, + {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, + {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, + {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, + {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, + {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +requires_python = ">=3.5" +summary = "Type system extensions for programs checked with the mypy type checker." +groups = ["lint"] +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "packaging" +version = "24.1" +requires_python = ">=3.8" +summary = "Core utilities for Python packages" +groups = ["test"] +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +requires_python = ">=3.8" +summary = "plugin and hook calling mechanisms for python" +groups = ["test"] +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[[package]] +name = "pytest" +version = "8.3.3" +requires_python = ">=3.8" +summary = "pytest: simple powerful testing with Python" +groups = ["test"] +dependencies = [ + "colorama; sys_platform == \"win32\"", + "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", + "iniconfig", + "packaging", + "pluggy<2,>=1.5", + "tomli>=1; python_version < \"3.11\"", +] +files = [ + {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, + {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +summary = "Extensions to the standard Python datetime module" +groups = ["default"] +dependencies = [ + "six>=1.5", +] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[[package]] +name = "ruff" +version = "0.7.2" +requires_python = ">=3.7" +summary = "An extremely fast Python linter and code formatter, written in Rust." +groups = ["lint"] +files = [ + {file = "ruff-0.7.2-py3-none-linux_armv6l.whl", hash = "sha256:b73f873b5f52092e63ed540adefc3c36f1f803790ecf2590e1df8bf0a9f72cb8"}, + {file = "ruff-0.7.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5b813ef26db1015953daf476202585512afd6a6862a02cde63f3bafb53d0b2d4"}, + {file = "ruff-0.7.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:853277dbd9675810c6826dad7a428d52a11760744508340e66bf46f8be9701d9"}, + {file = "ruff-0.7.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21aae53ab1490a52bf4e3bf520c10ce120987b047c494cacf4edad0ba0888da2"}, + {file = "ruff-0.7.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ccc7e0fc6e0cb3168443eeadb6445285abaae75142ee22b2b72c27d790ab60ba"}, + {file = "ruff-0.7.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd77877a4e43b3a98e5ef4715ba3862105e299af0c48942cc6d51ba3d97dc859"}, + {file = "ruff-0.7.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e00163fb897d35523c70d71a46fbaa43bf7bf9af0f4534c53ea5b96b2e03397b"}, + {file = "ruff-0.7.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3c54b538633482dc342e9b634d91168fe8cc56b30a4b4f99287f4e339103e88"}, + {file = "ruff-0.7.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b792468e9804a204be221b14257566669d1db5c00d6bb335996e5cd7004ba80"}, + {file = "ruff-0.7.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dba53ed84ac19ae4bfb4ea4bf0172550a2285fa27fbb13e3746f04c80f7fa088"}, + {file = "ruff-0.7.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b19fafe261bf741bca2764c14cbb4ee1819b67adb63ebc2db6401dcd652e3748"}, + {file = "ruff-0.7.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:28bd8220f4d8f79d590db9e2f6a0674f75ddbc3847277dd44ac1f8d30684b828"}, + {file = "ruff-0.7.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9fd67094e77efbea932e62b5d2483006154794040abb3a5072e659096415ae1e"}, + {file = "ruff-0.7.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:576305393998b7bd6c46018f8104ea3a9cb3fa7908c21d8580e3274a3b04b691"}, + {file = "ruff-0.7.2-py3-none-win32.whl", hash = "sha256:fa993cfc9f0ff11187e82de874dfc3611df80852540331bc85c75809c93253a8"}, + {file = "ruff-0.7.2-py3-none-win_amd64.whl", hash = "sha256:dd8800cbe0254e06b8fec585e97554047fb82c894973f7ff18558eee33d1cb88"}, + {file = "ruff-0.7.2-py3-none-win_arm64.whl", hash = "sha256:bb8368cd45bba3f57bb29cbb8d64b4a33f8415d0149d2655c5c8539452ce7760"}, + {file = "ruff-0.7.2.tar.gz", hash = "sha256:2b14e77293380e475b4e3a7a368e14549288ed2931fce259a6f99978669e844f"}, +] + +[[package]] +name = "six" +version = "1.16.0" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +summary = "Python 2 and 3 compatibility utilities" +groups = ["default"] +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "tomli" +version = "2.0.2" +requires_python = ">=3.8" +summary = "A lil' TOML parser" +groups = ["lint", "test"] +marker = "python_version < \"3.11\"" +files = [ + {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, + {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, +] + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20241003" +requires_python = ">=3.8" +summary = "Typing stubs for python-dateutil" +groups = ["lint"] +files = [ + {file = "types-python-dateutil-2.9.0.20241003.tar.gz", hash = "sha256:58cb85449b2a56d6684e41aeefb4c4280631246a0da1a719bdbe6f3fb0317446"}, + {file = "types_python_dateutil-2.9.0.20241003-py3-none-any.whl", hash = "sha256:250e1d8e80e7bbc3a6c99b907762711d1a1cdd00e978ad39cb5940f6f0a87f3d"}, +] + +[[package]] +name = "types-six" +version = "1.16.21.20241105" +requires_python = ">=3.8" +summary = "Typing stubs for six" +groups = ["lint"] +files = [ + {file = "types-six-1.16.21.20241105.tar.gz", hash = "sha256:ce3534c38079ec3242f4a20376283eb265a3837f80592b0ecacb14bd41acc29e"}, + {file = "types_six-1.16.21.20241105-py3-none-any.whl", hash = "sha256:8b4b29e5c8fe7f1131be8f3cb7cedbcd8bb889707336f32c3fb332c9b1c71991"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +requires_python = ">=3.8" +summary = "Backported and Experimental Type Hints for Python 3.8+" +groups = ["lint"] +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9f53bd8 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,44 @@ +[project] +name = "bson" +version = "0.5.10" +description = "BSON codec for Python" +authors = [ + {name = "Ayun Park", email = "iamparkayun@gmail.com"}, +] +dependencies = ["python-dateutil>=2.4.0"] +requires-python = ">=3.9" +readme = "README.rst" +license = {text = "BSD"} +classifiers=[ + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] + +[dependency-groups] +test = ["pytest>=8.3.3"] +lint = ["mypy>=1.13.0", "ruff>=0.7.2", "types-six>=1.16.21.20241105", "types-python-dateutil>=2.9.0.20241003"] + +[tool.pytest.ini_options] +minversion = "8.0" +addopts = [ + "--maxfail=1", + "--strict-config", + "--strict-markers", +] +xfail_strict = true +testpaths = ["tests"] + +[tool.ruff] +line-length = 80 +respect-gitignore = true +target-version = "py39" + +[tool.pdm] +distribution = true + +[build-system] +requires = ["pdm-backend"] +build-backend = "pdm.backend" diff --git a/setup.py b/setup.py deleted file mode 100755 index 74d5002..0000000 --- a/setup.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf8 shiftwidth=4 tabstop=4 textwidth=80 foldmethod=marker : -# Copyright (c) 2010, Kou Man Tong. All rights reserved. -# Copyright (c) 2015, Ayun Park. All rights reserved. -# For licensing, see LICENSE file included in the package. -import sys -import pkgutil -from setuptools import setup -from setuptools.command.install import install - - -class NewInstall(install): - - @staticmethod - def check_pymongo(): - if pkgutil.find_loader('pymongo'): - return True - return False - - def run(self): - install.run(self) - if self.check_pymongo(): - sys.stdout.write('\033[31mCaution! \033[33mbson(pymongo) is already installed.\033[0m\n') - - -setup( - name="bson", - version="0.5.10", - packages=["bson"], - install_requires=["python-dateutil>=2.4.0", "six>=1.9.0"], - author="Ayun Park", - author_email="iamparkayun@gmail.com", - description="BSON codec for Python", - long_description="""Independent BSON codec for Python that doesn't depend on MongoDB.""", - platforms="Any", - license="BSD", - keywords="BSON codec", - url="http://github.com/py-bson/bson", - classifiers=[ - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - ], - cmdclass={'install': NewInstall} -) diff --git a/test.py b/test.py deleted file mode 100755 index d01db74..0000000 --- a/test.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf8 shiftwidth=4 tabstop=4 textwidth=80 foldmethod=marker : -# Copyright (c) 2011, Kou Man Tong. All rights reserved. -# For licensing, see LICENSE file included in the package. - -import bson.tests - -if __name__ == "__main__": - bson.tests.main() diff --git a/bson/tests/__init__.py b/tests/__init__.py similarity index 100% rename from bson/tests/__init__.py rename to tests/__init__.py diff --git a/bson/tests/test_array.py b/tests/test_array.py similarity index 100% rename from bson/tests/test_array.py rename to tests/test_array.py diff --git a/bson/tests/test_binary.py b/tests/test_binary.py similarity index 100% rename from bson/tests/test_binary.py rename to tests/test_binary.py diff --git a/bson/tests/test_boolean.py b/tests/test_boolean.py similarity index 100% rename from bson/tests/test_boolean.py rename to tests/test_boolean.py diff --git a/bson/tests/test_datetime.py b/tests/test_datetime.py similarity index 100% rename from bson/tests/test_datetime.py rename to tests/test_datetime.py diff --git a/bson/tests/test_decimal.py b/tests/test_decimal.py similarity index 100% rename from bson/tests/test_decimal.py rename to tests/test_decimal.py diff --git a/bson/tests/test_int.py b/tests/test_int.py similarity index 100% rename from bson/tests/test_int.py rename to tests/test_int.py diff --git a/bson/tests/test_int_as_key.py b/tests/test_int_as_key.py similarity index 100% rename from bson/tests/test_int_as_key.py rename to tests/test_int_as_key.py diff --git a/bson/tests/test_non_utf8_binary.py b/tests/test_non_utf8_binary.py similarity index 100% rename from bson/tests/test_non_utf8_binary.py rename to tests/test_non_utf8_binary.py diff --git a/bson/tests/test_object.py b/tests/test_object.py similarity index 84% rename from bson/tests/test_object.py rename to tests/test_object.py index 2471e9c..9683b96 100644 --- a/bson/tests/test_object.py +++ b/tests/test_object.py @@ -4,7 +4,7 @@ from bson import BSONCoding, dumps, loads, import_class -class TestData(BSONCoding): +class _TestData(BSONCoding): def __init__(self, *args): self.args = list(args) self.nested = None @@ -17,7 +17,7 @@ def bson_init(self, raw_values): self.nested = raw_values["nested"] def __eq__(self, other): - if not isinstance(other, TestData): + if not isinstance(other, _TestData): return NotImplemented if self.args != other.args: return False @@ -31,12 +31,12 @@ def __ne__(self, other): class TestObjectCoding(TestCase): def test_codec(self): - import_class(TestData) - data = TestData(u"Lorem ipsum dolor sit amet", + import_class(_TestData) + data = _TestData(u"Lorem ipsum dolor sit amet", "consectetur adipisicing elit", 42) - data2 = TestData(u"She's got both hands in her pockets", + data2 = _TestData(u"She's got both hands in her pockets", "and she won't look at you won't look at you eh", 66, 23.54, diff --git a/bson/tests/test_objectid.py b/tests/test_objectid.py similarity index 100% rename from bson/tests/test_objectid.py rename to tests/test_objectid.py diff --git a/bson/tests/test_random_tree.py b/tests/test_random_tree.py similarity index 100% rename from bson/tests/test_random_tree.py rename to tests/test_random_tree.py diff --git a/bson/tests/test_types.py b/tests/test_types.py similarity index 100% rename from bson/tests/test_types.py rename to tests/test_types.py diff --git a/bson/tests/test_unknown_handler.py b/tests/test_unknown_handler.py similarity index 100% rename from bson/tests/test_unknown_handler.py rename to tests/test_unknown_handler.py diff --git a/bson/tests/test_uuid.py b/tests/test_uuid.py similarity index 100% rename from bson/tests/test_uuid.py rename to tests/test_uuid.py