-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add entry_points as py_binary's (#74)
* Add entry_points as py_binary's * Add test for generated binary
- Loading branch information
Showing
16 changed files
with
1,408 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#!/bin/bash | ||
set -e | ||
$TEST_SRCDIR/pypi__38__chardet_3_0_4/chardetect --help > /dev/null |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
Copyright (c) 2020 Pradyun Gedam | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
Metadata-Version: 2.1 | ||
Name: installer | ||
Version: 0.5.1 | ||
Summary: A library for installing Python wheels. | ||
Author-email: Pradyun Gedam <[email protected]> | ||
Requires-Python: >=3.7 | ||
Description-Content-Type: text/markdown | ||
Classifier: License :: OSI Approved :: MIT License | ||
Project-URL: GitHub, https://github.com/pypa/installer | ||
|
||
# installer | ||
|
||
<!-- start readme-pitch --> | ||
|
||
This is a low-level library for installing a Python package from a | ||
[wheel distribution](https://packaging.python.org/glossary/#term-Wheel). | ||
It provides basic functionality and abstractions for handling wheels and | ||
installing packages from wheels. | ||
|
||
- Logic for "unpacking" a wheel (i.e. installation). | ||
- Abstractions for various parts of the unpacking process. | ||
- Extensible simple implementations of the abstractions. | ||
- Platform-independent Python script wrapper generation. | ||
|
||
<!-- end readme-pitch --> | ||
|
||
You can read more in the [documentation](https://installer.rtfd.io/). | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Wheel-Version: 1.0 | ||
Generator: flit 3.2.0 | ||
Root-Is-Purelib: true | ||
Tag: py3-none-any |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
"""A library for installing Python wheels.""" | ||
|
||
__version__ = "0.5.1" | ||
__all__ = ["install"] | ||
|
||
from installer._core import install # noqa |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
"""Installer CLI.""" | ||
|
||
import argparse | ||
import os.path | ||
import sys | ||
import sysconfig | ||
from typing import Dict, Optional, Sequence | ||
|
||
import installer | ||
from installer.destinations import SchemeDictionaryDestination | ||
from installer.sources import WheelFile | ||
from installer.utils import get_launcher_kind | ||
|
||
|
||
def _get_main_parser() -> argparse.ArgumentParser: | ||
"""Construct the main parser.""" | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument("wheel", type=str, help="wheel file to install") | ||
parser.add_argument( | ||
"--destdir", | ||
"-d", | ||
metavar="path", | ||
type=str, | ||
help="destination directory (prefix to prepend to each file)", | ||
) | ||
parser.add_argument( | ||
"--compile-bytecode", | ||
action="append", | ||
metavar="level", | ||
type=int, | ||
choices=[0, 1, 2], | ||
help="generate bytecode for the specified optimization level(s) (default=0, 1)", | ||
) | ||
parser.add_argument( | ||
"--no-compile-bytecode", | ||
action="store_true", | ||
help="don't generate bytecode for installed modules", | ||
) | ||
return parser | ||
|
||
|
||
def _get_scheme_dict(distribution_name: str) -> Dict[str, str]: | ||
"""Calculate the scheme dictionary for the current Python environment.""" | ||
scheme_dict = sysconfig.get_paths() | ||
|
||
installed_base = sysconfig.get_config_var("base") | ||
assert installed_base | ||
|
||
# calculate 'headers' path, not currently in sysconfig - see | ||
# https://bugs.python.org/issue44445. This is based on what distutils does. | ||
# TODO: figure out original vs normalised distribution names | ||
scheme_dict["headers"] = os.path.join( | ||
sysconfig.get_path("include", vars={"installed_base": installed_base}), | ||
distribution_name, | ||
) | ||
|
||
return scheme_dict | ||
|
||
|
||
def _main(cli_args: Sequence[str], program: Optional[str] = None) -> None: | ||
"""Process arguments and perform the install.""" | ||
parser = _get_main_parser() | ||
if program: | ||
parser.prog = program | ||
args = parser.parse_args(cli_args) | ||
|
||
bytecode_levels = args.compile_bytecode | ||
if args.no_compile_bytecode: | ||
bytecode_levels = [] | ||
elif not bytecode_levels: | ||
bytecode_levels = [0, 1] | ||
|
||
with WheelFile.open(args.wheel) as source: | ||
destination = SchemeDictionaryDestination( | ||
scheme_dict=_get_scheme_dict(source.distribution), | ||
interpreter=sys.executable, | ||
script_kind=get_launcher_kind(), | ||
bytecode_optimization_levels=bytecode_levels, | ||
destdir=args.destdir, | ||
) | ||
installer.install(source, destination, {}) | ||
|
||
|
||
if __name__ == "__main__": # pragma: no cover | ||
_main(sys.argv[1:], "python -m installer") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
"""Core wheel installation logic.""" | ||
|
||
import posixpath | ||
from io import BytesIO | ||
from typing import Dict, Tuple, cast | ||
|
||
from installer.destinations import WheelDestination | ||
from installer.exceptions import InvalidWheelSource | ||
from installer.records import RecordEntry | ||
from installer.sources import WheelSource | ||
from installer.utils import SCHEME_NAMES, Scheme, parse_entrypoints, parse_metadata_file | ||
|
||
__all__ = ["install"] | ||
|
||
|
||
def _process_WHEEL_file(source: WheelSource) -> Scheme: | ||
"""Process the WHEEL file, from ``source``. | ||
Returns the scheme that the archive root should go in. | ||
""" | ||
stream = source.read_dist_info("WHEEL") | ||
metadata = parse_metadata_file(stream) | ||
|
||
# Ensure compatibility with this wheel version. | ||
if not (metadata["Wheel-Version"] and metadata["Wheel-Version"].startswith("1.")): | ||
message = "Incompatible Wheel-Version {}, only support version 1.x wheels." | ||
raise InvalidWheelSource(source, message.format(metadata["Wheel-Version"])) | ||
|
||
# Determine where archive root should go. | ||
if metadata["Root-Is-Purelib"] == "true": | ||
return cast(Scheme, "purelib") | ||
else: | ||
return cast(Scheme, "platlib") | ||
|
||
|
||
def _determine_scheme( | ||
path: str, source: WheelSource, root_scheme: Scheme | ||
) -> Tuple[Scheme, str]: | ||
"""Determine which scheme to place given path in, from source.""" | ||
data_dir = source.data_dir | ||
|
||
# If it's in not `{distribution}-{version}.data`, then it's in root_scheme. | ||
if posixpath.commonprefix([data_dir, path]) != data_dir: | ||
return root_scheme, path | ||
|
||
# Figure out which scheme this goes to. | ||
parts = [] | ||
scheme_name = None | ||
left = path | ||
while True: | ||
left, right = posixpath.split(left) | ||
parts.append(right) | ||
if left == source.data_dir: | ||
scheme_name = right | ||
break | ||
|
||
if scheme_name not in SCHEME_NAMES: | ||
msg_fmt = "{path} is not contained in a valid .data subdirectory." | ||
raise InvalidWheelSource(source, msg_fmt.format(path=path)) | ||
|
||
return cast(Scheme, scheme_name), posixpath.join(*reversed(parts[:-1])) | ||
|
||
|
||
def install( | ||
source: WheelSource, | ||
destination: WheelDestination, | ||
additional_metadata: Dict[str, bytes], | ||
) -> None: | ||
"""Install wheel described by ``source`` into ``destination``. | ||
:param source: wheel to install. | ||
:param destination: where to write the wheel. | ||
:param additional_metadata: additional metadata files to generate, usually | ||
generated by the installer. | ||
""" | ||
root_scheme = _process_WHEEL_file(source) | ||
|
||
# RECORD handling | ||
record_file_path = posixpath.join(source.dist_info_dir, "RECORD") | ||
written_records = [] | ||
|
||
# Write the entry_points based scripts. | ||
if "entry_points.txt" in source.dist_info_filenames: | ||
entrypoints_text = source.read_dist_info("entry_points.txt") | ||
for name, module, attr, section in parse_entrypoints(entrypoints_text): | ||
record = destination.write_script( | ||
name=name, | ||
module=module, | ||
attr=attr, | ||
section=section, | ||
) | ||
written_records.append((Scheme("scripts"), record)) | ||
|
||
# Write all the files from the wheel. | ||
for record_elements, stream, is_executable in source.get_contents(): | ||
source_record = RecordEntry.from_elements(*record_elements) | ||
path = source_record.path | ||
# Skip the RECORD, which is written at the end, based on this info. | ||
if path == record_file_path: | ||
continue | ||
|
||
# Figure out where to write this file. | ||
scheme, destination_path = _determine_scheme( | ||
path=path, | ||
source=source, | ||
root_scheme=root_scheme, | ||
) | ||
record = destination.write_file( | ||
scheme=scheme, | ||
path=destination_path, | ||
stream=stream, | ||
is_executable=is_executable, | ||
) | ||
written_records.append((scheme, record)) | ||
|
||
# Write all the installation-specific metadata | ||
for filename, contents in additional_metadata.items(): | ||
path = posixpath.join(source.dist_info_dir, filename) | ||
|
||
with BytesIO(contents) as other_stream: | ||
record = destination.write_file( | ||
scheme=root_scheme, | ||
path=path, | ||
stream=other_stream, | ||
is_executable=is_executable, | ||
) | ||
written_records.append((root_scheme, record)) | ||
|
||
written_records.append((root_scheme, RecordEntry(record_file_path, None, None))) | ||
destination.finalize_installation( | ||
scheme=root_scheme, | ||
record_file_path=record_file_path, | ||
records=written_records, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"""Internal package, containing launcher templates for ``installer.scripts``.""" |
Oops, something went wrong.