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
34 changes: 3 additions & 31 deletions Doc/library/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1422,38 +1422,10 @@ are always available. They are listed here in alphabetical order.

*errors* is an optional string that specifies how encoding and decoding
errors are to be handled—this cannot be used in binary mode.
A variety of standard error handlers are available
(listed under :ref:`error-handlers`), though any
error handling name that has been registered with
A variety of standard error handlers are available,
though any error handling name that has been registered with
:func:`codecs.register_error` is also valid. The standard names
include:

* ``'strict'`` to raise a :exc:`ValueError` exception if there is
an encoding error. The default value of ``None`` has the same
effect.

* ``'ignore'`` ignores errors. Note that ignoring encoding errors
can lead to data loss.

* ``'replace'`` causes a replacement marker (such as ``'?'``) to be inserted
where there is malformed data.

* ``'surrogateescape'`` will represent any incorrect bytes as low
surrogate code units ranging from U+DC80 to U+DCFF.
These surrogate code units will then be turned back into
the same bytes when the ``surrogateescape`` error handler is used
when writing data. This is useful for processing files in an
unknown encoding.

* ``'xmlcharrefreplace'`` is only supported when writing to a file.
Characters not supported by the encoding are replaced with the
appropriate XML character reference :samp:`&#{nnn};`.

* ``'backslashreplace'`` replaces malformed data by Python's backslashed
escape sequences.

* ``'namereplace'`` (also only supported when writing)
replaces unsupported characters with ``\N{...}`` escape sequences.
can be found in :ref:`error-handlers`.

.. index::
single: universal newlines; open() built-in function
Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_global_objects_fini_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Include/internal/pycore_global_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(options)
STRUCT_FOR_ID(order)
STRUCT_FOR_ID(origin)
STRUCT_FOR_ID(other)
STRUCT_FOR_ID(out_fd)
STRUCT_FOR_ID(outgoing)
STRUCT_FOR_ID(outpath)
Expand Down Expand Up @@ -704,6 +705,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(return)
STRUCT_FOR_ID(reverse)
STRUCT_FOR_ID(reversed)
STRUCT_FOR_ID(rounding)
STRUCT_FOR_ID(salt)
STRUCT_FOR_ID(sched_priority)
STRUCT_FOR_ID(scheduler)
Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_runtime_init_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Include/internal/pycore_unicodeobject_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 4 additions & 12 deletions Lib/annotationlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,21 +158,13 @@ def evaluate(
# as a way of emulating annotation scopes when calling `eval()`
type_params = getattr(owner, "__type_params__", None)

# type parameters require some special handling,
# as they exist in their own scope
# but `eval()` does not have a dedicated parameter for that scope.
# For classes, names in type parameter scopes should override
# names in the global scope (which here are called `localns`!),
# but should in turn be overridden by names in the class scope
# (which here are called `globalns`!)
# Type parameters exist in their own scope, which is logically
# between the locals and the globals. We simulate this by adding
# them to the globals.
if type_params is not None:
globals = dict(globals)
locals = dict(locals)
for param in type_params:
param_name = param.__name__
if not self.__forward_is_class__ or param_name not in globals:
globals[param_name] = param
locals.pop(param_name, None)
globals[param.__name__] = param
if self.__extra_names__:
locals = {**locals, **self.__extra_names__}

Expand Down
20 changes: 20 additions & 0 deletions Lib/test/test_annotationlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1365,6 +1365,11 @@ def test_annotations_to_string(self):
class A:
pass

TypeParamsAlias1 = int

class TypeParamsSample[TypeParamsAlias1, TypeParamsAlias2]:
TypeParamsAlias2 = str


class TestForwardRefClass(unittest.TestCase):
def test_forwardref_instance_type_error(self):
Expand Down Expand Up @@ -1597,6 +1602,21 @@ class Gen[T]:
ForwardRef("alias").evaluate(owner=Gen, locals={"alias": str}), str
)

def test_evaluate_with_type_params_and_scope_conflict(self):
for is_class in (False, True):
with self.subTest(is_class=is_class):
fwdref1 = ForwardRef("TypeParamsAlias1", owner=TypeParamsSample, is_class=is_class)
fwdref2 = ForwardRef("TypeParamsAlias2", owner=TypeParamsSample, is_class=is_class)

self.assertIs(
fwdref1.evaluate(),
TypeParamsSample.__type_params__[0],
)
self.assertIs(
fwdref2.evaluate(),
TypeParamsSample.TypeParamsAlias2,
)

def test_fwdref_with_module(self):
self.assertIs(ForwardRef("Format", module="annotationlib").evaluate(), Format)
self.assertIs(
Expand Down
22 changes: 20 additions & 2 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2366,10 +2366,13 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
# *base_globals* first rather than *base_locals*.
# This only affects ForwardRefs.
base_globals, base_locals = base_locals, base_globals
type_params = base.__type_params__
base_globals, base_locals = _add_type_params_to_scope(
type_params, base_globals, base_locals, True)
for name, value in ann.items():
if isinstance(value, str):
value = _make_forward_ref(value, is_argument=False, is_class=True)
value = _eval_type(value, base_globals, base_locals, base.__type_params__,
value = _eval_type(value, base_globals, base_locals, (),
format=format, owner=obj)
if value is None:
value = type(None)
Expand Down Expand Up @@ -2405,6 +2408,7 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
elif localns is None:
localns = globalns
type_params = getattr(obj, "__type_params__", ())
globalns, localns = _add_type_params_to_scope(type_params, globalns, localns, False)
for name, value in hints.items():
if isinstance(value, str):
# class-level forward refs were handled above, this must be either
Expand All @@ -2414,13 +2418,27 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
is_argument=not isinstance(obj, types.ModuleType),
is_class=False,
)
value = _eval_type(value, globalns, localns, type_params, format=format, owner=obj)
value = _eval_type(value, globalns, localns, (), format=format, owner=obj)
if value is None:
value = type(None)
hints[name] = value
return hints if include_extras else {k: _strip_annotations(t) for k, t in hints.items()}


# Add type parameters to the globals and locals scope. This is needed for
# compatibility.
def _add_type_params_to_scope(type_params, globalns, localns, is_class):
if not type_params:
return globalns, localns
globalns = dict(globalns)
localns = dict(localns)
for param in type_params:
if not is_class or param.__name__ not in globalns:
globalns[param.__name__] = param
localns.pop(param.__name__, None)
return globalns, localns


def _strip_annotations(t):
"""Strip the annotations from a given type."""
if isinstance(t, _AnnotatedAlias):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix behavior of :meth:`annotationlib.ForwardRef.evaluate` when the
*type_params* parameter is passed and the name of a type param is also
present in an enclosing scope.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Speedup processing arguments (up to 1.5x) in the :mod:`decimal` module
methods, that now using :c:macro:`METH_FASTCALL` calling convention. Patch
by Sergey B Kirpichev.
Loading
Loading