Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Lib/test/datetimetester.py
Original file line number Diff line number Diff line change
Expand Up @@ -3758,6 +3758,10 @@ def test_fromisoformat_fails_datetime(self):
'2009-04-19T12:30:45-00:90:00', # Time zone field out from range
'2009-04-19T12:30:45-00:00:90', # Time zone field out from range
'2020-2020', # Ambiguous 9-char date portion
'2009-04-19T12:30:45.+05:00', # Empty fraction before offset
'2009-04-19T12:30:45.-05:00', # Empty fraction before offset
'2009-04-19T12:30:45.Z', # Empty fraction before Z
'2009-04-19T12:30:45,+05:00', # Empty fraction (comma) before offset
]

for bad_str in bad_strs:
Expand Down Expand Up @@ -5034,6 +5038,10 @@ def test_fromisoformat_fails(self):
'24:01:00.000000', # Has non-zero minutes on 24:00
'12:30:45+00:90:00', # Time zone field out from range
'12:30:45+00:00:90', # Time zone field out from range
'12:30:45.+05:00', # Empty fraction before offset
'12:30:45.-05:00', # Empty fraction before offset
'12:30:45.Z', # Empty fraction before Z
'12:30:45,+05:00', # Empty fraction (comma) before offset
]

for bad_str in bad_strs:
Expand Down
65 changes: 45 additions & 20 deletions Lib/test/test_compileall.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,24 @@ def temporary_pycache_prefix(self):
finally:
sys.pycache_prefix = old_prefix

@contextlib.contextmanager
def no_pycache_prefix(self):
"""Ignore any ambient pycache prefix for the duration of the test.

Some tests assume bytecode is written next to the source in a
__pycache__ directory. When the test suite is run with
PYTHONPYCACHEPREFIX set, neutralize it both in this process (used by
cache_from_source) and in any spawned subprocesses.
"""
old_prefix = sys.pycache_prefix
sys.pycache_prefix = None
try:
with os_helper.EnvironmentVarGuard() as env:
env.unset('PYTHONPYCACHEPREFIX')
yield
finally:
sys.pycache_prefix = old_prefix

