[bumpversion]
-current_version = 63.4.2
+current_version = 63.4.3
commit = True
tag = True
+v63.4.3
+-------
+
+
+Misc
+^^^^
+* #3496: Update to pypa/distutils@b65aa40 including more robust support for library/include dir handling in msvccompiler (pypa/distutils#153) and test suite improvements.
+
+
v63.4.2
-------
1) build system requirement, 2) required dependency and 3) optional
dependency.
-.. attention::
- Each dependency, regardless of type, needs to be specified according to :pep:`508`.
- This allows adding version :pep:`range restrictions <440#version-specifiers>`
- and :ref:`environment markers <environment-markers>`.
- Please note however that public package indexes, such as `PyPI`_
- might not accept packages that declare dependencies using
- :pep:`direct URLs <440#direct-references>`.
+Each dependency, regardless of type, needs to be specified according to :pep:`508`
+and :pep:`440`.
+This allows adding version :pep:`range restrictions <440#version-specifiers>`
+and :ref:`environment markers <environment-markers>`.
.. _build-requires:
to implement custom detection logic.
+Direct URL dependencies
+-----------------------
+
+.. attention::
+ `PyPI`_ and other standards-conformant package indices **do not** accept
+ packages that declare dependencies using direct URLs. ``pip`` will accept them
+ when installing packages from the local filesystem or from another URL,
+ however.
+
+Dependencies that are not available on a package index but can be downloaded
+elsewhere in the form of a source repository or archive may be specified
+using a variant of :pep:`PEP 440's direct references <440#direct-references>`:
+
+.. tab:: pyproject.toml
+
+ .. code-block:: toml
+
+ [project]
+ # ...
+ dependencies = [
+ "Package-A @ git+https://example.net/package-a.git@main",
+ "Package-B @ https://example.net/archives/package-b.whl",
+ ]
+
+.. tab:: setup.cfg
+
+ .. code-block:: ini
+
+ [options]
+ #...
+ install_requires =
+ Package-A @ git+https://example.net/package-a.git@main
+ Package-B @ https://example.net/archives/package-b.whl
+
+.. tab:: setup.py
+
+ .. code-block:: python
+
+ setup(
+ install_requires=[
+ "Package-A @ git+https://example.net/package-a.git@main",
+ "Package-B @ https://example.net/archives/package-b.whl",
+ ],
+ ...,
+ )
+
+For source repository URLs, a list of supported protocols and VCS-specific
+features such as selecting certain branches or tags can be found in pip's
+documentation on `VCS support <https://pip.pypa.io/en/latest/topics/vcs-support/>`_.
+Supported formats for archive URLs are sdists and wheels.
+
+
Optional dependencies
=====================
Setuptools allows you to declare dependencies that are not installed by default.
which other components can refer and have them installed.
A use case for this approach is that other package can use this "extra" for their
-own dependencies. For example, if ``Package-B`` needs ``Package-B`` with PDF support
+own dependencies. For example, if ``Package-B`` needs ``Package-A`` with PDF support
installed, it might declare the dependency like this:
.. tab:: pyproject.toml
[metadata]
name = setuptools
-version = 63.4.2
+version = 63.4.3
author = Python Packaging Authority
author_email = distutils-sig@python.org
description = Easily download, build, install, upgrade, and uninstall Python packages
import subprocess
import contextlib
import warnings
-import unittest.mock
+import unittest.mock as mock
with contextlib.suppress(ImportError):
import winreg
self.plat_name = None
self.initialized = False
+ @classmethod
+ def _configure(cls, vc_env):
+ """
+ Set class-level include/lib dirs.
+ """
+ cls.include_dirs = cls._parse_path(vc_env.get('include', ''))
+ cls.library_dirs = cls._parse_path(vc_env.get('lib', ''))
+
+ @staticmethod
+ def _parse_path(val):
+ return [dir.rstrip(os.sep) for dir in val.split(os.pathsep) if dir]
+
def initialize(self, plat_name=None):
# multi-init means we would need to check platform same each time...
assert not self.initialized, "don't init multiple times"
raise DistutilsPlatformError(
"Unable to find a compatible " "Visual Studio installation."
)
+ self._configure(vc_env)
self._paths = vc_env.get('path', '')
paths = self._paths.split(os.pathsep)
self.mc = _find_exe("mc.exe", paths) # message compiler
self.mt = _find_exe("mt.exe", paths) # message compiler
- for dir in vc_env.get('include', '').split(os.pathsep):
- if dir:
- self.add_include_dir(dir.rstrip(os.sep))
-
- for dir in vc_env.get('lib', '').split(os.pathsep):
- if dir:
- self.add_library_dir(dir.rstrip(os.sep))
-
self.preprocess_options = None
# bpo-38597: Always compile with dynamic linking
# Future releases of Python 3.x will include all past
else:
return
warnings.warn("Fallback spawn triggered. Please update distutils monkeypatch.")
- with unittest.mock.patch.dict('os.environ', env):
+ with mock.patch.dict('os.environ', env):
bag.value = super().spawn(cmd)
# -- Miscellaneous methods -----------------------------------------
# compression using `compress`
if compress == 'compress':
- warn("'compress' will be deprecated.", PendingDeprecationWarning)
+ warn("'compress' is deprecated.", DeprecationWarning)
# the option varies depending on the platform
compressed_name = archive_name + compress_ext[compress]
if sys.platform == 'win32':
}
language_order = ["c++", "objc", "c"]
+ include_dirs = []
+ """
+ include dirs specific to this compiler class
+ """
+
+ library_dirs = []
+ """
+ library dirs specific to this compiler class
+ """
+
def __init__(self, verbose=0, dry_run=0, force=0):
self.dry_run = dry_run
self.force = force
def _setup_compile(self, outdir, macros, incdirs, sources, depends, extra):
"""Process arguments and decide which source files to compile."""
- if outdir is None:
- outdir = self.output_dir
- elif not isinstance(outdir, str):
- raise TypeError("'output_dir' must be a string or None")
-
- if macros is None:
- macros = self.macros
- elif isinstance(macros, list):
- macros = macros + (self.macros or [])
- else:
- raise TypeError("'macros' (if supplied) must be a list of tuples")
-
- if incdirs is None:
- incdirs = self.include_dirs
- elif isinstance(incdirs, (list, tuple)):
- incdirs = list(incdirs) + (self.include_dirs or [])
- else:
- raise TypeError("'include_dirs' (if supplied) must be a list of strings")
+ outdir, macros, incdirs = self._fix_compile_args(outdir, macros, incdirs)
if extra is None:
extra = []
else:
raise TypeError("'include_dirs' (if supplied) must be a list of strings")
+ # add include dirs for class
+ include_dirs += self.__class__.include_dirs
+
return output_dir, macros, include_dirs
def _prep_compile(self, sources, output_dir, depends=None):
else:
raise TypeError("'library_dirs' (if supplied) must be a list of strings")
+ # add library dirs for class
+ library_dirs += self.__class__.library_dirs
+
if runtime_library_dirs is None:
runtime_library_dirs = self.runtime_library_dirs
elif isinstance(runtime_library_dirs, (list, tuple)):
Implements the Distutils 'check' command.
"""
+import contextlib
+
from distutils.core import Command
from distutils.errors import DistutilsSetupError
-try:
- # docutils is installed
- from docutils.utils import Reporter
- from docutils.parsers.rst import Parser
- from docutils import frontend
- from docutils import nodes
+with contextlib.suppress(ImportError):
+ import docutils.utils
+ import docutils.parsers.rst
+ import docutils.frontend
+ import docutils.nodes
- class SilentReporter(Reporter):
+ class SilentReporter(docutils.utils.Reporter):
def __init__(
self,
source,
def system_message(self, level, message, *children, **kwargs):
self.messages.append((level, message, children, kwargs))
- return nodes.system_message(
+ return docutils.nodes.system_message(
message, level=level, type=self.levels[level], *children, **kwargs
)
- HAS_DOCUTILS = True
-except Exception:
- # Catch all exceptions because exceptions besides ImportError probably
- # indicate that docutils is not ported to Py3k.
- HAS_DOCUTILS = False
-
class check(Command):
"""This command checks the meta-data of the package."""
if self.metadata:
self.check_metadata()
if self.restructuredtext:
- if HAS_DOCUTILS:
- self.check_restructuredtext()
+ if 'docutils' in globals():
+ try:
+ self.check_restructuredtext()
+ except TypeError as exc:
+ raise DistutilsSetupError(str(exc))
elif self.strict:
raise DistutilsSetupError('The docutils package is needed.')
"""Returns warnings when the provided data doesn't compile."""
# the include and csv_table directives need this to be a path
source_path = self.distribution.script_name or 'setup.py'
- parser = Parser()
- settings = frontend.OptionParser(components=(Parser,)).get_default_values()
+ parser = docutils.parsers.rst.Parser()
+ settings = docutils.frontend.OptionParser(
+ components=(docutils.parsers.rst.Parser,)
+ ).get_default_values()
settings.tab_width = 4
settings.pep_references = None
settings.rfc_references = None
error_handler=settings.error_encoding_error_handler,
)
- document = nodes.document(settings, reporter, source=source_path)
+ document = docutils.nodes.document(settings, reporter, source=source_path)
document.note_source(source_path, -1)
try:
parser.parse(data, document)
def check_metadata(self):
"""Deprecated API."""
warn(
- "distutils.command.register.check_metadata is deprecated, \
- use the check command instead",
- PendingDeprecationWarning,
+ "distutils.command.register.check_metadata is deprecated; "
+ "use the check command instead",
+ DeprecationWarning,
)
check = self.distribution.get_command_obj('check')
check.ensure_finalized()
)
+try:
+ from test.support.import_helper import (
+ DirsOnSysPath,
+ CleanImport,
+ )
+except (ModuleNotFoundError, ImportError):
+ from test.support import (
+ DirsOnSysPath,
+ CleanImport,
+ )
+
+
if sys.version_info < (3, 9):
requires_zlib = lambda: test.support.requires_zlib
import sys
import shutil
import tempfile
-import unittest
import sysconfig
import itertools
@pytest.mark.usefixtures('distutils_managed_tempdir')
class TempdirManager:
- """Mix-in class that handles temporary directories for test cases.
-
- This is intended to be used with unittest.TestCase.
+ """
+ Mix-in class that handles temporary directories for test cases.
"""
def mkdtemp(self):
If the source file can be found, it will be copied to *directory*. If not,
the test will be skipped. Errors during copy are not caught.
"""
- filename = _get_xxmodule_path()
- if filename is None:
- raise unittest.SkipTest(
- 'cannot find xxmodule.c (test must run in ' 'the python build dir)'
- )
- shutil.copy(filename, directory)
+ shutil.copy(_get_xxmodule_path(), os.path.join(directory, 'xxmodule.c'))
def _get_xxmodule_path():
- srcdir = sysconfig.get_config_var('srcdir')
- candidates = [
- # use installed copy if available
- os.path.join(os.path.dirname(__file__), 'xxmodule.c'),
- # otherwise try using copy from build directory
- os.path.join(srcdir, 'Modules', 'xxmodule.c'),
- # srcdir mysteriously can be $srcdir/Lib/distutils/tests when
- # this file is run from its parent directory, so walk up the
- # tree to find the real srcdir
- os.path.join(srcdir, '..', '..', '..', 'Modules', 'xxmodule.c'),
- ]
- for path in candidates:
- if os.path.exists(path):
- return path
+ source_name = 'xxmodule.c' if sys.version_info > (3, 9) else 'xxmodule-3.8.c'
+ return os.path.join(os.path.dirname(__file__), source_name)
def fixup_build_ext(cmd):
"""Tests for distutils.archive_util."""
-import unittest
import os
import sys
import tarfile
from os.path import splitdrive
import warnings
+import functools
+import operator
+import pathlib
import pytest
make_archive,
ARCHIVE_FORMATS,
)
-from distutils.spawn import find_executable, spawn
+from distutils.spawn import spawn
from distutils.tests import support
from test.support import patch
from .unix_compat import require_unix_id, require_uid_0, grp, pwd, UID_0_SUPPORT
from .py38compat import check_warnings
-try:
- import zipfile
-
- ZIP_SUPPORT = True
-except ImportError:
- ZIP_SUPPORT = find_executable('zip')
-
-try:
- import bz2
-except ImportError:
- bz2 = None
-
-try:
- import lzma
-except ImportError:
- lzma = None
-
-
def can_fs_encode(filename):
"""
Return True if the filename can be saved in the file system.
return True
+def all_equal(values):
+ return functools.reduce(operator.eq, values)
+
+
+def same_drive(*paths):
+ return all_equal(pathlib.Path(path).drive for path in paths)
+
+
class ArchiveUtilTestCase(support.TempdirManager, support.LoggingSilencer):
@pytest.mark.usefixtures('needs_zlib')
def test_make_tarball(self, name='archive'):
tmpdir = self._create_files()
self._make_tarball(tmpdir, 'archive', '.tar.gz', compress='gzip')
- @unittest.skipUnless(bz2, 'Need bz2 support to run')
def test_make_tarball_bzip2(self):
+ pytest.importorskip('bz2')
tmpdir = self._create_files()
self._make_tarball(tmpdir, 'archive', '.tar.bz2', compress='bzip2')
- @unittest.skipUnless(lzma, 'Need lzma support to run')
def test_make_tarball_xz(self):
+ pytest.importorskip('lzma')
tmpdir = self._create_files()
self._make_tarball(tmpdir, 'archive', '.tar.xz', compress='xz')
- @unittest.skipUnless(
- can_fs_encode('årchiv'), 'File system cannot handle this filename'
- )
+ @pytest.mark.skipif("not can_fs_encode('årchiv')")
def test_make_tarball_latin1(self):
"""
Mirror test_make_tarball, except filename contains latin characters.
"""
self.test_make_tarball('årchiv') # note this isn't a real word
- @unittest.skipUnless(
- can_fs_encode('のアーカイブ'), 'File system cannot handle this filename'
- )
+ @pytest.mark.skipif("not can_fs_encode('のアーカイブ')")
def test_make_tarball_extended(self):
"""
Mirror test_make_tarball, except filename contains extended
def _make_tarball(self, tmpdir, target_name, suffix, **kwargs):
tmpdir2 = self.mkdtemp()
- unittest.skipUnless(
- splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0],
- "source and target should be on same drive",
- )
+ if same_drive(tmpdir, tmpdir2):
+ pytest.skip("source and target should be on same drive")
base_name = os.path.join(tmpdir2, target_name)
return tmpdir
@pytest.mark.usefixtures('needs_zlib')
- @unittest.skipUnless(
- find_executable('tar') and find_executable('gzip'),
- 'Need the tar and gzip commands to run',
- )
+ @pytest.mark.skipif("not (find_executable('tar') and find_executable('gzip'))")
def test_tarfile_vs_tar(self):
tmpdir = self._create_files()
tmpdir2 = self.mkdtemp()
tarball = base_name + '.tar'
assert os.path.exists(tarball)
- @unittest.skipUnless(
- find_executable('compress'), 'The compress program is required'
- )
+ @pytest.mark.skipif("not find_executable('compress')")
def test_compress_deprecated(self):
tmpdir = self._create_files()
base_name = os.path.join(self.mkdtemp(), 'archive')
- # using compress and testing the PendingDeprecationWarning
+ # using compress and testing the DeprecationWarning
old_dir = os.getcwd()
os.chdir(tmpdir)
try:
assert len(w.warnings) == 1
@pytest.mark.usefixtures('needs_zlib')
- @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run')
def test_make_zipfile(self):
+ zipfile = pytest.importorskip('zipfile')
# creating something to tar
tmpdir = self._create_files()
base_name = os.path.join(self.mkdtemp(), 'archive')
with zipfile.ZipFile(tarball) as zf:
assert sorted(zf.namelist()) == self._zip_created_files
- @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run')
def test_make_zipfile_no_zlib(self):
+ zipfile = pytest.importorskip('zipfile')
patch(self, archive_util.zipfile, 'zlib', None) # force zlib ImportError
called = []
assert os.path.basename(res) == 'archive.tar.gz'
assert self._tarinfo(res) == self._created_files
- @unittest.skipUnless(bz2, 'Need bz2 support to run')
def test_make_archive_bztar(self):
+ pytest.importorskip('bz2')
base_dir = self._create_files()
base_name = os.path.join(self.mkdtemp(), 'archive')
res = make_archive(base_name, 'bztar', base_dir, 'dist')
assert os.path.basename(res) == 'archive.tar.bz2'
assert self._tarinfo(res) == self._created_files
- @unittest.skipUnless(lzma, 'Need xz support to run')
def test_make_archive_xztar(self):
+ pytest.importorskip('lzma')
base_dir = self._create_files()
base_name = os.path.join(self.mkdtemp(), 'archive')
res = make_archive(base_name, 'xztar', base_dir, 'dist')
"""Tests for distutils.command.bdist_rpm."""
-import unittest
import sys
import os
from distutils.core import Distribution
from distutils.command.bdist_rpm import bdist_rpm
from distutils.tests import support
-from distutils.spawn import find_executable
+from distutils.spawn import find_executable # noqa: F401
from .py38compat import requires_zlib
pytest.skip("sys.executable is not encodable to UTF-8")
+mac_woes = pytest.mark.skipif(
+ "not sys.platform.startswith('linux')",
+ reason='spurious sdtout/stderr output under macOS',
+)
+
+
@pytest.mark.usefixtures('save_env')
@pytest.mark.usefixtures('save_argv')
@pytest.mark.usefixtures('save_cwd')
support.TempdirManager,
support.LoggingSilencer,
):
-
- # XXX I am unable yet to make this test work without
- # spurious sdtout/stderr output under Mac OS X
- @unittest.skipUnless(
- sys.platform.startswith('linux'), 'spurious sdtout/stderr output under Mac OS X'
- )
+ @mac_woes
@requires_zlib()
- @unittest.skipIf(find_executable('rpm') is None, 'the rpm command is not found')
- @unittest.skipIf(
- find_executable('rpmbuild') is None, 'the rpmbuild command is not found'
- )
+ @pytest.mark.skipif("not find_executable('rpm')")
+ @pytest.mark.skipif("not find_executable('rpmbuild')")
def test_quiet(self):
# let's create a package
tmp_dir = self.mkdtemp()
assert ('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm') in dist.dist_files
assert ('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm') in dist.dist_files
- # XXX I am unable yet to make this test work without
- # spurious sdtout/stderr output under Mac OS X
- @unittest.skipUnless(
- sys.platform.startswith('linux'), 'spurious sdtout/stderr output under Mac OS X'
- )
+ @mac_woes
@requires_zlib()
# http://bugs.python.org/issue1533164
- @unittest.skipIf(find_executable('rpm') is None, 'the rpm command is not found')
- @unittest.skipIf(
- find_executable('rpmbuild') is None, 'the rpmbuild command is not found'
- )
+ @pytest.mark.skipif("not find_executable('rpm')")
+ @pytest.mark.skipif("not find_executable('rpmbuild')")
def test_no_optimize_flag(self):
# let's create a package that breaks bdist_rpm
tmp_dir = self.mkdtemp()
"""Tests for distutils.command.bdist_wininst."""
-import sys
-import platform
-import unittest
+import pytest
from .py38compat import check_warnings
from distutils.tests import support
-@unittest.skipIf(
- sys.platform == 'win32' and platform.machine() == 'ARM64',
- 'bdist_wininst is not supported in this install',
-)
-@unittest.skipIf(
- getattr(bdist_wininst, '_unsupported', False),
- 'bdist_wininst is not supported in this install',
-)
+@pytest.mark.skipif("platform.machine() == 'ARM64'")
+@pytest.mark.skipif("bdist_wininst._unsupported")
class TestBuildWinInst(support.TempdirManager, support.LoggingSilencer):
def test_get_exe_bytes(self):
"""Tests for distutils.command.build."""
-import unittest
import os
import sys
from sysconfig import get_platform
-class BuildTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase):
+class TestBuild(support.TempdirManager, support.LoggingSilencer):
def test_finalize_options(self):
pkg_dir, dist = self.create_dist()
cmd = build(dist)
"""Tests for distutils.command.build_clib."""
-import unittest
import os
-import sys
from test.support import missing_compiler_executable
+import pytest
+
from distutils.command.build_clib import build_clib
from distutils.errors import DistutilsSetupError
from distutils.tests import support
-import pytest
class TestBuildCLib(support.TempdirManager, support.LoggingSilencer):
with pytest.raises(DistutilsSetupError):
cmd.finalize_options()
- @unittest.skipIf(sys.platform == 'win32', "can't test on Windows")
+ @pytest.mark.skipif('platform.system() == "Windows"')
def test_run(self):
pkg_dir, dist = self.create_dist()
cmd = build_clib(dist)
from io import StringIO
import textwrap
import site
+import contextlib
+import platform
+import tempfile
+import importlib
+import shutil
from distutils.core import Distribution
from distutils.command.build_ext import build_ext
LoggingSilencer,
copy_xxmodule_c,
fixup_build_ext,
- combine_markers,
)
from distutils.extension import Extension
from distutils.errors import (
UnknownFileError,
)
-import unittest
from test import support
from . import py38compat as os_helper
-from test.support.script_helper import assert_python_ok
+from . import py38compat as import_helper
import pytest
import re
-# http://bugs.python.org/issue4373
-# Don't load the xx module more than once.
-ALREADY_TESTED = False
-
@pytest.fixture()
def user_site_dir(request):
build_ext.USER_BASE = orig_user_base
+@contextlib.contextmanager
+def safe_extension_import(name, path):
+ with import_helper.CleanImport(name):
+ with extension_redirect(name, path) as new_path:
+ with import_helper.DirsOnSysPath(new_path):
+ yield
+
+
+@contextlib.contextmanager
+def extension_redirect(mod, path):
+ """
+ Tests will fail to tear down an extension module if it's been imported.
+
+ Before importing, copy the file to a temporary directory that won't
+ be cleaned up. Yield the new path.
+ """
+ if platform.system() != "Windows" and sys.platform != "cygwin":
+ yield path
+ return
+ with import_helper.DirsOnSysPath(path):
+ spec = importlib.util.find_spec(mod)
+ filename = os.path.basename(spec.origin)
+ trash_dir = tempfile.mkdtemp(prefix='deleteme')
+ dest = os.path.join(trash_dir, os.path.basename(filename))
+ shutil.copy(spec.origin, dest)
+ yield trash_dir
+ # TODO: can the file be scheduled for deletion?
+
+
@pytest.mark.usefixtures('user_site_dir')
class TestBuildExt(TempdirManager, LoggingSilencer):
def build_ext(self, *args, **kwargs):
def test_build_ext(self):
cmd = support.missing_compiler_executable()
- if cmd is not None:
- self.skipTest('The %r command is not found' % cmd)
- global ALREADY_TESTED
copy_xxmodule_c(self.tmp_dir)
xx_c = os.path.join(self.tmp_dir, 'xxmodule.c')
xx_ext = Extension('xx', [xx_c])
finally:
sys.stdout = old_stdout
- if ALREADY_TESTED:
- self.skipTest('Already tested in %s' % ALREADY_TESTED)
- else:
- ALREADY_TESTED = type(self).__name__
-
- code = textwrap.dedent(
- f"""
- tmp_dir = {self.tmp_dir!r}
-
- import sys
- import unittest
- from test import support
+ with safe_extension_import('xx', self.tmp_dir):
+ self._test_xx()
- sys.path.insert(0, tmp_dir)
- import xx
+ @staticmethod
+ def _test_xx():
+ import xx
- class Tests(unittest.TestCase):
- def test_xx(self):
- for attr in ('error', 'foo', 'new', 'roj'):
- self.assertTrue(hasattr(xx, attr))
+ for attr in ('error', 'foo', 'new', 'roj'):
+ assert hasattr(xx, attr)
- self.assertEqual(xx.foo(2, 5), 7)
- self.assertEqual(xx.foo(13,15), 28)
- self.assertEqual(xx.new().demo(), None)
- if support.HAVE_DOCSTRINGS:
- doc = 'This is a template module just for instruction.'
- self.assertEqual(xx.__doc__, doc)
- self.assertIsInstance(xx.Null(), xx.Null)
- self.assertIsInstance(xx.Str(), xx.Str)
-
-
- unittest.main()
- """
- )
- assert_python_ok('-c', code)
+ assert xx.foo(2, 5) == 7
+ assert xx.foo(13, 15) == 28
+ assert xx.new().demo() is None
+ if support.HAVE_DOCSTRINGS:
+ doc = 'This is a template module just for instruction.'
+ assert xx.__doc__ == doc
+ assert isinstance(xx.Null(), xx.Null)
+ assert isinstance(xx.Str(), xx.Str)
def test_solaris_enable_shared(self):
dist = Distribution({'name': 'xx'})
def test_get_outputs(self):
cmd = support.missing_compiler_executable()
- if cmd is not None:
- self.skipTest('The %r command is not found' % cmd)
tmp_dir = self.mkdtemp()
c_file = os.path.join(tmp_dir, 'foo.c')
self.write_file(c_file, 'void PyInit_foo(void) {}\n')
wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext)
assert wanted == path
- @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX')
+ @pytest.mark.skipif('platform.system() != "Darwin"')
@pytest.mark.usefixtures('save_env')
def test_deployment_target_default(self):
# Issue 9516: Test that, in the absence of the environment variable,
# the interpreter.
self._try_compile_deployment_target('==', None)
- @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX')
+ @pytest.mark.skipif('platform.system() != "Darwin"')
@pytest.mark.usefixtures('save_env')
def test_deployment_target_too_low(self):
# Issue 9516: Test that an extension module is not allowed to be
with pytest.raises(DistutilsPlatformError):
self._try_compile_deployment_target('>', '10.1')
- @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX')
+ @pytest.mark.skipif('platform.system() != "Darwin"')
@pytest.mark.usefixtures('save_env')
def test_deployment_target_higher_ok(self):
# Issue 9516: Test that an extension module can be compiled with a
import os
import sys
-import unittest
+import unittest.mock as mock
+
+import pytest
from distutils.command.build_py import build_py
from distutils.core import Distribution
from distutils.errors import DistutilsFileError
-from unittest.mock import patch
from distutils.tests import support
except DistutilsFileError:
self.fail("failed package_data test when package_dir is ''")
- @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled')
+ @pytest.mark.skipif('sys.dont_write_bytecode')
def test_byte_compile(self):
project_dir, dist = self.create_dist(py_modules=['boiledeggs'])
os.chdir(project_dir)
found = os.listdir(os.path.join(cmd.build_lib, '__pycache__'))
assert found == ['boiledeggs.%s.pyc' % sys.implementation.cache_tag]
- @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled')
+ @pytest.mark.skipif('sys.dont_write_bytecode')
def test_byte_compile_optimized(self):
project_dir, dist = self.create_dist(py_modules=['boiledeggs'])
os.chdir(project_dir)
assert 'byte-compiling is disabled' in self.logs[0][1] % self.logs[0][2]
- @patch("distutils.command.build_py.log.warn")
+ @mock.patch("distutils.command.build_py.log.warn")
def test_namespace_package_does_not_warn(self, log_warn):
"""
Originally distutils implementation did not account for PEP 420
--- /dev/null
+import os
+import sys
+import platform
+import textwrap
+import sysconfig
+
+import pytest
+
+from distutils import ccompiler
+
+
+def _make_strs(paths):
+ """
+ Convert paths to strings for legacy compatibility.
+ """
+ if sys.version_info > (3, 8) and platform.system() != "Windows":
+ return paths
+ return list(map(os.fspath, paths))
+
+
+@pytest.fixture
+def c_file(tmp_path):
+ c_file = tmp_path / 'foo.c'
+ gen_headers = ('Python.h',)
+ is_windows = platform.system() == "Windows"
+ plat_headers = ('windows.h',) * is_windows
+ all_headers = gen_headers + plat_headers
+ headers = '\n'.join(f'#include <{header}>\n' for header in all_headers)
+ payload = (
+ textwrap.dedent(
+ """
+ #headers
+ void PyInit_foo(void) {}
+ """
+ )
+ .lstrip()
+ .replace('#headers', headers)
+ )
+ c_file.write_text(payload)
+ return c_file
+
+
+def test_set_include_dirs(c_file):
+ """
+ Extensions should build even if set_include_dirs is invoked.
+ In particular, compiler-specific paths should not be overridden.
+ """
+ compiler = ccompiler.new_compiler()
+ python = sysconfig.get_paths()['include']
+ compiler.set_include_dirs([python])
+ compiler.compile(_make_strs([c_file]))
+
+ # do it again, setting include dirs after any initialization
+ compiler.set_include_dirs([python])
+ compiler.compile(_make_strs([c_file]))
"""Tests for distutils.command.check."""
import os
import textwrap
-import unittest
-from distutils.command.check import check, HAS_DOCUTILS
+import pytest
+
+from distutils.command.check import check
from distutils.tests import support
from distutils.errors import DistutilsSetupError
-import pytest
try:
import pygments
cmd = self._run(metadata)
assert cmd._warnings == 0
- @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils")
def test_check_document(self):
+ pytest.importorskip('docutils')
pkg_info, dist = self.create_dist()
cmd = check(dist)
msgs = cmd._check_rst_data(rest)
assert len(msgs) == 0
- @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils")
def test_check_restructuredtext(self):
+ pytest.importorskip('docutils')
# let's see if it detects broken rest in long_description
broken_rest = 'title\n===\n\ntest'
pkg_info, dist = self.create_dist(long_description=broken_rest)
cmd = self._run(metadata, cwd=HERE, strict=1, restructuredtext=1)
assert cmd._warnings == 0
- @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils")
def test_check_restructuredtext_with_syntax_highlight(self):
+ pytest.importorskip('docutils')
# Don't fail if there is a `code` or `code-block` directive
example_rst_docs = []
"""Tests for distutils.cmd."""
-import unittest
import os
from test.support import captured_stdout
pass
-class TestCommand(unittest.TestCase):
- def setUp(self):
- dist = Distribution()
- self.cmd = MyCmd(dist)
+@pytest.fixture
+def cmd(request):
+ return MyCmd(Distribution())
- def test_ensure_string_list(self):
- cmd = self.cmd
+class TestCommand:
+ def test_ensure_string_list(self, cmd):
cmd.not_string_list = ['one', 2, 'three']
cmd.yes_string_list = ['one', 'two', 'three']
cmd.not_string_list2 = object()
with pytest.raises(DistutilsOptionError):
cmd.ensure_string_list('option3')
- def test_make_file(self):
-
- cmd = self.cmd
-
+ def test_make_file(self, cmd):
# making sure it raises when infiles is not a string or a list/tuple
with pytest.raises(TypeError):
cmd.make_file(infiles=1, outfile='', func='func', args=())
cmd.execute = _execute
cmd.make_file(infiles='in', outfile='out', func='func', args=())
- def test_dump_options(self):
+ def test_dump_options(self, cmd):
msgs = []
def _announce(msg, level):
msgs.append(msg)
- cmd = self.cmd
cmd.announce = _announce
cmd.option1 = 1
cmd.option2 = 1
wanted = ["command options for 'MyCmd':", ' option1 = 1', ' option2 = 1']
assert msgs == wanted
- def test_ensure_string(self):
- cmd = self.cmd
+ def test_ensure_string(self, cmd):
cmd.option1 = 'ok'
cmd.ensure_string('option1')
with pytest.raises(DistutilsOptionError):
cmd.ensure_string('option3')
- def test_ensure_filename(self):
- cmd = self.cmd
+ def test_ensure_filename(self, cmd):
cmd.option1 = __file__
cmd.ensure_filename('option1')
cmd.option2 = 'xxx'
with pytest.raises(DistutilsOptionError):
cmd.ensure_filename('option2')
- def test_ensure_dirname(self):
- cmd = self.cmd
+ def test_ensure_dirname(self, cmd):
cmd.option1 = os.path.dirname(__file__) or os.curdir
cmd.ensure_dirname('option1')
cmd.option2 = 'xxx'
with pytest.raises(DistutilsOptionError):
cmd.ensure_dirname('option2')
- def test_debug_print(self):
- cmd = self.cmd
+ def test_debug_print(self, cmd):
with captured_stdout() as stdout:
cmd.debug_print('xxx')
stdout.seek(0)
"""Tests for distutils.pypirc.pypirc."""
import os
-import unittest
import pytest
-from distutils.core import PyPIRCCommand
-from distutils.core import Distribution
-from distutils.log import set_threshold
-from distutils.log import WARN
-
from distutils.tests import support
PYPIRC = """\
@support.combine_markers
-@pytest.mark.usefixtures('save_env')
+@pytest.mark.usefixtures('threshold_warn')
+@pytest.mark.usefixtures('pypirc')
class BasePyPIRCCommandTestCase(
support.TempdirManager,
support.LoggingSilencer,
- unittest.TestCase,
):
- def setUp(self):
- """Patches the environment."""
- super().setUp()
- self.tmp_dir = self.mkdtemp()
- os.environ['HOME'] = self.tmp_dir
- os.environ['USERPROFILE'] = self.tmp_dir
- self.rc = os.path.join(self.tmp_dir, '.pypirc')
- self.dist = Distribution()
-
- class command(PyPIRCCommand):
- def __init__(self, dist):
- super().__init__(dist)
-
- def initialize_options(self):
- pass
-
- finalize_options = initialize_options
-
- self._cmd = command
- self.old_threshold = set_threshold(WARN)
-
- def tearDown(self):
- """Removes the patch."""
- set_threshold(self.old_threshold)
- super().tearDown()
+ pass
class PyPIRCCommandTestCase(BasePyPIRCCommandTestCase):
"""Tests for distutils.command.config."""
-import unittest
import os
import sys
from test.support import missing_compiler_executable
+import pytest
+
from distutils.command.config import dump_file, config
from distutils.tests import support
from distutils import log
+@pytest.fixture(autouse=True)
+def info_log(request, monkeypatch):
+ self = request.instance
+ self._logs = []
+ monkeypatch.setattr(log, 'info', self._info)
+
+
@support.combine_markers
-class ConfigTestCase(
- support.LoggingSilencer, support.TempdirManager, unittest.TestCase
-):
+class TestConfig(support.LoggingSilencer, support.TempdirManager):
def _info(self, msg, *args):
for line in msg.splitlines():
self._logs.append(line)
- def setUp(self):
- super().setUp()
- self._logs = []
- self.old_log = log.info
- log.info = self._info
-
- def tearDown(self):
- log.info = self.old_log
- super().tearDown()
-
def test_dump_file(self):
this_file = os.path.splitext(__file__)[0] + '.py'
f = open(this_file)
dump_file(this_file, 'I am the header')
assert len(self._logs) == numlines + 1
- @unittest.skipIf(sys.platform == 'win32', "can't test on Windows")
+ @pytest.mark.skipif('platform.system() == "Windows"')
def test_search_cpp(self):
cmd = missing_compiler_executable(['preprocessor'])
if cmd is not None:
import io
import distutils.core
import os
-import shutil
import sys
from test.support import captured_stdout
import pytest
from . import py38compat as os_helper
-import unittest
-from distutils import log
from distutils.dist import Distribution
# setup script that uses __file__
"""
+@pytest.fixture(autouse=True)
+def save_stdout(monkeypatch):
+ monkeypatch.setattr(sys, 'stdout', sys.stdout)
+
+
@pytest.mark.usefixtures('save_env')
@pytest.mark.usefixtures('save_argv')
-class CoreTestCase(unittest.TestCase):
- def setUp(self):
- super().setUp()
- self.old_stdout = sys.stdout
- self.cleanup_testfn()
- self.addCleanup(log.set_threshold, log._global_log.threshold)
-
- def tearDown(self):
- sys.stdout = self.old_stdout
- self.cleanup_testfn()
- super().tearDown()
-
- def cleanup_testfn(self):
- path = os_helper.TESTFN
- if os.path.isfile(path):
- os.remove(path)
- elif os.path.isdir(path):
- shutil.rmtree(path)
-
+@pytest.mark.usefixtures('cleanup_testfn')
+class TestCore:
def write_setup(self, text, path=os_helper.TESTFN):
f = open(path, "w")
try:
"""Tests for distutils.cygwinccompiler."""
-import unittest
import sys
import os
+import pytest
+
from distutils.cygwinccompiler import (
check_config_h,
CONFIG_H_OK,
get_msvcr,
)
from distutils.tests import support
-import pytest
-
-
-class CygwinCCompilerTestCase(support.TempdirManager, unittest.TestCase):
- def setUp(self):
- super().setUp()
- self.version = sys.version
- self.python_h = os.path.join(self.mkdtemp(), 'python.h')
- from distutils import sysconfig
+from distutils import sysconfig
- self.old_get_config_h_filename = sysconfig.get_config_h_filename
- sysconfig.get_config_h_filename = self._get_config_h_filename
- def tearDown(self):
- sys.version = self.version
- from distutils import sysconfig
+@pytest.fixture(autouse=True)
+def stuff(request, monkeypatch, distutils_managed_tempdir):
+ self = request.instance
+ self.python_h = os.path.join(self.mkdtemp(), 'python.h')
+ monkeypatch.setattr(sysconfig, 'get_config_h_filename', self._get_config_h_filename)
+ monkeypatch.setattr(sys, 'version', sys.version)
- sysconfig.get_config_h_filename = self.old_get_config_h_filename
- super().tearDown()
+class TestCygwinCCompiler(support.TempdirManager):
def _get_config_h_filename(self):
return self.python_h
- @unittest.skipIf(sys.platform != "cygwin", "Not running on Cygwin")
- @unittest.skipIf(
- not os.path.exists("/usr/lib/libbash.dll.a"), "Don't know a linkable library"
- )
+ @pytest.mark.skipif('sys.platform != "cygwin"')
+ @pytest.mark.skipif('not os.path.exists("/usr/lib/libbash.dll.a")')
def test_find_library_file(self):
from distutils.cygwinccompiler import CygwinCCompiler
assert os.path.exists(linkable_file)
assert linkable_file == f"/usr/lib/lib{link_name:s}.dll.a"
- @unittest.skipIf(sys.platform != "cygwin", "Not running on Cygwin")
+ @pytest.mark.skipif('sys.platform != "cygwin"')
def test_runtime_library_dir_option(self):
from distutils.cygwinccompiler import CygwinCCompiler
"""Tests for distutils.dir_util."""
-import unittest
import os
import stat
-import sys
-from unittest.mock import patch
+import unittest.mock as mock
from distutils import dir_util, errors
from distutils.dir_util import (
import pytest
-class DirUtilTestCase(support.TempdirManager, unittest.TestCase):
+@pytest.fixture(autouse=True)
+def stuff(request, monkeypatch, distutils_managed_tempdir):
+ self = request.instance
+ self._logs = []
+ tmp_dir = self.mkdtemp()
+ self.root_target = os.path.join(tmp_dir, 'deep')
+ self.target = os.path.join(self.root_target, 'here')
+ self.target2 = os.path.join(tmp_dir, 'deep2')
+ monkeypatch.setattr(log, 'info', self._log)
+
+
+class TestDirUtil(support.TempdirManager):
def _log(self, msg, *args):
if len(args) > 0:
self._logs.append(msg % args)
else:
self._logs.append(msg)
- def setUp(self):
- super().setUp()
- self._logs = []
- tmp_dir = self.mkdtemp()
- self.root_target = os.path.join(tmp_dir, 'deep')
- self.target = os.path.join(self.root_target, 'here')
- self.target2 = os.path.join(tmp_dir, 'deep2')
- self.old_log = log.info
- log.info = self._log
-
- def tearDown(self):
- log.info = self.old_log
- super().tearDown()
-
def test_mkpath_remove_tree_verbosity(self):
mkpath(self.target, verbose=0)
wanted = ["removing '%s' (and everything under it)" % self.root_target]
assert self._logs == wanted
- @unittest.skipIf(
- sys.platform.startswith('win'),
- "This test is only appropriate for POSIX-like systems.",
- )
+ @pytest.mark.skipif("platform.system() == 'Windows'")
def test_mkpath_with_custom_mode(self):
# Get and set the current umask value for testing mode bits.
umask = os.umask(0o002)
"""
An exception in listdir should raise a DistutilsFileError
"""
- with patch("os.listdir", side_effect=OSError()), pytest.raises(
+ with mock.patch("os.listdir", side_effect=OSError()), pytest.raises(
errors.DistutilsFileError
):
src = self.tempdirs[-1]
import os
import io
import sys
-import unittest
import warnings
import textwrap
import functools
-
-from unittest import mock
+import unittest.mock as mock
import pytest
assert isinstance(cmd, test_dist)
assert cmd.sample_option == "sometext"
- @unittest.skipIf(
+ @pytest.mark.skipif(
'distutils' not in Distribution.parse_config_files.__module__,
- 'Cannot test when virtualenv has monkey-patched Distribution.',
+ reason='Cannot test when virtualenv has monkey-patched Distribution',
)
def test_venv_install_options(self, request):
sys.argv.append("install")
"""Tests for distutils.file_util."""
-import unittest
import os
import errno
-from unittest.mock import patch
+import unittest.mock as mock
from distutils.file_util import move_file, copy_file
from distutils import log
import pytest
-class FileUtilTestCase(support.TempdirManager, unittest.TestCase):
+@pytest.fixture(autouse=True)
+def stuff(request, monkeypatch, distutils_managed_tempdir):
+ self = request.instance
+ self._logs = []
+ tmp_dir = self.mkdtemp()
+ self.source = os.path.join(tmp_dir, 'f1')
+ self.target = os.path.join(tmp_dir, 'f2')
+ self.target_dir = os.path.join(tmp_dir, 'd1')
+ monkeypatch.setattr(log, 'info', self._log)
+
+
+class TestFileUtil(support.TempdirManager):
def _log(self, msg, *args):
if len(args) > 0:
self._logs.append(msg % args)
else:
self._logs.append(msg)
- def setUp(self):
- super().setUp()
- self._logs = []
- self.old_log = log.info
- log.info = self._log
- tmp_dir = self.mkdtemp()
- self.source = os.path.join(tmp_dir, 'f1')
- self.target = os.path.join(tmp_dir, 'f2')
- self.target_dir = os.path.join(tmp_dir, 'd1')
-
- def tearDown(self):
- log.info = self.old_log
- super().tearDown()
-
def test_move_file_verbosity(self):
f = open(self.source, 'w')
try:
def test_move_file_exception_unpacking_rename(self):
# see issue 22182
- with patch("os.rename", side_effect=OSError("wrong", 1)), pytest.raises(
+ with mock.patch("os.rename", side_effect=OSError("wrong", 1)), pytest.raises(
DistutilsFileError
):
with open(self.source, 'w') as fobj:
def test_move_file_exception_unpacking_unlink(self):
# see issue 22182
- with patch("os.rename", side_effect=OSError(errno.EXDEV, "wrong")), patch(
- "os.unlink", side_effect=OSError("wrong", 1)
- ), pytest.raises(DistutilsFileError):
+ with mock.patch(
+ "os.rename", side_effect=OSError(errno.EXDEV, "wrong")
+ ), mock.patch("os.unlink", side_effect=OSError("wrong", 1)), pytest.raises(
+ DistutilsFileError
+ ):
with open(self.source, 'w') as fobj:
fobj.write('spam eggs')
move_file(self.source, self.target, verbose=0)
with open(self.source, 'w') as f:
f.write('some content')
st = os.stat(self.source)
- with patch("os.link", side_effect=OSError(0, "linking unsupported")):
+ with mock.patch("os.link", side_effect=OSError(0, "linking unsupported")):
copy_file(self.source, self.target, link='hard')
st2 = os.stat(self.source)
st3 = os.stat(self.target)
import os
import sys
-import unittest
import site
+import pathlib
from test.support import captured_stdout
@support.combine_markers
@pytest.mark.usefixtures('save_env')
-class InstallTestCase(
+class TestInstall(
support.TempdirManager,
support.LoggingSilencer,
- unittest.TestCase,
):
@pytest.mark.xfail(
'platform.system() == "Windows" and sys.version_info > (3, 11)',
check_path(cmd.install_scripts, os.path.join(destination, "bin"))
check_path(cmd.install_data, destination)
- def test_user_site(self):
+ def test_user_site(self, monkeypatch):
# test install with --user
# preparing the environment for the test
- self.old_user_base = site.USER_BASE
- self.old_user_site = site.USER_SITE
self.tmpdir = self.mkdtemp()
- self.user_base = os.path.join(self.tmpdir, 'B')
- self.user_site = os.path.join(self.tmpdir, 'S')
- site.USER_BASE = self.user_base
- site.USER_SITE = self.user_site
- install_module.USER_BASE = self.user_base
- install_module.USER_SITE = self.user_site
+ orig_site = site.USER_SITE
+ orig_base = site.USER_BASE
+ monkeypatch.setattr(site, 'USER_BASE', os.path.join(self.tmpdir, 'B'))
+ monkeypatch.setattr(site, 'USER_SITE', os.path.join(self.tmpdir, 'S'))
+ monkeypatch.setattr(install_module, 'USER_BASE', site.USER_BASE)
+ monkeypatch.setattr(install_module, 'USER_SITE', site.USER_SITE)
def _expanduser(path):
if path.startswith('~'):
return os.path.normpath(self.tmpdir + path[1:])
return path
- self.old_expand = os.path.expanduser
- os.path.expanduser = _expanduser
-
- def cleanup():
- site.USER_BASE = self.old_user_base
- site.USER_SITE = self.old_user_site
- install_module.USER_BASE = self.old_user_base
- install_module.USER_SITE = self.old_user_site
- os.path.expanduser = self.old_expand
-
- self.addCleanup(cleanup)
+ monkeypatch.setattr(os.path, 'expanduser', _expanduser)
for key in ('nt_user', 'posix_user'):
assert key in INSTALL_SCHEMES
cmd.user = 1
# user base and site shouldn't be created yet
- assert not os.path.exists(self.user_base)
- assert not os.path.exists(self.user_site)
+ assert not os.path.exists(site.USER_BASE)
+ assert not os.path.exists(site.USER_SITE)
# let's run finalize
cmd.ensure_finalized()
# now they should
- assert os.path.exists(self.user_base)
- assert os.path.exists(self.user_site)
+ assert os.path.exists(site.USER_BASE)
+ assert os.path.exists(site.USER_SITE)
assert 'userbase' in cmd.config_vars
assert 'usersite' in cmd.config_vars
- actual_headers = os.path.relpath(cmd.install_headers, self.user_base)
+ actual_headers = os.path.relpath(cmd.install_headers, site.USER_BASE)
if os.name == 'nt':
- site_path = os.path.relpath(
- os.path.dirname(self.old_user_site), self.old_user_base
- )
+ site_path = os.path.relpath(os.path.dirname(orig_site), orig_base)
include = os.path.join(site_path, 'Include')
else:
include = sysconfig.get_python_inc(0, '')
def test_record_extensions(self):
cmd = test_support.missing_compiler_executable()
if cmd is not None:
- self.skipTest('The %r command is not found' % cmd)
+ pytest.skip('The %r command is not found' % cmd)
install_dir = self.mkdtemp()
project_dir, dist = self.create_dist(
ext_modules=[Extension('xx', ['xxmodule.c'])]
cmd.ensure_finalized()
cmd.run()
- f = open(cmd.record)
- try:
- content = f.read()
- finally:
- f.close()
+ content = pathlib.Path(cmd.record).read_text()
found = [os.path.basename(line) for line in content.splitlines()]
expected = [
import sys
import os
import importlib.util
-import unittest
import pytest
cmd.finalize_options()
assert cmd.optimize == 2
- @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled')
+ @pytest.mark.skipif('sys.dont_write_bytecode')
def test_byte_compile(self):
project_dir, dist = self.create_dist()
os.chdir(project_dir)
import io
import sys
-import unittest
from test.support import swap_attr
+import pytest
+
from distutils import log
-class TestLog(unittest.TestCase):
- def test_non_ascii(self):
- # Issues #8663, #34421: test that non-encodable text is escaped with
- # backslashreplace error handler and encodable non-ASCII text is
- # output as is.
- for errors in (
+class TestLog:
+ @pytest.mark.parametrize(
+ 'errors',
+ (
'strict',
'backslashreplace',
'surrogateescape',
'replace',
'ignore',
- ):
- with self.subTest(errors=errors):
- stdout = io.TextIOWrapper(io.BytesIO(), encoding='cp437', errors=errors)
- stderr = io.TextIOWrapper(io.BytesIO(), encoding='cp437', errors=errors)
- old_threshold = log.set_threshold(log.DEBUG)
- try:
- with swap_attr(sys, 'stdout', stdout), swap_attr(
- sys, 'stderr', stderr
- ):
- log.debug('Dεbug\tMėssãge')
- log.fatal('Fαtal\tÈrrōr')
- finally:
- log.set_threshold(old_threshold)
+ ),
+ )
+ def test_non_ascii(self, errors):
+ # Issues #8663, #34421: test that non-encodable text is escaped with
+ # backslashreplace error handler and encodable non-ASCII text is
+ # output as is.
+ stdout = io.TextIOWrapper(io.BytesIO(), encoding='cp437', errors=errors)
+ stderr = io.TextIOWrapper(io.BytesIO(), encoding='cp437', errors=errors)
+ old_threshold = log.set_threshold(log.DEBUG)
+ try:
+ with swap_attr(sys, 'stdout', stdout), swap_attr(sys, 'stderr', stderr):
+ log.debug('Dεbug\tMėssãge')
+ log.fatal('Fαtal\tÈrrōr')
+ finally:
+ log.set_threshold(old_threshold)
- stdout.seek(0)
- assert stdout.read().rstrip() == (
- 'Dεbug\tM?ss?ge'
- if errors == 'replace'
- else 'Dεbug\tMssge'
- if errors == 'ignore'
- else 'Dεbug\tM\\u0117ss\\xe3ge'
- )
- stderr.seek(0)
- assert stderr.read().rstrip() == (
- 'Fαtal\t?rr?r'
- if errors == 'replace'
- else 'Fαtal\trrr'
- if errors == 'ignore'
- else 'Fαtal\t\\xc8rr\\u014dr'
- )
+ stdout.seek(0)
+ assert stdout.read().rstrip() == (
+ 'Dεbug\tM?ss?ge'
+ if errors == 'replace'
+ else 'Dεbug\tMssge'
+ if errors == 'ignore'
+ else 'Dεbug\tM\\u0117ss\\xe3ge'
+ )
+ stderr.seek(0)
+ assert stderr.read().rstrip() == (
+ 'Fαtal\t?rr?r'
+ if errors == 'replace'
+ else 'Fαtal\trrr'
+ if errors == 'ignore'
+ else 'Fαtal\t\\xc8rr\\u014dr'
+ )
"""Tests for distutils._msvccompiler."""
import sys
-import unittest
import os
import threading
+import unittest.mock as mock
import pytest
os.environ['DISTUTILS_USE_SDK'] = old_distutils_use_sdk
@needs_winreg
- def test_get_vc2017(self):
- # This function cannot be mocked, so pass it if we find VS 2017
- # and mark it skipped if we do not.
- version, path = _msvccompiler._find_vc2017()
- if version:
- assert version >= 15
- assert os.path.isdir(path)
- else:
- raise unittest.SkipTest("VS 2017 is not installed")
-
- @needs_winreg
- def test_get_vc2015(self):
- # This function cannot be mocked, so pass it if we find VS 2015
- # and mark it skipped if we do not.
- version, path = _msvccompiler._find_vc2015()
- if version:
- assert version >= 14
- assert os.path.isdir(path)
- else:
- raise unittest.SkipTest("VS 2015 is not installed")
+ @pytest.mark.parametrize('ver', (2015, 2017))
+ def test_get_vc(self, ver):
+ # This function cannot be mocked, so pass if VC is found
+ # and skip otherwise.
+ lookup = getattr(_msvccompiler, f'_find_vc{ver}')
+ expected_version = {2015: 14, 2017: 15}[ver]
+ version, path = lookup()
+ if not version:
+ pytest.skip(f"VS {ver} is not installed")
+ assert version >= expected_version
+ assert os.path.isdir(path)
class CheckThread(threading.Thread):
"A spawn without an env argument."
assert os.environ["PATH"] == "expected"
- with unittest.mock.patch.object(ccompiler.CCompiler, 'spawn', CCompiler_spawn):
+ with mock.patch.object(ccompiler.CCompiler, 'spawn', CCompiler_spawn):
compiler.spawn(["n/a"])
assert os.environ.get("PATH") != "expected"
"""Tests for distutils.command.register."""
import os
-import unittest
import getpass
import urllib
-import warnings
-
-
-from .py38compat import check_warnings
from distutils.command import register as register_module
from distutils.command.register import register
}.get(name.lower(), default)
-class RegisterTestCase(BasePyPIRCCommandTestCase):
- def setUp(self):
- super().setUp()
- # patching the password prompt
- self._old_getpass = getpass.getpass
+@pytest.fixture(autouse=True)
+def autopass(monkeypatch):
+ monkeypatch.setattr(getpass, 'getpass', lambda prompt: 'password')
- def _getpass(prompt):
- return 'password'
- getpass.getpass = _getpass
- urllib.request._opener = None
- self.old_opener = urllib.request.build_opener
- self.conn = urllib.request.build_opener = FakeOpener()
+@pytest.fixture(autouse=True)
+def fake_opener(monkeypatch, request):
+ opener = FakeOpener()
+ monkeypatch.setattr(urllib.request, 'build_opener', opener)
+ monkeypatch.setattr(urllib.request, '_opener', None)
+ request.instance.conn = opener
- def tearDown(self):
- getpass.getpass = self._old_getpass
- urllib.request._opener = None
- urllib.request.build_opener = self.old_opener
- super().tearDown()
+class TestRegister(BasePyPIRCCommandTestCase):
def _get_cmd(self, metadata=None):
if metadata is None:
metadata = {
'author_email': 'xxx',
'name': 'xxx',
'version': 'xxx',
+ 'long_description': 'xxx',
}
pkg_info, dist = self.create_dist(**metadata)
return register(dist)
req1 = dict(self.conn.reqs[0].headers)
req2 = dict(self.conn.reqs[1].headers)
- assert req1['Content-length'] == '1359'
- assert req2['Content-length'] == '1359'
+ assert req1['Content-length'] == '1358'
+ assert req2['Content-length'] == '1358'
assert b'xxx' in self.conn.reqs[1].data
def test_password_not_in_file(self):
assert headers['Content-length'] == '290'
assert b'tarek' in req.data
- @unittest.skipUnless(docutils is not None, 'needs docutils')
def test_strict(self):
- # testing the script option
+ # testing the strict option
# when on, the register command stops if
# the metadata is incomplete or if
# long_description is not reSt compliant
+ pytest.importorskip('docutils')
+
# empty metadata
cmd = self._get_cmd({})
cmd.ensure_finalized()
finally:
del register_module.input
- @unittest.skipUnless(docutils is not None, 'needs docutils')
- def test_register_invalid_long_description(self):
+ def test_register_invalid_long_description(self, monkeypatch):
+ pytest.importorskip('docutils')
description = ':funkie:`str`' # mimic Sphinx-specific markup
metadata = {
'url': 'xxx',
cmd.ensure_finalized()
cmd.strict = True
inputs = Inputs('2', 'tarek', 'tarek@ziade.org')
- register_module.input = inputs
- self.addCleanup(delattr, register_module, 'input')
+ monkeypatch.setattr(register_module, 'input', inputs, raising=False)
with pytest.raises(DistutilsSetupError):
cmd.run()
- def test_check_metadata_deprecated(self):
- # makes sure make_metadata is deprecated
- cmd = self._get_cmd()
- with check_warnings() as w:
- warnings.simplefilter("always")
- cmd.check_metadata()
- assert len(w.warnings) == 1
-
def test_list_classifiers(self):
cmd = self._get_cmd()
cmd.list_classifiers = 1
"""Tests for distutils.command.sdist."""
import os
import tarfile
-import unittest
import warnings
import zipfile
from os.path import join
from .unix_compat import require_unix_id, require_uid_0, pwd, grp
import pytest
+import path
+import jaraco.path
from .py38compat import check_warnings
from distutils.core import Distribution
from distutils.tests.test_config import BasePyPIRCCommandTestCase
from distutils.errors import DistutilsOptionError
-from distutils.spawn import find_executable
+from distutils.spawn import find_executable # noqa: F401
from distutils.log import WARN
from distutils.filelist import FileList
from distutils.archive_util import ARCHIVE_FORMATS
"""
-class SDistTestCase(BasePyPIRCCommandTestCase):
- def setUp(self):
- # PyPIRCCommandTestCase creates a temp dir already
- # and put it in self.tmp_dir
- super().setUp()
- # setting up an environment
- self.old_path = os.getcwd()
- os.mkdir(join(self.tmp_dir, 'somecode'))
- os.mkdir(join(self.tmp_dir, 'dist'))
- # a package, and a README
- self.write_file((self.tmp_dir, 'README'), 'xxx')
- self.write_file((self.tmp_dir, 'somecode', '__init__.py'), '#')
- self.write_file((self.tmp_dir, 'setup.py'), SETUP_PY)
- os.chdir(self.tmp_dir)
-
- def tearDown(self):
- # back to normal
- os.chdir(self.old_path)
- super().tearDown()
-
+@pytest.fixture(autouse=True)
+def project_dir(request, pypirc):
+ self = request.instance
+ jaraco.path.build(
+ {
+ 'somecode': {
+ '__init__.py': '#',
+ },
+ 'README': 'xxx',
+ 'setup.py': SETUP_PY,
+ },
+ self.tmp_dir,
+ )
+ with path.Path(self.tmp_dir):
+ yield
+
+
+class TestSDist(BasePyPIRCCommandTestCase):
def get_cmd(self, metadata=None):
"""Returns a cmd"""
if metadata is None:
assert sorted(content) == ['fake-1.0/' + x for x in expected]
@pytest.mark.usefixtures('needs_zlib')
- @unittest.skipIf(find_executable('tar') is None, "The tar command is not found")
- @unittest.skipIf(find_executable('gzip') is None, "The gzip command is not found")
+ @pytest.mark.skipif("not find_executable('tar')")
+ @pytest.mark.skipif("not find_executable('gzip')")
def test_make_distribution(self):
# now building a sdist
dist, cmd = self.get_cmd()
# this manifest command takes one argument
self._check_template('prune')
- @unittest.skipIf(os.name != 'nt', 'test relevant for Windows only')
+ @pytest.mark.skipif("platform.system() != 'Windows'")
def test_invalid_template_wrong_path(self):
# on Windows, trailing slashes are not allowed
# this used to crash instead of raising a warning: #8286
@pytest.mark.usefixtures('needs_zlib')
@require_unix_id
@require_uid_0
- @unittest.skipIf(find_executable('tar') is None, "The tar command is not found")
- @unittest.skipIf(find_executable('gzip') is None, "The gzip command is not found")
+ @pytest.mark.skipif("not find_executable('tar')")
+ @pytest.mark.skipif("not find_executable('gzip')")
def test_make_distribution_owner_group(self):
# now building a sdist
dist, cmd = self.get_cmd()
import os
import stat
import sys
-import unittest.mock
+import unittest.mock as mock
+
from test.support import unix_shell
from . import py38compat as os_helper
class TestSpawn(support.TempdirManager, support.LoggingSilencer):
- @unittest.skipUnless(os.name in ('nt', 'posix'), 'Runs only under posix or nt')
+ @pytest.mark.skipif("os.name not in ('nt', 'posix')")
def test_spawn(self):
tmpdir = self.mkdtemp()
# PATH='': no match, except in the current directory
with os_helper.EnvironmentVarGuard() as env:
env['PATH'] = ''
- with unittest.mock.patch(
+ with mock.patch(
'distutils.spawn.os.confstr', return_value=tmp_dir, create=True
- ), unittest.mock.patch('distutils.spawn.os.defpath', tmp_dir):
+ ), mock.patch('distutils.spawn.os.defpath', tmp_dir):
rv = find_executable(program)
assert rv is None
# PATH=':': explicitly looks in the current directory
with os_helper.EnvironmentVarGuard() as env:
env['PATH'] = os.pathsep
- with unittest.mock.patch(
+ with mock.patch(
'distutils.spawn.os.confstr', return_value='', create=True
- ), unittest.mock.patch('distutils.spawn.os.defpath', ''):
+ ), mock.patch('distutils.spawn.os.defpath', ''):
rv = find_executable(program)
assert rv is None
env.pop('PATH', None)
# without confstr
- with unittest.mock.patch(
+ with mock.patch(
'distutils.spawn.os.confstr', side_effect=ValueError, create=True
- ), unittest.mock.patch('distutils.spawn.os.defpath', tmp_dir):
+ ), mock.patch('distutils.spawn.os.defpath', tmp_dir):
rv = find_executable(program)
assert rv == filename
# with confstr
- with unittest.mock.patch(
+ with mock.patch(
'distutils.spawn.os.confstr', return_value=tmp_dir, create=True
- ), unittest.mock.patch('distutils.spawn.os.defpath', ''):
+ ), mock.patch('distutils.spawn.os.defpath', ''):
rv = find_executable(program)
assert rv == filename
import subprocess
import sys
import textwrap
-import unittest
import pytest
import jaraco.envs
import distutils
from distutils import sysconfig
-from distutils.ccompiler import get_default_compiler
+from distutils.ccompiler import get_default_compiler # noqa: F401
from distutils.unixccompiler import UnixCCompiler
from test.support import swap_item
@pytest.mark.usefixtures('save_env')
-class SysconfigTestCase(unittest.TestCase):
- def setUp(self):
- super().setUp()
- self.makefile = None
-
- def tearDown(self):
- if self.makefile is not None:
- os.unlink(self.makefile)
- self.cleanup_testfn()
- super().tearDown()
-
+@pytest.mark.usefixtures('cleanup_testfn')
+class TestSysconfig:
def cleanup_testfn(self):
if os.path.isfile(TESTFN):
os.remove(TESTFN)
config_h = sysconfig.get_config_h_filename()
assert os.path.isfile(config_h), config_h
- @unittest.skipIf(
- sys.platform == 'win32', 'Makefile only exists on Unix like systems'
- )
- @unittest.skipIf(
- sys.implementation.name != 'cpython', 'Makefile only exists in CPython'
- )
+ @pytest.mark.skipif("platform.system() == 'Windows'")
+ @pytest.mark.skipif("sys.implementation.name != 'cpython'")
def test_get_makefile_filename(self):
makefile = sysconfig.get_makefile_filename()
assert os.path.isfile(makefile), makefile
assert isinstance(cvars, dict)
assert cvars
- @unittest.skip('sysconfig.IS_PYPY')
+ @pytest.mark.skipif('sysconfig.IS_PYPY')
+ @pytest.mark.xfail(reason="broken")
def test_srcdir(self):
# See Issues #15322, #15364.
srcdir = sysconfig.get_config_var('srcdir')
return comp
- @unittest.skipUnless(
- get_default_compiler() == 'unix', 'not testing if default compiler is not unix'
- )
+ @pytest.mark.skipif("get_default_compiler() != 'unix'")
def test_customize_compiler(self):
# Make sure that sysconfig._config_vars is initialized
sysconfig.get_config_vars()
'LDFLAGS'
)
- @unittest.skipIf(
- sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'), 'compiler flags customized'
- )
+ @pytest.mark.skipif("sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER')")
def test_sysconfig_compiler_vars(self):
# On OS X, binary installers support extension module building on
# various levels of the operating system with differing Xcode
import sysconfig as global_sysconfig
if sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'):
- self.skipTest('compiler flags customized')
+ pytest.skip('compiler flags customized')
assert global_sysconfig.get_config_var('LDSHARED') == sysconfig.get_config_var(
'LDSHARED'
)
assert global_sysconfig.get_config_var('CC') == sysconfig.get_config_var('CC')
- @unittest.skipIf(
- sysconfig.get_config_var('EXT_SUFFIX') is None,
- 'EXT_SUFFIX required for this test',
- )
+ @pytest.mark.skipif("not sysconfig.get_config_var('EXT_SUFFIX')")
def test_SO_deprecation(self):
with pytest.warns(DeprecationWarning):
sysconfig.get_config_var('SO')
result = sysconfig.parse_config_h(f)
assert isinstance(result, dict)
- @unittest.skipUnless(sys.platform == 'win32', 'Testing windows pyd suffix')
- @unittest.skipUnless(
- sys.implementation.name == 'cpython', 'Need cpython for this test'
- )
+ @pytest.mark.skipif("platform.system() != 'Windows'")
+ @pytest.mark.skipif("sys.implementation.name != 'cpython'")
def test_win_ext_suffix(self):
assert sysconfig.get_config_var("EXT_SUFFIX").endswith(".pyd")
assert sysconfig.get_config_var("EXT_SUFFIX") != ".pyd"
- @unittest.skipUnless(sys.platform == 'win32', 'Testing Windows build layout')
- @unittest.skipUnless(
- sys.implementation.name == 'cpython', 'Need cpython for this test'
- )
- @unittest.skipUnless(
- '\\PCbuild\\'.casefold() in sys.executable.casefold(),
- 'Need sys.executable to be in a source tree',
+ @pytest.mark.skipif("platform.system() != 'Windows'")
+ @pytest.mark.skipif("sys.implementation.name != 'cpython'")
+ @pytest.mark.skipif(
+ '\\PCbuild\\'.casefold() not in sys.executable.casefold(),
+ reason='Need sys.executable to be in a source tree',
)
def test_win_build_venv_from_source_tree(self):
"""Ensure distutils.sysconfig detects venvs from source tree builds."""
"""Tests for distutils.unixccompiler."""
import os
import sys
-import unittest
-from unittest.mock import patch
+import unittest.mock as mock
from .py38compat import EnvironmentVarGuard
import pytest
-class UnixCCompilerTestCase(support.TempdirManager, unittest.TestCase):
- def setUp(self):
- super().setUp()
- self._backup_platform = sys.platform
- self._backup_get_config_var = sysconfig.get_config_var
- self._backup_get_config_vars = sysconfig.get_config_vars
+@pytest.fixture(autouse=True)
+def save_values(monkeypatch):
+ monkeypatch.setattr(sys, 'platform', sys.platform)
+ monkeypatch.setattr(sysconfig, 'get_config_var', sysconfig.get_config_var)
+ monkeypatch.setattr(sysconfig, 'get_config_vars', sysconfig.get_config_vars)
- class CompilerWrapper(UnixCCompiler):
- def rpath_foo(self):
- return self.runtime_library_dir_option('/foo')
- self.cc = CompilerWrapper()
+@pytest.fixture(autouse=True)
+def compiler_wrapper(request):
+ class CompilerWrapper(UnixCCompiler):
+ def rpath_foo(self):
+ return self.runtime_library_dir_option('/foo')
- def tearDown(self):
- super().tearDown()
- sys.platform = self._backup_platform
- sysconfig.get_config_var = self._backup_get_config_var
- sysconfig.get_config_vars = self._backup_get_config_vars
+ request.instance.cc = CompilerWrapper()
- @unittest.skipIf(sys.platform == 'win32', "can't test on Windows")
+
+class TestUnixCCompiler(support.TempdirManager):
+ @pytest.mark.skipif('platform.system == "Windows"') # noqa: C901
def test_runtime_libdir_option(self): # noqa: C901
# Issue #5900; GitHub Issue #37
#
sysconfig.get_config_var = gcv
assert self.cc.rpath_foo() == '-Wl,-R/foo'
- @unittest.skipIf(sys.platform == 'win32', "can't test on Windows")
+ @pytest.mark.skipif('platform.system == "Windows"')
def test_cc_overrides_ldshared(self):
# Issue #18080:
# ensure that setting CC env variable also changes default linker
sysconfig.customize_compiler(self.cc)
assert self.cc.linker_so[0] == 'my_cc'
- @unittest.skipIf(sys.platform == 'win32', "can't test on Windows")
+ @pytest.mark.skipif('platform.system == "Windows"')
def test_cc_overrides_ldshared_for_cxx_correctly(self):
"""
Ensure that setting CC env variable also changes default linker
sysconfig.get_config_var = gcv
sysconfig.get_config_vars = gcvs
- with patch.object(
+ with mock.patch.object(
self.cc, 'spawn', return_value=None
- ) as mock_spawn, patch.object(
+ ) as mock_spawn, mock.patch.object(
self.cc, '_need_link', return_value=True
- ), patch.object(
+ ), mock.patch.object(
self.cc, 'mkpath', return_value=None
), EnvironmentVarGuard() as env:
env['CC'] = 'ccache my_cc'
expected = ['my_cxx', '-bundle', '-undefined', 'dynamic_lookup']
assert call_args[:4] == expected
- @unittest.skipIf(sys.platform == 'win32', "can't test on Windows")
+ @pytest.mark.skipif('platform.system == "Windows"')
def test_explicit_ldshared(self):
# Issue #18080:
# ensure that setting CC env variable does not change
return self.code
-class uploadTestCase(BasePyPIRCCommandTestCase):
- def setUp(self):
- super().setUp()
- self.old_open = upload_mod.urlopen
- upload_mod.urlopen = self._urlopen
- self.last_open = None
- self.next_msg = None
- self.next_code = None
-
- def tearDown(self):
- upload_mod.urlopen = self.old_open
- super().tearDown()
+@pytest.fixture(autouse=True)
+def urlopen(request, monkeypatch):
+ self = request.instance
+ monkeypatch.setattr(upload_mod, 'urlopen', self._urlopen)
+ self.next_msg = self.next_code = None
+
+class TestUpload(BasePyPIRCCommandTestCase):
def _urlopen(self, url):
self.last_open = FakeOpen(url, msg=self.next_msg, code=self.next_code)
return self.last_open
with pytest.raises(DistutilsError):
self.test_upload()
- def test_wrong_exception_order(self):
+ @pytest.mark.parametrize(
+ 'exception,expected,raised_exception',
+ [
+ (OSError('oserror'), 'oserror', OSError),
+ pytest.param(
+ HTTPError('url', 400, 'httperror', {}, None),
+ 'Upload failed (400): httperror',
+ DistutilsError,
+ id="HTTP 400",
+ ),
+ ],
+ )
+ def test_wrong_exception_order(self, exception, expected, raised_exception):
tmp = self.mkdtemp()
path = os.path.join(tmp, 'xxx')
self.write_file(path)
self.write_file(self.rc, PYPIRC_LONG_PASSWORD)
pkg_dir, dist = self.create_dist(dist_files=dist_files)
- tests = [
- (OSError('oserror'), 'oserror', OSError),
- (
- HTTPError('url', 400, 'httperror', {}, None),
- 'Upload failed (400): httperror',
- DistutilsError,
- ),
- ]
- for exception, expected, raised_exception in tests:
- with self.subTest(exception=type(exception).__name__):
- with mock.patch(
- 'distutils.command.upload.urlopen',
- new=mock.Mock(side_effect=exception),
- ):
- with pytest.raises(raised_exception):
- cmd = upload(dist)
- cmd.ensure_finalized()
- cmd.run()
- results = self.get_logs(ERROR)
- assert expected in results[-1]
- self.clear_logs()
+
+ with mock.patch(
+ 'distutils.command.upload.urlopen',
+ new=mock.Mock(side_effect=exception),
+ ):
+ with pytest.raises(raised_exception):
+ cmd = upload(dist)
+ cmd.ensure_finalized()
+ cmd.run()
+ results = self.get_logs(ERROR)
+ assert expected in results[-1]
+ self.clear_logs()
"""Tests for distutils.util."""
import os
import sys
-import unittest
import sysconfig as stdlib_sysconfig
+import unittest.mock as mock
from copy import copy
-from unittest import mock
import pytest
grok_environment_error,
get_host_platform,
)
-from distutils import util # used to patch _environ_checked
+from distutils import util
from distutils import sysconfig
from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError
-@pytest.mark.usefixtures('save_env')
-class UtilTestCase(unittest.TestCase):
- def setUp(self):
- super().setUp()
- # saving the environment
- self.name = os.name
- self.platform = sys.platform
- self.version = sys.version
- self.sep = os.sep
- self.join = os.path.join
- self.isabs = os.path.isabs
- self.splitdrive = os.path.splitdrive
- self._config_vars = copy(sysconfig._config_vars)
-
- # patching os.uname
- if hasattr(os, 'uname'):
- self.uname = os.uname
- self._uname = os.uname()
- else:
- self.uname = None
- self._uname = None
-
- os.uname = self._get_uname
-
- def tearDown(self):
- # getting back the environment
- os.name = self.name
- sys.platform = self.platform
- sys.version = self.version
- os.sep = self.sep
- os.path.join = self.join
- os.path.isabs = self.isabs
- os.path.splitdrive = self.splitdrive
- if self.uname is not None:
- os.uname = self.uname
- else:
- del os.uname
- sysconfig._config_vars = copy(self._config_vars)
- super().tearDown()
-
- def _set_uname(self, uname):
- self._uname = uname
-
- def _get_uname(self):
- return self._uname
+@pytest.fixture(autouse=True)
+def environment(monkeypatch):
+ monkeypatch.setattr(os, 'name', os.name)
+ monkeypatch.setattr(sys, 'platform', sys.platform)
+ monkeypatch.setattr(sys, 'version', sys.version)
+ monkeypatch.setattr(os, 'sep', os.sep)
+ monkeypatch.setattr(os.path, 'join', os.path.join)
+ monkeypatch.setattr(os.path, 'isabs', os.path.isabs)
+ monkeypatch.setattr(os.path, 'splitdrive', os.path.splitdrive)
+ monkeypatch.setattr(sysconfig, '_config_vars', copy(sysconfig._config_vars))
+
+@pytest.mark.usefixtures('save_env')
+class TestUtil:
def test_get_host_platform(self):
- with unittest.mock.patch('os.name', 'nt'):
- with unittest.mock.patch('sys.version', '... [... (ARM64)]'):
+ with mock.patch('os.name', 'nt'):
+ with mock.patch('sys.version', '... [... (ARM64)]'):
assert get_host_platform() == 'win-arm64'
- with unittest.mock.patch('sys.version', '... [... (ARM)]'):
+ with mock.patch('sys.version', '... [... (ARM)]'):
assert get_host_platform() == 'win-arm32'
- with unittest.mock.patch('sys.version_info', (3, 9, 0, 'final', 0)):
+ with mock.patch('sys.version_info', (3, 9, 0, 'final', 0)):
assert get_host_platform() == stdlib_sysconfig.get_platform()
def test_get_platform(self):
- with unittest.mock.patch('os.name', 'nt'):
- with unittest.mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'x86'}):
+ with mock.patch('os.name', 'nt'):
+ with mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'x86'}):
assert get_platform() == 'win32'
- with unittest.mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'x64'}):
+ with mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'x64'}):
assert get_platform() == 'win-amd64'
- with unittest.mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'arm'}):
+ with mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'arm'}):
assert get_platform() == 'win-arm32'
- with unittest.mock.patch.dict(
- 'os.environ', {'VSCMD_ARG_TGT_ARCH': 'arm64'}
- ):
+ with mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'arm64'}):
assert get_platform() == 'win-arm64'
def test_convert_path(self):
assert os.environ['PLAT'] == get_platform()
assert util._environ_checked == 1
- @unittest.skipUnless(os.name == 'posix', 'specific to posix')
+ @pytest.mark.skipif("os.name != 'posix'")
def test_check_environ_getpwuid(self):
util._environ_checked = 0
os.environ.pop('HOME', None)
"""Tests for distutils.version."""
-import unittest
+import pytest
+
import distutils
from distutils.version import LooseVersion
from distutils.version import StrictVersion
-class VersionTestCase(unittest.TestCase):
- def setUp(self):
- self.ctx = distutils.version.suppress_known_deprecation()
- self.ctx.__enter__()
+@pytest.fixture(autouse=True)
+def suppress_deprecation():
+ with distutils.version.suppress_known_deprecation():
+ yield
- def tearDown(self):
- self.ctx.__exit__(None, None, None)
+class TestVersion:
def test_prerelease(self):
version = StrictVersion('1.2.3a1')
assert version.version == (1, 2, 3)
import sys
-import unittest
try:
import grp
except ImportError:
grp = pwd = None
+import pytest
+
UNIX_ID_SUPPORT = grp and pwd
UID_0_SUPPORT = UNIX_ID_SUPPORT and sys.platform != "cygwin"
-require_unix_id = unittest.skipUnless(UNIX_ID_SUPPORT, "Requires grp and pwd support")
-require_uid_0 = unittest.skipUnless(UID_0_SUPPORT, "Requires UID 0 support")
+require_unix_id = pytest.mark.skipif(
+ not UNIX_ID_SUPPORT, reason="Requires grp and pwd support"
+)
+require_uid_0 = pytest.mark.skipif(not UID_0_SUPPORT, reason="Requires UID 0 support")
--- /dev/null
+
+/* Use this file as a template to start implementing a module that
+ also declares object types. All occurrences of 'Xxo' should be changed
+ to something reasonable for your objects. After that, all other
+ occurrences of 'xx' should be changed to something reasonable for your
+ module. If your module is named foo your sourcefile should be named
+ foomodule.c.
+
+ You will probably want to delete all references to 'x_attr' and add
+ your own types of attributes instead. Maybe you want to name your
+ local variables other than 'self'. If your object type is needed in
+ other files, you'll have to create a file "foobarobject.h"; see
+ floatobject.h for an example. */
+
+/* Xxo objects */
+
+#include "Python.h"
+
+static PyObject *ErrorObject;
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *x_attr; /* Attributes dictionary */
+} XxoObject;
+
+static PyTypeObject Xxo_Type;
+
+#define XxoObject_Check(v) (Py_TYPE(v) == &Xxo_Type)
+
+static XxoObject *
+newXxoObject(PyObject *arg)
+{
+ XxoObject *self;
+ self = PyObject_New(XxoObject, &Xxo_Type);
+ if (self == NULL)
+ return NULL;
+ self->x_attr = NULL;
+ return self;
+}
+
+/* Xxo methods */
+
+static void
+Xxo_dealloc(XxoObject *self)
+{
+ Py_XDECREF(self->x_attr);
+ PyObject_Del(self);
+}
+
+static PyObject *
+Xxo_demo(XxoObject *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ":demo"))
+ return NULL;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef Xxo_methods[] = {
+ {"demo", (PyCFunction)Xxo_demo, METH_VARARGS,
+ PyDoc_STR("demo() -> None")},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject *
+Xxo_getattro(XxoObject *self, PyObject *name)
+{
+ if (self->x_attr != NULL) {
+ PyObject *v = PyDict_GetItemWithError(self->x_attr, name);
+ if (v != NULL) {
+ Py_INCREF(v);
+ return v;
+ }
+ else if (PyErr_Occurred()) {
+ return NULL;
+ }
+ }
+ return PyObject_GenericGetAttr((PyObject *)self, name);
+}
+
+static int
+Xxo_setattr(XxoObject *self, const char *name, PyObject *v)
+{
+ if (self->x_attr == NULL) {
+ self->x_attr = PyDict_New();
+ if (self->x_attr == NULL)
+ return -1;
+ }
+ if (v == NULL) {
+ int rv = PyDict_DelItemString(self->x_attr, name);
+ if (rv < 0 && PyErr_ExceptionMatches(PyExc_KeyError))
+ PyErr_SetString(PyExc_AttributeError,
+ "delete non-existing Xxo attribute");
+ return rv;
+ }
+ else
+ return PyDict_SetItemString(self->x_attr, name, v);
+}
+
+static PyTypeObject Xxo_Type = {
+ /* The ob_type field must be initialized in the module init function
+ * to be portable to Windows without using C++. */
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "xxmodule.Xxo", /*tp_name*/
+ sizeof(XxoObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)Xxo_dealloc, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ (getattrfunc)0, /*tp_getattr*/
+ (setattrfunc)Xxo_setattr, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ (getattrofunc)Xxo_getattro, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ Xxo_methods, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ 0, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+};
+/* --------------------------------------------------------------------- */
+
+/* Function of two integers returning integer */
+
+PyDoc_STRVAR(xx_foo_doc,
+"foo(i,j)\n\
+\n\
+Return the sum of i and j.");
+
+static PyObject *
+xx_foo(PyObject *self, PyObject *args)
+{
+ long i, j;
+ long res;
+ if (!PyArg_ParseTuple(args, "ll:foo", &i, &j))
+ return NULL;
+ res = i+j; /* XXX Do something here */
+ return PyLong_FromLong(res);
+}
+
+
+/* Function of no arguments returning new Xxo object */
+
+static PyObject *
+xx_new(PyObject *self, PyObject *args)
+{
+ XxoObject *rv;
+
+ if (!PyArg_ParseTuple(args, ":new"))
+ return NULL;
+ rv = newXxoObject(args);
+ if (rv == NULL)
+ return NULL;
+ return (PyObject *)rv;
+}
+
+/* Example with subtle bug from extensions manual ("Thin Ice"). */
+
+static PyObject *
+xx_bug(PyObject *self, PyObject *args)
+{
+ PyObject *list, *item;
+
+ if (!PyArg_ParseTuple(args, "O:bug", &list))
+ return NULL;
+
+ item = PyList_GetItem(list, 0);
+ /* Py_INCREF(item); */
+ PyList_SetItem(list, 1, PyLong_FromLong(0L));
+ PyObject_Print(item, stdout, 0);
+ printf("\n");
+ /* Py_DECREF(item); */
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Test bad format character */
+
+static PyObject *
+xx_roj(PyObject *self, PyObject *args)
+{
+ PyObject *a;
+ long b;
+ if (!PyArg_ParseTuple(args, "O#:roj", &a, &b))
+ return NULL;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+/* ---------- */
+
+static PyTypeObject Str_Type = {
+ /* The ob_type field must be initialized in the module init function
+ * to be portable to Windows without using C++. */
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "xxmodule.Str", /*tp_name*/
+ 0, /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ 0, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /* see PyInit_xx */ /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ 0, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+};
+
+/* ---------- */
+
+static PyObject *
+null_richcompare(PyObject *self, PyObject *other, int op)
+{
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+}
+
+static PyTypeObject Null_Type = {
+ /* The ob_type field must be initialized in the module init function
+ * to be portable to Windows without using C++. */
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "xxmodule.Null", /*tp_name*/
+ 0, /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ 0, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ null_richcompare, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /* see PyInit_xx */ /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ PyType_GenericNew, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+};
+
+
+/* ---------- */
+
+
+/* List of functions defined in the module */
+
+static PyMethodDef xx_methods[] = {
+ {"roj", xx_roj, METH_VARARGS,
+ PyDoc_STR("roj(a,b) -> None")},
+ {"foo", xx_foo, METH_VARARGS,
+ xx_foo_doc},
+ {"new", xx_new, METH_VARARGS,
+ PyDoc_STR("new() -> new Xx object")},
+ {"bug", xx_bug, METH_VARARGS,
+ PyDoc_STR("bug(o) -> None")},
+ {NULL, NULL} /* sentinel */
+};
+
+PyDoc_STRVAR(module_doc,
+"This is a template module just for instruction.");
+
+
+static int
+xx_exec(PyObject *m)
+{
+ /* Slot initialization is subject to the rules of initializing globals.
+ C99 requires the initializers to be "address constants". Function
+ designators like 'PyType_GenericNew', with implicit conversion to
+ a pointer, are valid C99 address constants.
+
+ However, the unary '&' operator applied to a non-static variable
+ like 'PyBaseObject_Type' is not required to produce an address
+ constant. Compilers may support this (gcc does), MSVC does not.
+
+ Both compilers are strictly standard conforming in this particular
+ behavior.
+ */
+ Null_Type.tp_base = &PyBaseObject_Type;
+ Str_Type.tp_base = &PyUnicode_Type;
+
+ /* Finalize the type object including setting type of the new type
+ * object; doing it here is required for portability, too. */
+ if (PyType_Ready(&Xxo_Type) < 0)
+ goto fail;
+
+ /* Add some symbolic constants to the module */
+ if (ErrorObject == NULL) {
+ ErrorObject = PyErr_NewException("xx.error", NULL, NULL);
+ if (ErrorObject == NULL)
+ goto fail;
+ }
+ Py_INCREF(ErrorObject);
+ PyModule_AddObject(m, "error", ErrorObject);
+
+ /* Add Str */
+ if (PyType_Ready(&Str_Type) < 0)
+ goto fail;
+ PyModule_AddObject(m, "Str", (PyObject *)&Str_Type);
+
+ /* Add Null */
+ if (PyType_Ready(&Null_Type) < 0)
+ goto fail;
+ PyModule_AddObject(m, "Null", (PyObject *)&Null_Type);
+ return 0;
+ fail:
+ Py_XDECREF(m);
+ return -1;
+}
+
+static struct PyModuleDef_Slot xx_slots[] = {
+ {Py_mod_exec, xx_exec},
+ {0, NULL},
+};
+
+static struct PyModuleDef xxmodule = {
+ PyModuleDef_HEAD_INIT,
+ "xx",
+ module_doc,
+ 0,
+ xx_methods,
+ xx_slots,
+ NULL,
+ NULL,
+ NULL
+};
+
+/* Export function for the module (*must* be called PyInit_xx) */
+
+PyMODINIT_FUNC
+PyInit_xx(void)
+{
+ return PyModuleDef_Init(&xxmodule);
+}
--- /dev/null
+
+/* Use this file as a template to start implementing a module that
+ also declares object types. All occurrences of 'Xxo' should be changed
+ to something reasonable for your objects. After that, all other
+ occurrences of 'xx' should be changed to something reasonable for your
+ module. If your module is named foo your sourcefile should be named
+ foomodule.c.
+
+ You will probably want to delete all references to 'x_attr' and add
+ your own types of attributes instead. Maybe you want to name your
+ local variables other than 'self'. If your object type is needed in
+ other files, you'll have to create a file "foobarobject.h"; see
+ floatobject.h for an example. */
+
+/* Xxo objects */
+
+#include "Python.h"
+
+static PyObject *ErrorObject;
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *x_attr; /* Attributes dictionary */
+} XxoObject;
+
+static PyTypeObject Xxo_Type;
+
+#define XxoObject_Check(v) Py_IS_TYPE(v, &Xxo_Type)
+
+static XxoObject *
+newXxoObject(PyObject *arg)
+{
+ XxoObject *self;
+ self = PyObject_New(XxoObject, &Xxo_Type);
+ if (self == NULL)
+ return NULL;
+ self->x_attr = NULL;
+ return self;
+}
+
+/* Xxo methods */
+
+static void
+Xxo_dealloc(XxoObject *self)
+{
+ Py_XDECREF(self->x_attr);
+ PyObject_Free(self);
+}
+
+static PyObject *
+Xxo_demo(XxoObject *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ":demo"))
+ return NULL;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef Xxo_methods[] = {
+ {"demo", (PyCFunction)Xxo_demo, METH_VARARGS,
+ PyDoc_STR("demo() -> None")},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject *
+Xxo_getattro(XxoObject *self, PyObject *name)
+{
+ if (self->x_attr != NULL) {
+ PyObject *v = PyDict_GetItemWithError(self->x_attr, name);
+ if (v != NULL) {
+ Py_INCREF(v);
+ return v;
+ }
+ else if (PyErr_Occurred()) {
+ return NULL;
+ }
+ }
+ return PyObject_GenericGetAttr((PyObject *)self, name);
+}
+
+static int
+Xxo_setattr(XxoObject *self, const char *name, PyObject *v)
+{
+ if (self->x_attr == NULL) {
+ self->x_attr = PyDict_New();
+ if (self->x_attr == NULL)
+ return -1;
+ }
+ if (v == NULL) {
+ int rv = PyDict_DelItemString(self->x_attr, name);
+ if (rv < 0 && PyErr_ExceptionMatches(PyExc_KeyError))
+ PyErr_SetString(PyExc_AttributeError,
+ "delete non-existing Xxo attribute");
+ return rv;
+ }
+ else
+ return PyDict_SetItemString(self->x_attr, name, v);
+}
+
+static PyTypeObject Xxo_Type = {
+ /* The ob_type field must be initialized in the module init function
+ * to be portable to Windows without using C++. */
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "xxmodule.Xxo", /*tp_name*/
+ sizeof(XxoObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)Xxo_dealloc, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ (getattrfunc)0, /*tp_getattr*/
+ (setattrfunc)Xxo_setattr, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ (getattrofunc)Xxo_getattro, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ Xxo_methods, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ 0, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+};
+/* --------------------------------------------------------------------- */
+
+/* Function of two integers returning integer */
+
+PyDoc_STRVAR(xx_foo_doc,
+"foo(i,j)\n\
+\n\
+Return the sum of i and j.");
+
+static PyObject *
+xx_foo(PyObject *self, PyObject *args)
+{
+ long i, j;
+ long res;
+ if (!PyArg_ParseTuple(args, "ll:foo", &i, &j))
+ return NULL;
+ res = i+j; /* XXX Do something here */
+ return PyLong_FromLong(res);
+}
+
+
+/* Function of no arguments returning new Xxo object */
+
+static PyObject *
+xx_new(PyObject *self, PyObject *args)
+{
+ XxoObject *rv;
+
+ if (!PyArg_ParseTuple(args, ":new"))
+ return NULL;
+ rv = newXxoObject(args);
+ if (rv == NULL)
+ return NULL;
+ return (PyObject *)rv;
+}
+
+/* Example with subtle bug from extensions manual ("Thin Ice"). */
+
+static PyObject *
+xx_bug(PyObject *self, PyObject *args)
+{
+ PyObject *list, *item;
+
+ if (!PyArg_ParseTuple(args, "O:bug", &list))
+ return NULL;
+
+ item = PyList_GetItem(list, 0);
+ /* Py_INCREF(item); */
+ PyList_SetItem(list, 1, PyLong_FromLong(0L));
+ PyObject_Print(item, stdout, 0);
+ printf("\n");
+ /* Py_DECREF(item); */
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Test bad format character */
+
+static PyObject *
+xx_roj(PyObject *self, PyObject *args)
+{
+ PyObject *a;
+ long b;
+ if (!PyArg_ParseTuple(args, "O#:roj", &a, &b))
+ return NULL;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+/* ---------- */
+
+static PyTypeObject Str_Type = {
+ /* The ob_type field must be initialized in the module init function
+ * to be portable to Windows without using C++. */
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "xxmodule.Str", /*tp_name*/
+ 0, /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ 0, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /* see PyInit_xx */ /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ 0, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+};
+
+/* ---------- */
+
+static PyObject *
+null_richcompare(PyObject *self, PyObject *other, int op)
+{
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+}
+
+static PyTypeObject Null_Type = {
+ /* The ob_type field must be initialized in the module init function
+ * to be portable to Windows without using C++. */
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "xxmodule.Null", /*tp_name*/
+ 0, /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ 0, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ null_richcompare, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /* see PyInit_xx */ /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ PyType_GenericNew, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+};
+
+
+/* ---------- */
+
+
+/* List of functions defined in the module */
+
+static PyMethodDef xx_methods[] = {
+ {"roj", xx_roj, METH_VARARGS,
+ PyDoc_STR("roj(a,b) -> None")},
+ {"foo", xx_foo, METH_VARARGS,
+ xx_foo_doc},
+ {"new", xx_new, METH_VARARGS,
+ PyDoc_STR("new() -> new Xx object")},
+ {"bug", xx_bug, METH_VARARGS,
+ PyDoc_STR("bug(o) -> None")},
+ {NULL, NULL} /* sentinel */
+};
+
+PyDoc_STRVAR(module_doc,
+"This is a template module just for instruction.");
+
+
+static int
+xx_exec(PyObject *m)
+{
+ /* Slot initialization is subject to the rules of initializing globals.
+ C99 requires the initializers to be "address constants". Function
+ designators like 'PyType_GenericNew', with implicit conversion to
+ a pointer, are valid C99 address constants.
+
+ However, the unary '&' operator applied to a non-static variable
+ like 'PyBaseObject_Type' is not required to produce an address
+ constant. Compilers may support this (gcc does), MSVC does not.
+
+ Both compilers are strictly standard conforming in this particular
+ behavior.
+ */
+ Null_Type.tp_base = &PyBaseObject_Type;
+ Str_Type.tp_base = &PyUnicode_Type;
+
+ /* Finalize the type object including setting type of the new type
+ * object; doing it here is required for portability, too. */
+ if (PyType_Ready(&Xxo_Type) < 0) {
+ return -1;
+ }
+
+ /* Add some symbolic constants to the module */
+ if (ErrorObject == NULL) {
+ ErrorObject = PyErr_NewException("xx.error", NULL, NULL);
+ if (ErrorObject == NULL) {
+ return -1;
+ }
+ }
+ int rc = PyModule_AddType(m, (PyTypeObject *)ErrorObject);
+ Py_DECREF(ErrorObject);
+ if (rc < 0) {
+ return -1;
+ }
+
+ /* Add Str and Null types */
+ if (PyModule_AddType(m, &Str_Type) < 0) {
+ return -1;
+ }
+ if (PyModule_AddType(m, &Null_Type) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct PyModuleDef_Slot xx_slots[] = {
+ {Py_mod_exec, xx_exec},
+ {0, NULL},
+};
+
+static struct PyModuleDef xxmodule = {
+ PyModuleDef_HEAD_INIT,
+ "xx",
+ module_doc,
+ 0,
+ xx_methods,
+ xx_slots,
+ NULL,
+ NULL,
+ NULL
+};
+
+/* Export function for the module (*must* be called PyInit_xx) */
+
+PyMODINIT_FUNC
+PyInit_xx(void)
+{
+ return PyModuleDef_Init(&xxmodule);
+}