Skip to content

Commit

Permalink
Document markers.default_environment()
Browse files Browse the repository at this point in the history
  • Loading branch information
edgarrmondragon committed Dec 21, 2023
1 parent 7698237 commit 701cc51
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 26 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ repos:
- id: trailing-whitespace

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.991
rev: v1.7.1
hooks:
- id: mypy
exclude: '^(docs|tasks|tests)|setup\.py'
Expand Down
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"sphinx.ext.doctest",
"sphinx.ext.extlinks",
"sphinx.ext.intersphinx",
"sphinx_toolbox.more_autodoc.autotypeddict",
]

# General information about the project.
Expand Down
17 changes: 15 additions & 2 deletions docs/markers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,28 @@ Reference

Evaluate the marker given the context of the current Python process.

:param dict environment: A dictionary containing keys and values to
override the detected environment.
:param Environment environment: A dictionary containing keys and values to
override the detected environment.
:raises: UndefinedComparison: If the marker uses a comparison on strings
which are not valid versions per the
:ref:`specification of version specifiers
<pypug:version-specifiers>`.
:raises: UndefinedEnvironmentName: If the marker accesses a value that
isn't present inside of the environment
dictionary.
:rtype: bool

.. autotypeddict:: packaging.markers.Environment

A dictionary that represents a Python environment.

.. function:: default_environment()

Returns a dictionary representing the current Python process. This is the
base environment that is used when evaluating markers in
:meth:`Marker.evaluate`.

:rtype: Environment

.. exception:: InvalidMarker

