Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into issue-219
Browse files Browse the repository at this point in the history
  • Loading branch information
zooba committed Jan 10, 2024
2 parents f24fc22 + fb5c570 commit fbe8abe
Show file tree
Hide file tree
Showing 15 changed files with 184 additions and 48 deletions.
16 changes: 13 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
name: tests

on: [push, pull_request]
on:
merge_group:
push:
branches-ignore:
# disabled for jaraco/skeleton#103
# - gh-readonly-queue/** # Temporary merge queue-related GH-made branches
pull_request:

concurrency:
group: >-
Expand Down Expand Up @@ -62,7 +68,9 @@ jobs:
strategy:
fail-fast: false
matrix:
job: [diffcov, docs]
job:
- diffcov
- docs
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.python == '3.12' }}
steps:
Expand Down Expand Up @@ -102,6 +110,7 @@ jobs:
gcc-core,
gcc-g++,
ncompress
git
- name: Run tests
shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0}
run: tox
Expand Down Expand Up @@ -151,7 +160,8 @@ jobs:
needs:
- test
- collateral
- test_cygwin
# disabled due to disabled job
# - test_cygwin

runs-on: ubuntu-latest

Expand Down
7 changes: 4 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
repos:
- repo: https://github.com/psf/black
rev: 22.6.0
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.8
hooks:
- id: black
- id: ruff
- id: ruff-format
6 changes: 1 addition & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,10 @@
:target: https://github.com/astral-sh/ruff
:alt: Ruff

.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
:alt: Code style: Black

.. .. image:: https://readthedocs.org/projects/PROJECT_RTD/badge/?version=latest
.. :target: https://PROJECT_RTD.readthedocs.io/en/latest/?badge=latest
.. image:: https://img.shields.io/badge/skeleton-2023-informational
.. image:: https://img.shields.io/badge/skeleton-2024-informational
:target: https://blog.jaraco.com/skeleton

