Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PYTHON-4451 Use Hatch as Build Backend #1644

Merged
merged 24 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .evergreen/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ if [ -n "$TEST_ENCRYPTION" ] || [ -n "$TEST_FLE_AZURE_AUTO" ] || [ -n "$TEST_FLE
if [ ! -d "libmongocrypt_git" ]; then
git clone https://github.com/mongodb/libmongocrypt.git libmongocrypt_git
fi
python -m pip install -U setuptools
python -m pip install ./libmongocrypt_git/bindings/python
python -c "import pymongocrypt; print('pymongocrypt version: '+pymongocrypt.__version__)"
python -c "import pymongocrypt; print('libmongocrypt version: '+pymongocrypt.libmongocrypt_version())"
Expand Down
2 changes: 1 addition & 1 deletion .evergreen/utils.sh
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ createvirtualenv () {

export PIP_QUIET=1
python -m pip install --upgrade pip
python -m pip install --upgrade setuptools tox
python -m pip install --upgrade tox
}

# Usage:
Expand Down
6 changes: 2 additions & 4 deletions .github/workflows/test-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,10 @@ jobs:
- name: Run linters
run: |
tox -m lint-manual
- name: Check Manifest
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No manifest file!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read the Why Hatch? page. I'm starting to get it. Very nice.

run: |
tox -m manifest
- name: Run compilation
run: |
pip install -e .
export PYMONGO_C_EXT_MUST_BUILD=1
pip install -v -e .
python tools/fail_if_no_c.py
- name: Run typecheck
run: |
Expand Down
32 changes: 0 additions & 32 deletions MANIFEST.in

This file was deleted.

6 changes: 0 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,6 @@ PyMongo can be installed with [pip](http://pypi.python.org/pypi/pip):
python -m pip install pymongo
```

Or `easy_install` from [setuptools](http://pypi.python.org/pypi/setuptools):

```bash
python -m easy_install pymongo
```

You can also download the project source and do:

```bash
Expand Down
36 changes: 36 additions & 0 deletions hatch_build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""A custom hatch build hook for pymongo."""
from __future__ import annotations

import os
import subprocess
import sys
from pathlib import Path

from hatchling.builders.hooks.plugin.interface import BuildHookInterface


class CustomHook(BuildHookInterface):
"""The pymongo build hook."""

def initialize(self, version, build_data):
"""Initialize the hook."""
if self.target_name == "sdist":
return
here = Path(__file__).parent.resolve()
sys.path.insert(0, str(here))

subprocess.check_call([sys.executable, "setup.py", "build_ext", "-i"])

# Ensure wheel is marked as binary and contains the binary files.
build_data["infer_tag"] = True
build_data["pure_python"] = False
if os.name == "nt":
patt = ".pyd"
else:
patt = ".so"
for pkg in ["bson", "pymongo"]:
dpath = here / pkg
for fpath in dpath.glob(f"*{patt}"):
relpath = os.path.relpath(fpath, here)
build_data["artifacts"].append(relpath)
build_data["force_include"][relpath] = relpath
24 changes: 15 additions & 9 deletions pymongo/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,22 @@
"""Current version of PyMongo."""
from __future__ import annotations

from typing import Tuple, Union
import re
from typing import List, Tuple, Union

version_tuple: Tuple[Union[int, str], ...] = (4, 8, 0, ".dev0")
__version__ = "4.8.0.dev1"


def get_version_string() -> str:
if isinstance(version_tuple[-1], str):
return ".".join(map(str, version_tuple[:-1])) + version_tuple[-1]
return ".".join(map(str, version_tuple))
pattern = r"(?P<major>\d+).(?P<minor>\d+).(?P<patch>\d+)(?P<rest>.*)"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this a function and add a few unit tests to make sure it works with a variety of versions? Eg "4.8.0.dev1", "4.8.0", "5.0", etc...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. We don't support "5.0", it has to have the patch version.

Copy link
Member

@ShaneHarvey ShaneHarvey Jun 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think not supporting "5.0" is a mistake. Our 4.0 release used "4.0" for example, same for "3.0". If we don't support "5.0" then I worry we'll accidentally set the version to a 2 part at some point and lead to a bug where version_tuple is silently set to an empty tuple.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

match = re.match(pattern, __version__)
if match:
parts: List[Union[int, str]] = [int(match[part]) for part in ["major", "minor", "patch"]]
if match["rest"]:
parts.append(match["rest"])
else:
parts = []
version_tuple: Tuple[Union[int, str], ...] = tuple(parts)
version = __version__


__version__: str = get_version_string()
version = __version__
def get_version_string() -> str:
return __version__
32 changes: 22 additions & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[build-system]
requires = ["setuptools>=63.0"]
build-backend = "setuptools.build_meta"
requires = ["hatchling>1.24","setuptools>=65.0","hatch-requirements-txt>=0.4.1"]
build-backend = "hatchling.build"

[project]
name = "pymongo"
Expand Down Expand Up @@ -45,16 +45,27 @@ Documentation = "https://pymongo.readthedocs.io"
Source = "https://github.com/mongodb/mongo-python-driver"
Tracker = "https://jira.mongodb.org/projects/PYTHON/issues"

[tool.setuptools.dynamic]
version = {attr = "pymongo._version.__version__"}
# Used to call hatch_build.py
[tool.hatch.build.hooks.custom]

[tool.setuptools.packages.find]
include = ["bson","gridfs", "pymongo"]
[tool.hatch.version]
path = "pymongo/_version.py"

[tool.setuptools.package-data]
bson=["py.typed", "*.pyi"]
pymongo=["py.typed", "*.pyi"]
gridfs=["py.typed", "*.pyi"]
[tool.hatch.build.targets.wheel]
packages = ["bson","gridfs", "pymongo"]

[tool.hatch.metadata.hooks.requirements_txt]
files = ["requirements.txt"]

[tool.hatch.metadata.hooks.requirements_txt.optional-dependencies]
aws = ["requirements/aws.txt"]
docs = ["requirements/docs.txt"]
encryption = ["requirements/encryption.txt"]
gssapi = ["requirements/gssapi.txt"]
ocsp = ["requirements/ocsp.txt"]
snappy = ["requirements/snappy.txt"]
test = ["requirements/test.txt"]
zstd = ["requirements/zstd.txt"]

[tool.pytest.ini_options]
minversion = "7"
Expand Down Expand Up @@ -168,6 +179,7 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?)|dummy.*)$"
"UP031", "F401", "B023", "F811"]
"tools/*.py" = ["T201"]
"green_framework_test.py" = ["T201"]
"hatch_build.py" = ["S"]

[tool.coverage.run]
branch = true
Expand Down
26 changes: 1 addition & 25 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,32 +136,8 @@ def build_extension(self, ext):
)
ext_modules = []


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You were able to get rid of all this because of the way that requirements.txt is handled? That's awesome!

def parse_reqs_file(fname):
with open(fname) as fid:
lines = [li.strip() for li in fid.readlines()]
return [li for li in lines if li and not li.startswith("#")]


dependencies = parse_reqs_file("requirements.txt")

extras_require = dict(
aws=parse_reqs_file("requirements/aws.txt"),
encryption=parse_reqs_file("requirements/encryption.txt"),
gssapi=parse_reqs_file("requirements/gssapi.txt"),
ocsp=parse_reqs_file("requirements/ocsp.txt"),
snappy=parse_reqs_file("requirements/snappy.txt"),
# PYTHON-3423 Removed in 4.3 but kept here to avoid pip warnings.
srv=[],
tls=[],
# PYTHON-2133 Removed in 4.0 but kept here to avoid pip warnings.
zstd=parse_reqs_file("requirements/zstd.txt"),
test=parse_reqs_file("requirements/test.txt"),
)

setup(
cmdclass={"build_ext": custom_build_ext},
install_requires=dependencies,
extras_require=extras_require,
ext_modules=ext_modules,
packages=["bson", "pymongo", "gridfs"],
) # type:ignore
8 changes: 8 additions & 0 deletions tools/fail_if_no_c.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@
import pymongo # noqa: E402

if not pymongo.has_c() or not bson.has_c():
try:
from pymongo import _cmessage # type:ignore[attr-defined] # noqa: F401
except Exception as e:
print(e)
try:
from bson import _cbson # type:ignore[attr-defined] # noqa: F401
except Exception as e:
print(e)
sys.exit("could not load C extensions")

if os.environ.get("ENSURE_UNIVERSAL2") == "1":
Expand Down
13 changes: 0 additions & 13 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ envlist =
doc-test,
# Linkcheck sphinx docs
linkcheck
# Check the sdist integrity.
manifest

labels = # Use labels and -m instead of -e so that tox -m <label> fails instantly if the label does not exist
test = test
Expand All @@ -51,7 +49,6 @@ labels = # Use labels and -m instead of -e so that tox -m <label> fails instantl
linkcheck = linkcheck
test-mockupdb = test-mockupdb
aws-secrets = aws-secrets
manifest = manifest

[testenv]
package = editable
Expand All @@ -71,8 +68,6 @@ commands =
description = run tests using run-tests.sh Evergreen script
passenv = *
extras = test
deps =
setuptools
allowlist_externals =
bash
commands =
Expand Down Expand Up @@ -184,14 +179,6 @@ allowlist_externals =
commands =
{[testenv:test]commands} ./test/mockupdb

[testenv:manifest]
description = ensure the sdist manifest is correct
skip_install = true
deps =
check-manifest
commands =
python -m check_manifest -v

[testenv:setup-encryption]
description = set up encryption assets and servers
skip_install = true
Expand Down
Loading