Expand Down
2 changes: 2 additions & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
furo
sphinx-toolbox
typing-extensions>=4.1.0; python_version < "3.9"
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ exclude_lines = ["pragma: no cover", "@abc.abstractmethod", "@abc.abstractproper
strict = true
show_error_codes = true
enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
warn_unused_ignores = true

[[tool.mypy.overrides]]
module = ["_manylinux"]
Expand Down
35 changes: 35 additions & 0 deletions src/packaging/_compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import sys
import typing

if sys.version_info[:2] >= (3, 8): # pragma: no cover
from typing import Literal
elif typing.TYPE_CHECKING: # pragma: no cover
from typing_extensions import Literal
else: # pragma: no cover
try:
from typing_extensions import Literal
except ImportError:

class Literal:
def __init_subclass__(*_args, **_kwargs):
pass


if sys.version_info[:2] >= (3, 9): # pragma: no cover
from typing import TypedDict
elif typing.TYPE_CHECKING: # pragma: no cover
from typing_extensions import TypedDict
else: # pragma: no cover
try:
from typing_extensions import TypedDict
except ImportError:

class TypedDict:
def __init_subclass__(*_args, **_kwargs):
pass


__all__ = [
"Literal",
"TypedDict",
]
95 changes: 90 additions & 5 deletions src/packaging/markers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import sys
from typing import Any, Callable, Dict, List, Optional, Tuple, Union

from ._compat import TypedDict
from ._parser import (
MarkerAtom,
MarkerList,
Expand Down Expand Up @@ -50,6 +51,89 @@ class UndefinedEnvironmentName(ValueError):
"""


class Environment(TypedDict, total=False):
implementation_name: str
"""The implementation's identifier, e.g. ``'cpython'``."""

implementation_version: str
"""
The implementation's version, e.g. ``'3.13.0a2'`` for CPython 3.13.0a2, or
``'7.3.13'`` for PyPy3.10 v7.3.13.
"""

os_name: str
"""
The value of :py:data:`os.name`. The name of the operating system dependent module
imported, e.g. ``'posix'``.
"""

platform_machine: str
"""
Returns the machine type, e.g. ``'i386'``.
An empty string if the value cannot be determined.
"""

platform_release: str
"""
The system's release, e.g. ``'2.2.0'`` or ``'NT'``.
An empty string if the value cannot be determined.
"""

platform_system: str
"""
The system/OS name, e.g. ``'Linux'``, ``'Windows'`` or ``'Java'``.
An empty string if the value cannot be determined.
"""

platform_version: str
"""
The system's release version, e.g. ``'#3 on degas'``.
An empty string if the value cannot be determined.
"""

python_full_version: str
"""
The Python version as string ``'major.minor.patchlevel'``.
Note that unlike the Python :py:data:`sys.version`, this value will always include
the patchlevel (it defaults to 0).
"""

platform_python_implementation: str
"""
A string identifying the Python implementation.
Currently, the following implementations are identified: ``'CPython'`` (C
implementation of Python), ``'IronPython'`` (.NET implementation of Python),
``'Jython'`` (Java implementation of Python), ``'PyPy'`` (Python implementation of
Python).
"""

python_version: str
"""The Python version as string ``'major.minor'``."""

sys_platform: str
"""
This string contains a platform identifier that can be used to append
platform-specific components to :py:data:`sys.path`, for instance.
For Unix systems, except on Linux and AIX, this is the lowercased OS name as
returned by ``uname -s`` with the first part of the version as returned by
``uname -r`` appended, e.g. ``'sunos5'`` or ``'freebsd8'``, at the time when Python
was built.
"""

extra: Optional[str]
"""
An optional string used by wheels to signal which specifications apply to a given
extra in the wheel ``METADATA`` file.
"""


def _normalize_extra_values(results: Any) -> Any:
"""
Normalize extra values.
Expand Down Expand Up @@ -134,10 +218,11 @@ def _normalize(*values: str, key: str) -> Tuple[str, ...]:
return values


def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool:
def _evaluate_markers(markers: MarkerList, environment: Environment) -> bool:
groups: List[List[bool]] = [[]]

for marker in markers:

assert isinstance(marker, (list, tuple, str))

if isinstance(marker, list):
Expand All @@ -147,12 +232,12 @@ def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool:

if isinstance(lhs, Variable):
environment_key = lhs.value
lhs_value = environment[environment_key]
lhs_value = environment[environment_key] # type: ignore[literal-required] # noqa: E501
rhs_value = rhs.value
else:
lhs_value = lhs.value
environment_key = rhs.value
rhs_value = environment[environment_key]
rhs_value = environment[environment_key] # type: ignore[literal-required] # noqa: E501

lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key)
groups[-1].append(_eval_op(lhs_value, op, rhs_value))
Expand All @@ -172,7 +257,7 @@ def format_full_version(info: "sys._version_info") -> str:
return version


def default_environment() -> Dict[str, str]:
def default_environment() -> Environment:
iver = format_full_version(sys.implementation.version)
implementation_name = sys.implementation.name
return {
Expand Down Expand Up @@ -231,7 +316,7 @@ def __eq__(self, other: Any) -> bool:

return str(self) == str(other)

def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool:
def evaluate(self, environment: Optional[Environment] = None) -> bool:
"""Evaluate a marker.
Return the boolean from evaluating the given marker against the
Expand Down
19 changes: 1 addition & 18 deletions src/packaging/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import email.message
import email.parser
import email.policy
import sys
import typing
from typing import (
Any,
Expand All @@ -19,25 +18,9 @@
)

from . import requirements, specifiers, utils, version as version_module
from ._compat import Literal, TypedDict

T = typing.TypeVar("T")
if sys.version_info[:2] >= (3, 8): # pragma: no cover
from typing import Literal, TypedDict
else: # pragma: no cover
if typing.TYPE_CHECKING:
from typing_extensions import Literal, TypedDict
else:
try:
from typing_extensions import Literal, TypedDict
except ImportError:

class Literal:
def __init_subclass__(*_args, **_kwargs):
pass

class TypedDict:
def __init_subclass__(*_args, **_kwargs):
pass


try:
Expand Down

0 comments on commit 701cc51

Please sign in to comment.