Python Module Distribution Utilities extracted from the Python Standard Library
Expand Down
32 changes: 32 additions & 0 deletions distutils/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,35 @@
distutils.command.tests package, since command identification is done
by import rather than matching pre-defined names.
"""

def missing_compiler_executable(cmd_names=[]): # pragma: no cover
"""Check if the compiler components used to build the interpreter exist.
Check for the existence of the compiler executables whose names are listed
in 'cmd_names' or all the compiler executables when 'cmd_names' is empty
and return the first missing executable or None when none is found
missing.
"""
from distutils import ccompiler, sysconfig, spawn
from distutils import errors

compiler = ccompiler.new_compiler()
sysconfig.customize_compiler(compiler)
if compiler.compiler_type == "msvc":
# MSVC has no executables, so check whether initialization succeeds
try:
compiler.initialize()
except errors.PlatformError:
return "msvc"
for name in compiler.executables:
if cmd_names and name not in cmd_names:
continue
cmd = getattr(compiler, name)
if cmd_names:
assert cmd is not None, \
"the '%s' executable is not configured" % name
elif not cmd:
continue
if spawn.find_executable(cmd[0]) is None:
return cmd[0]
4 changes: 1 addition & 3 deletions distutils/tests/test_build_clib.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
"""Tests for distutils.command.build_clib."""
import os

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
from distutils.tests import support, missing_compiler_executable


class TestBuildCLib(support.TempdirManager):
Expand Down
5 changes: 3 additions & 2 deletions distutils/tests/test_build_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from distutils.core import Distribution
from distutils.command.build_ext import build_ext
from distutils import sysconfig
from distutils.tests import missing_compiler_executable
from distutils.tests.support import (
TempdirManager,
copy_xxmodule_c,
Expand Down Expand Up @@ -89,7 +90,7 @@ def build_ext(self, *args, **kwargs):
return build_ext(*args, **kwargs)

def test_build_ext(self):
cmd = support.missing_compiler_executable()
missing_compiler_executable()
copy_xxmodule_c(self.tmp_dir)
xx_c = os.path.join(self.tmp_dir, 'xxmodule.c')
xx_ext = Extension('xx', [xx_c])
Expand Down Expand Up @@ -359,7 +360,7 @@ def test_compiler_option(self):
assert cmd.compiler == 'unix'

def test_get_outputs(self):
cmd = support.missing_compiler_executable()
missing_compiler_executable()
tmp_dir = self.mkdtemp()
c_file = os.path.join(tmp_dir, 'foo.c')
self.write_file(c_file, 'void PyInit_foo(void) {}\n')
Expand Down
3 changes: 1 addition & 2 deletions distutils/tests/test_config_cmd.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
"""Tests for distutils.command.config."""
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.tests import support, missing_compiler_executable
from distutils._log import log


Expand Down
41 changes: 41 additions & 0 deletions distutils/tests/test_dist.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
"""Tests for distutils.dist."""
import os
import io
import email
import email.policy
import email.generator
import sys
import warnings
import textwrap
Expand Down Expand Up @@ -510,3 +513,41 @@ def test_read_metadata(self):
assert metadata.platforms is None
assert metadata.obsoletes is None
assert metadata.requires == ['foo']

def test_round_trip_through_email_generator(self):
"""
In pypa/setuptools#4033, it was shown that once PKG-INFO is
re-generated using ``email.generator.Generator``, some control
characters might cause problems.
"""
# Given a PKG-INFO file ...
attrs = {
"name": "package",
"version": "1.0",
"long_description": "hello\x0b\nworld\n",
}
dist = Distribution(attrs)
metadata = dist.metadata

with io.StringIO() as buffer:
metadata.write_pkg_file(buffer)
msg = buffer.getvalue()

# ... when it is read and re-written using stdlib's email library,
orig = email.message_from_string(msg)
policy = email.policy.EmailPolicy(
utf8=True,
mangle_from_=False,
max_line_length=0,
)
with io.StringIO() as buffer:
email.generator.Generator(buffer, policy=policy).flatten(orig)

buffer.seek(0)
regen = email.message_from_file(buffer)

# ... then it should be the same as the original
# (except for the specific line break characters)
orig_desc = set(orig["Description"].splitlines())
regen_desc = set(regen["Description"].splitlines())
assert regen_desc == orig_desc
5 changes: 2 additions & 3 deletions distutils/tests/test_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
from distutils.errors import DistutilsOptionError
from distutils.extension import Extension

from distutils.tests import support
from test import support as test_support
from distutils.tests import support, missing_compiler_executable


def _make_ext_name(modname):
Expand Down Expand Up @@ -213,7 +212,7 @@ def test_record(self):
assert found == expected

def test_record_extensions(self):
cmd = test_support.missing_compiler_executable()
cmd = missing_compiler_executable()
if cmd is not None:
pytest.skip('The %r command is not found' % cmd)
install_dir = self.mkdtemp()
Expand Down
59 changes: 53 additions & 6 deletions distutils/tests/test_util.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
"""Tests for distutils.util."""
import email
import email.policy
import email.generator
import io
import os
import sys
import sysconfig as stdlib_sysconfig
Expand Down Expand Up @@ -184,12 +188,55 @@ def test_strtobool(self):
for n in no:
assert not strtobool(n)

def test_rfc822_escape(self):
header = 'I am a\npoor\nlonesome\nheader\n'
res = rfc822_escape(header)
wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s' 'header%(8s)s') % {
'8s': '\n' + 8 * ' '
}
indent = 8 * ' '

@pytest.mark.parametrize(
"given,wanted",
[
# 0x0b, 0x0c, ..., etc are also considered a line break by Python
("hello\x0b\nworld\n", f"hello\x0b{indent}\n{indent}world\n{indent}"),
("hello\x1eworld", f"hello\x1e{indent}world"),
("", ""),
(
"I am a\npoor\nlonesome\nheader\n",
f"I am a\n{indent}poor\n{indent}lonesome\n{indent}header\n{indent}",
),
],
)
def test_rfc822_escape(self, given, wanted):
"""
We want to ensure a multi-line header parses correctly.
For interoperability, the escaped value should also "round-trip" over
`email.generator.Generator.flatten` and `email.message_from_*`
(see pypa/setuptools#4033).
The main issue is that internally `email.policy.EmailPolicy` uses
`splitlines` which will split on some control chars. If all the new lines
are not prefixed with spaces, the parser will interrupt reading
the current header and produce an incomplete value, while
incorrectly interpreting the rest of the headers as part of the payload.
"""
res = rfc822_escape(given)

policy = email.policy.EmailPolicy(
utf8=True,
mangle_from_=False,
max_line_length=0,
)
with io.StringIO() as buffer:
raw = f"header: {res}\nother-header: 42\n\npayload\n"
orig = email.message_from_string(raw)
email.generator.Generator(buffer, policy=policy).flatten(orig)
buffer.seek(0)
regen = email.message_from_file(buffer)

for msg in (orig, regen):
assert msg.get_payload() == "payload\n"
assert msg["other-header"] == "42"
# Generator may replace control chars with `\n`
assert set(msg["header"].splitlines()) == set(res.splitlines())

assert res == wanted

def test_dont_write_bytecode(self):
Expand Down
12 changes: 9 additions & 3 deletions distutils/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,12 @@ def rfc822_escape(header):
"""Return a version of the string escaped for inclusion in an
RFC-822 header, by ensuring there are 8 spaces space after each newline.
"""
lines = header.split('\n')
sep = '\n' + 8 * ' '
return sep.join(lines)
indent = 8 * " "
lines = header.splitlines(keepends=True)

# Emulate the behaviour of `str.split`
# (the terminal line break in `splitlines` does not result in an extra line):
ends_in_newline = lines and lines[-1].splitlines()[0] != lines[-1]
suffix = indent if ends_in_newline else ""

return indent.join(lines) + suffix
3 changes: 0 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,4 @@
requires = ["setuptools>=56", "setuptools_scm[toml]>=3.4.1"]
build-backend = "setuptools.build_meta"

[tool.black]
skip-string-normalization = true

[tool.setuptools_scm]
8 changes: 0 additions & 8 deletions pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,6 @@ filterwarnings=
# Ensure ResourceWarnings are emitted
default::ResourceWarning

# shopkeep/pytest-black#55
ignore:<class 'pytest_black.BlackItem'> is not using a cooperative constructor:pytest.PytestDeprecationWarning
ignore:The \(fspath. py.path.local\) argument to BlackItem is deprecated.:pytest.PytestDeprecationWarning
ignore:BlackItem is an Item subclass and should not be a collector:pytest.PytestWarning

# shopkeep/pytest-black#67
ignore:'encoding' argument not specified::pytest_black

# realpython/pytest-mypy#152
ignore:'encoding' argument not specified::pytest_mypy

Expand Down
22 changes: 22 additions & 0 deletions ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[lint]
ignore = [
# https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
"W191",
"E111",
"E114",
"E117",
"D206",
"D300",
"Q000",
"Q001",
"Q002",
"Q003",
"COM812",
"COM819",
"ISC001",
"ISC002",
]

[format]
# https://docs.astral.sh/ruff/settings/#format-quote-style
quote-style = "preserve"
9 changes: 2 additions & 7 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ install_requires =

[options.packages.find]
exclude =
build*
dist*
docs*
tests*

Expand All @@ -30,16 +28,13 @@ testing =
# upstream
pytest >= 6
pytest-checkdocs >= 2.4
pytest-black >= 0.3.7; \
# workaround for jaraco/skeleton#22
python_implementation != "PyPy"
pytest-cov
# disabled
#pytest-mypy >= 0.9.1; \
#pytest-mypy; \
# # workaround for jaraco/skeleton#22
# python_implementation != "PyPy"
pytest-enabler >= 2.2
pytest-ruff
pytest-ruff >= 0.2.1

# local
pytest >= 7.4.3 #186
Expand Down

0 comments on commit fbe8abe

Please sign in to comment.