def _get_run_args(self, args):
return [*support.optim_args_from_interpreter_flags(),
'-S', '-m', 'compileall',
Expand Down Expand Up @@ -650,15 +668,16 @@ def test_legacy_paths(self):
def test_multiple_runs(self):
# Bug 8527 reported that multiple calls produced empty
# __pycache__/__pycache__ directories.
self.assertRunOK('-q', self.pkgdir)
# Verify the __pycache__ directory contents.
self.assertTrue(os.path.exists(self.pkgdir_cachedir))
cachecachedir = os.path.join(self.pkgdir_cachedir, '__pycache__')
self.assertFalse(os.path.exists(cachecachedir))
# Call compileall again.
self.assertRunOK('-q', self.pkgdir)
self.assertTrue(os.path.exists(self.pkgdir_cachedir))
self.assertFalse(os.path.exists(cachecachedir))
with self.no_pycache_prefix():
self.assertRunOK('-q', self.pkgdir)
# Verify the __pycache__ directory contents.
self.assertTrue(os.path.exists(self.pkgdir_cachedir))
cachecachedir = os.path.join(self.pkgdir_cachedir, '__pycache__')
self.assertFalse(os.path.exists(cachecachedir))
# Call compileall again.
self.assertRunOK('-q', self.pkgdir)
self.assertTrue(os.path.exists(self.pkgdir_cachedir))
self.assertFalse(os.path.exists(cachecachedir))

@without_source_date_epoch # timestamp invalidation test
def test_force(self):
Expand Down Expand Up @@ -731,10 +750,13 @@ def test_symlink_loop(self):
script_helper.make_pkg(pkg)
os.symlink('.', os.path.join(pkg, 'evil'))
os.symlink('.', os.path.join(pkg, 'evil2'))
self.assertRunOK('-q', self.pkgdir)
self.assertCompiled(os.path.join(
self.pkgdir, 'spam', 'evil', 'evil2', '__init__.py'
))
# This relies on the __pycache__ layout (shared across the symlinked
# paths), so neutralize any ambient PYTHONPYCACHEPREFIX.
with self.no_pycache_prefix():
self.assertRunOK('-q', self.pkgdir)
self.assertCompiled(os.path.join(
self.pkgdir, 'spam', 'evil', 'evil2', '__init__.py'
))

def test_quiet(self):
noisy = self.assertRunOK(self.pkgdir)
Expand Down Expand Up @@ -821,13 +843,16 @@ def test_include_on_stdin(self):
f2 = script_helper.make_script(self.pkgdir, 'f2', '')
f3 = script_helper.make_script(self.pkgdir, 'f3', '')
f4 = script_helper.make_script(self.pkgdir, 'f4', '')
p = script_helper.spawn_python(*(self._get_run_args(()) + ['-i', '-']))
p.stdin.write((f3+os.linesep).encode('ascii'))
script_helper.kill_python(p)
self.assertNotCompiled(f1)
self.assertNotCompiled(f2)
self.assertCompiled(f3)
self.assertNotCompiled(f4)
# spawn_python() runs with -E, ignoring PYTHONPYCACHEPREFIX, so make
# cache_from_source() in this process agree by neutralizing it too.
with self.no_pycache_prefix():
p = script_helper.spawn_python(*(self._get_run_args(()) + ['-i', '-']))
p.stdin.write((f3+os.linesep).encode('ascii'))
script_helper.kill_python(p)
self.assertNotCompiled(f1)
self.assertNotCompiled(f2)
self.assertCompiled(f3)
self.assertNotCompiled(f4)

def test_compiles_as_much_as_possible(self):
bingfn = script_helper.make_script(self.pkgdir, 'bing', 'syntax(error')
Expand Down
41 changes: 25 additions & 16 deletions Lib/test/test_curses.py
Original file line number Diff line number Diff line change
Expand Up @@ -2332,27 +2332,36 @@ def test_textbox_8bit_fill_last_cell(self):
def test_textbox_unicode(self):
# Like test_textbox_8bit, but characters are entered as strings -- the
# way do_command() receives get_wch() input -- rather than integer
# bytes. Each string is used only if encodable in the current locale.
# bytes. Each string is used only if encodable in the current locale;
# a narrow build stores one byte per cell, so multi-byte characters
# additionally need a wide build.
for text in ['abc', 'héšλ', 'café', 'naïve ¤', 'soupçon €Š', 'дякую єі']:
if self._encodable(text):
with self.subTest(text=text):
box, win = self._make_textbox(1, 12)
for ch in text:
box.do_command(ch)
self.assertEqual(box.gather(), text + ' ')
if not self._encodable(text):
continue
if not WIDE_BUILD and len(text.encode(self.stdscr.encoding)) != len(text):
continue
with self.subTest(text=text):
box, win = self._make_textbox(1, 12)
for ch in text:
box.do_command(ch)
self.assertEqual(box.gather(), text + ' ')

def test_textbox_unicode_insert_mode(self):
# Like test_textbox_8bit_insert, but the character is entered as a string
# (get_wch() input). Each string is used only if encodable.
# (get_wch() input). Each string is used only if encodable; multi-byte
# characters additionally need a wide build (one byte per cell otherwise).
for text in ['abcd', 'aβλc', 'aéàc', 'a¤½c', 'a€Šc', 'aдві']:
if self._encodable(text):
with self.subTest(text=text):
box, win = self._make_textbox(1, 10, insert_mode=True)
for ch in text[0] + text[2:]: # all but the 2nd character
box.do_command(ch)
win.move(0, 1)
box.do_command(text[1]) # insert it at position 1
self.assertEqual(box.gather(), text + ' ')
if not self._encodable(text):
continue
if not WIDE_BUILD and len(text.encode(self.stdscr.encoding)) != len(text):
continue
with self.subTest(text=text):
box, win = self._make_textbox(1, 10, insert_mode=True)
for ch in text[0] + text[2:]: # all but the 2nd character
box.do_command(ch)
win.move(0, 1)
box.do_command(text[1]) # insert it at position 1
self.assertEqual(box.gather(), text + ' ')

@requires_wide_build
def test_textbox_combining(self):
Expand Down
3 changes: 2 additions & 1 deletion Lib/test/test_future_stmt/test_future.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import __future__
import ast
import unittest
from test.support import import_helper
from test.support import force_not_colorized, import_helper
from test.support.script_helper import spawn_python, kill_python
from textwrap import dedent
import os
Expand Down Expand Up @@ -176,6 +176,7 @@ def test_unicode_literals_exec(self):
exec("from __future__ import unicode_literals; x = ''", {}, scope)
self.assertIsInstance(scope["x"], str)

@force_not_colorized
def test_syntactical_future_repl(self):
p = spawn_python('-i')
p.stdin.write(b"from __future__ import barry_as_FLUFL\n")
Expand Down
6 changes: 6 additions & 0 deletions Lib/test/test_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1669,6 +1669,11 @@ def _clean(self):
unlink(self.source)

def setUp(self):
# These tests assume bytecode is written next to the source in a
# local __pycache__ directory, so neutralize any pycache prefix (e.g.
# when the test suite is run with PYTHONPYCACHEPREFIX set).
self._orig_pycache_prefix = sys.pycache_prefix
sys.pycache_prefix = None
self.source = TESTFN + '.py'
self._clean()
with open(self.source, 'w', encoding='utf-8') as fp:
Expand All @@ -1680,6 +1685,7 @@ def tearDown(self):
assert sys.path[0] == os.curdir, 'Unexpected sys.path[0]'
del sys.path[0]
self._clean()
sys.pycache_prefix = self._orig_pycache_prefix

@skip_if_dont_write_bytecode
def test_import_pyc_path(self):
Expand Down
11 changes: 11 additions & 0 deletions Lib/test/test_importlib/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,17 @@ class PEP3147Tests:

tag = sys.implementation.cache_tag

def setUp(self):
# Most of these tests assume the default (unset) pycache prefix, so
# clear it for the duration of the test (e.g. when the test suite is
# run with PYTHONPYCACHEPREFIX set). Tests that need a specific prefix
# set their own via util.temporary_pycache_prefix().
self._orig_pycache_prefix = sys.pycache_prefix
sys.pycache_prefix = None

def tearDown(self):
sys.pycache_prefix = self._orig_pycache_prefix

@unittest.skipIf(sys.implementation.cache_tag is None,
'requires sys.implementation.cache_tag not be None')
def test_cache_from_source(self):
Expand Down
23 changes: 20 additions & 3 deletions Lib/test/test_inspect/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import functools
import gc
import importlib
import importlib.util
import inspect
import io
import linecache
Expand Down Expand Up @@ -6519,6 +6520,19 @@ class TestModuleCLI(unittest.TestCase):
NO_SOURCE_ERROR = "No source code available for defining module"
NO_SOURCE_TARGET_ERROR = "Failed to retrieve source code for given target"

@staticmethod
def _expected_cached(module):
# assert_python_ok() runs the subprocess in isolated mode (-I), which
# ignores PYTHONPYCACHEPREFIX, so compute the expected cached path the
# same way (i.e. without any pycache prefix) to stay independent of the
# environment the test suite is run in. Modules without a cached path
# (e.g. frozen modules such as ntpath/importlib.machinery on Windows)
# report None, so preserve that.
if module.__spec__.cached is None:
return None
with support.swap_attr(sys, 'pycache_prefix', None):
return importlib.util.cache_from_source(module.__spec__.origin)

def test_only_source(self):
module = importlib.import_module('unittest')
rc, out, err = assert_python_ok('-m', 'inspect',
Expand Down Expand Up @@ -6576,12 +6590,13 @@ def test_details_option_with_package(self):
args = support.optim_args_from_interpreter_flags()
rc, out, err = assert_python_ok(*args, '-m', 'inspect',
module_name, '--details')
cached = self._expected_cached(module)
# Full rendering check on the expected output
expected_lines = [
f"Target: {module.__name__}", # No aliasing
f"Origin: {module.__spec__.origin}",
f"Source: {module.__file__}",
f"Cached: {module.__spec__.cached}", # None is still displayed
f"Cached: {cached}", # None is still displayed
f"Loader: {_clean_object_ids(repr(module.__spec__.loader))}",
f"Submodule search paths: {module.__path__}",
"",
Expand Down Expand Up @@ -6619,13 +6634,14 @@ def test_details_option_with_data_target(self):
args = support.optim_args_from_interpreter_flags()
rc, out, err = assert_python_ok(*args, '-m', 'inspect',
cli_target, '--details')
cached = self._expected_cached(module)
# Full rendering check on the expected output
# The error is only informational when reading source details
expected_lines = [
f"Target: {cli_target}", # No aliasing
f"Origin: {module.__spec__.origin}",
f"Source: {module.__file__}",
f"Cached: {module.__spec__.cached}", # None is still displayed
f"Cached: {cached}", # None is still displayed
self.NO_SOURCE_TARGET_ERROR,
"",
]
Expand All @@ -6644,12 +6660,13 @@ def test_details_option_with_aliased_target(self):
args = support.optim_args_from_interpreter_flags()
rc, out, err = assert_python_ok(*args, '-m', 'inspect',
cli_target, '--details')
cached = self._expected_cached(module)
# Full rendering check on the expected output
expected_lines = [
f'Target: {defining_target} (looked up as "{cli_target}")',
f"Origin: {module.__spec__.origin}",
f"Source: {module.__file__}",
f"Cached: {module.__spec__.cached}", # None is still displayed
f"Cached: {cached}", # None is still displayed
f"Line: {inspect.findsource(target)[1]}",
"",
]
Expand Down
41 changes: 25 additions & 16 deletions Lib/test/test_py_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,21 +186,24 @@ def test_source_date_epoch(self):
def test_double_dot_no_clobber(self):
# http://bugs.python.org/issue22966
# py_compile foo.bar.py -> __pycache__/foo.cpython-34.pyc
weird_path = os.path.join(self.directory, 'foo.bar.py')
cache_path = importlib.util.cache_from_source(weird_path)
pyc_path = weird_path + 'c'
head, tail = os.path.split(cache_path)
penultimate_tail = os.path.basename(head)
self.assertEqual(
os.path.join(penultimate_tail, tail),
os.path.join(
'__pycache__',
'foo.bar.{}.pyc'.format(sys.implementation.cache_tag)))
with open(weird_path, 'w') as file:
file.write('x = 123\n')
py_compile.compile(weird_path)
self.assertTrue(os.path.exists(cache_path))
self.assertFalse(os.path.exists(pyc_path))
# This test asserts the default __pycache__ layout, so neutralize any
# pycache prefix (e.g. when run with PYTHONPYCACHEPREFIX set).
with support.swap_attr(sys, 'pycache_prefix', None):
weird_path = os.path.join(self.directory, 'foo.bar.py')
cache_path = importlib.util.cache_from_source(weird_path)
pyc_path = weird_path + 'c'
head, tail = os.path.split(cache_path)
penultimate_tail = os.path.basename(head)
self.assertEqual(
os.path.join(penultimate_tail, tail),
os.path.join(
'__pycache__',
'foo.bar.{}.pyc'.format(sys.implementation.cache_tag)))
with open(weird_path, 'w') as file:
file.write('x = 123\n')
py_compile.compile(weird_path)
self.assertTrue(os.path.exists(cache_path))
self.assertFalse(os.path.exists(pyc_path))

@unittest.skipIf(sys.implementation.cache_tag is None,
'requires sys.implementation.cache_tag is not None')
Expand Down Expand Up @@ -307,7 +310,13 @@ def test_with_files(self):
self.assertEqual(rc, 0)
self.assertEqual(stdout, b'')
self.assertEqual(stderr, b'')
self.assertTrue(os.path.exists(self.cache_path))
# pycompilecmd() runs the interpreter in isolated mode (-I), which
# ignores PYTHONPYCACHEPREFIX, so the bytecode is written next to the
# source. Compute the expected cache path the same way.
with support.swap_attr(sys, 'pycache_prefix', None):
cache_path = importlib.util.cache_from_source(
self.source_path, optimization='' if __debug__ else 1)
self.assertTrue(os.path.exists(cache_path))

def test_bad_syntax(self):
bad_syntax = os.path.join(os.path.dirname(__file__),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The C implementations of :meth:`~datetime.datetime.fromisoformat` and :meth:`~datetime.time.fromisoformat`
now reject a decimal separator that is not followed by any
fractional digit before a timezone designator.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Fix several tests in ``test.test_inspect``, ``test.test_import``,
``test.test_importlib``, ``test.test_py_compile`` and
``test.test_compileall`` that failed when the test suite was run with
:envvar:`PYTHONPYCACHEPREFIX` set. These tests now neutralize the pycache
prefix where they assume the default ``__pycache__`` bytecode layout.
21 changes: 13 additions & 8 deletions Modules/_datetimemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1034,7 +1034,16 @@ parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour,
has_separator = (c == ':');
}

if (p >= p_end) {
if (c == '.' || c == ',') {
if (i < 2) {
return -3; // Decimal mark on hour or minute
}
if (p >= p_end) {
return -3; // Decimal mark not followed by any digit
}
break;
}
else if (p >= p_end) {
return c != '\0';
}
else if (has_separator && (c == ':')) {
Expand All @@ -1043,14 +1052,10 @@ parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour,
}
continue;
}
else if (c == '.' || c == ',') {
if (i < 2) {
return -3; // Decimal mark on hour or minute
}
break;
} else if (!has_separator) {
else if (!has_separator) {
--p;
} else {
}
else {
return -4; // Malformed time separator
}
}
Expand Down
Loading
Loading