From e69ce8143e5ea0c8618ca67353a93cc71c73d138 Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Tue, 14 Nov 2023 10:48:37 +0000 Subject: [PATCH] feat: Introduce compatibility with native namespace packages --- .coveragerc | 5 --- google/__init__.py | 24 --------------- google/cloud/__init__.py | 24 --------------- google/cloud/bigquery/magics/magics.py | 5 ++- noxfile.py | 5 +-- samples/magics/conftest.py | 7 +++-- samples/snippets/jupyter_tutorial_test.py | 5 ++- setup.py | 9 +----- tests/unit/test_packaging.py | 37 +++++++++++++++++++++++ 9 files changed, 54 insertions(+), 67 deletions(-) delete mode 100644 google/__init__.py delete mode 100644 google/cloud/__init__.py create mode 100644 tests/unit/test_packaging.py diff --git a/.coveragerc b/.coveragerc index 1ed1a9704f..04092257a4 100644 --- a/.coveragerc +++ b/.coveragerc @@ -12,8 +12,3 @@ exclude_lines = pragma: NO COVER # Ignore debug-only repr def __repr__ - # Ignore pkg_resources exceptions. - # This is added at the module level as a safeguard for if someone - # generates the code and tries to run it without pip installing. This - # makes it virtually impossible to test properly. - except pkg_resources.DistributionNotFound diff --git a/google/__init__.py b/google/__init__.py deleted file mode 100644 index 8e60d8439d..0000000000 --- a/google/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2019 Google LLC -# -# 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 -# -# https://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. - -try: - import pkg_resources - - pkg_resources.declare_namespace(__name__) -except ImportError: - import pkgutil - - __path__ = pkgutil.extend_path(__path__, __name__) # type: ignore diff --git a/google/cloud/__init__.py b/google/cloud/__init__.py deleted file mode 100644 index 8e60d8439d..0000000000 --- a/google/cloud/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2019 Google LLC -# -# 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 -# -# https://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. - -try: - import pkg_resources - - pkg_resources.declare_namespace(__name__) -except ImportError: - import pkgutil - - __path__ = pkgutil.extend_path(__path__, __name__) # type: ignore diff --git a/google/cloud/bigquery/magics/magics.py b/google/cloud/bigquery/magics/magics.py index 2a3583c66a..979d8402ff 100644 --- a/google/cloud/bigquery/magics/magics.py +++ b/google/cloud/bigquery/magics/magics.py @@ -110,7 +110,10 @@ from google.cloud.bigquery.magics import line_arg_parser as lap -IPYTHON_USER_AGENT = "ipython-{}".format(IPython.__version__) +# Add `# type: ignore` to silence mypy error `Cannot determine type of "__version__" [has-type]` +# The IPython source file below does not have type hints. +# https://github.com/ipython/ipython/blob/86d44b7f4674d5bf830afea5e6235c28a4b56bfd/IPython/__init__.py#L65 +IPYTHON_USER_AGENT = "ipython-{}".format(IPython.__version__) # type: ignore class Context(object): diff --git a/noxfile.py b/noxfile.py index 703e36cbb5..c3c92d9160 100644 --- a/noxfile.py +++ b/noxfile.py @@ -137,7 +137,7 @@ def mypy(session): "types-requests", "types-setuptools", ) - session.run("mypy", "google/cloud", "--show-traceback") + session.run("mypy", "-p", "google", "--show-traceback") @nox.session(python=DEFAULT_PYTHON_VERSION) @@ -149,7 +149,8 @@ def pytype(session): session.install("attrs==20.3.0") session.install("-e", ".[all]") session.install(PYTYPE_VERSION) - session.run("pytype") + # See https://github.com/google/pytype/issues/464 + session.run("pytype", "-P", ".", "google/cloud/bigquery") @nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS) diff --git a/samples/magics/conftest.py b/samples/magics/conftest.py index 55ea30f90d..dc58181438 100644 --- a/samples/magics/conftest.py +++ b/samples/magics/conftest.py @@ -18,7 +18,7 @@ import pytest if typing.TYPE_CHECKING: - from IPython.core.interactiveshell import TerminalInteractiveShell + from IPython.terminal.interactiveshell import TerminalInteractiveShell interactiveshell = pytest.importorskip("IPython.terminal.interactiveshell") tools = pytest.importorskip("IPython.testing.tools") @@ -40,5 +40,8 @@ def ipython_interactive( for the duration of the test scope. """ - with ipython.builtin_trap: + # Remove ` # type: ignore` once + # https://github.com/ipython/ipython/issues/14181 is fixed. + # Also see https://github.com/ipython/ipython/blob/86d44b7f4674d5bf830afea5e6235c28a4b56bfd/IPython/core/interactiveshell.py#L1838. + with ipython.builtin_trap: # type: ignore yield ipython diff --git a/samples/snippets/jupyter_tutorial_test.py b/samples/snippets/jupyter_tutorial_test.py index 9d42a4eda7..622303c048 100644 --- a/samples/snippets/jupyter_tutorial_test.py +++ b/samples/snippets/jupyter_tutorial_test.py @@ -45,7 +45,10 @@ def ipython_interactive( for the duration of the test scope. """ - with ipython.builtin_trap: + # Remove `# type: ignore` once + # https://github.com/ipython/ipython/issues/14181 is fixed. + # Also see https://github.com/ipython/ipython/blob/86d44b7f4674d5bf830afea5e6235c28a4b56bfd/IPython/core/interactiveshell.py#L1838. + with ipython.builtin_trap: # type: ignore yield ipython diff --git a/setup.py b/setup.py index 4e87b3b84e..6f03bd1aba 100644 --- a/setup.py +++ b/setup.py @@ -108,16 +108,10 @@ # benchmarks, etc. packages = [ package - for package in setuptools.PEP420PackageFinder.find() + for package in setuptools.find_namespace_packages() if package.startswith("google") ] -# Determine which namespaces are needed. -namespaces = ["google"] -if "google.cloud" in packages: - namespaces.append("google.cloud") - - setuptools.setup( name=name, version=version, @@ -143,7 +137,6 @@ ], platforms="Posix; MacOS X; Windows", packages=packages, - namespace_packages=namespaces, install_requires=dependencies, extras_require=extras, python_requires=">=3.7", diff --git a/tests/unit/test_packaging.py b/tests/unit/test_packaging.py new file mode 100644 index 0000000000..107c9c295a --- /dev/null +++ b/tests/unit/test_packaging.py @@ -0,0 +1,37 @@ +# Copyright 2022 Google LLC +# +# 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 os +import subprocess +import sys + + +def test_namespace_package_compat(tmp_path): + # The ``google`` namespace package should not be masked + # by the presence of ``google-cloud-bigquery``. + google = tmp_path / "google" + google.mkdir() + google.joinpath("othermod.py").write_text("") + env = dict(os.environ, PYTHONPATH=str(tmp_path)) + cmd = [sys.executable, "-m", "google.othermod"] + subprocess.check_call(cmd, env=env) + + # The ``google.cloud`` namespace package should not be masked + # by the presence of ``google-cloud-bigquery``. + google_cloud = tmp_path / "google" / "cloud" + google_cloud.mkdir() + google_cloud.joinpath("othermod.py").write_text("") + env = dict(os.environ, PYTHONPATH=str(tmp_path)) + cmd = [sys.executable, "-m", "google.cloud.othermod"] + subprocess.check_call(cmd, env=env)