564 lines
26 KiB
ReStructuredText
564 lines
26 KiB
ReStructuredText
PEP: 722
|
|
Title: Dependency specification for single-file scripts
|
|
Author: Paul Moore <p.f.moore@gmail.com>
|
|
PEP-Delegate: Brett Cannon <brett@python.org>
|
|
Discussions-To: https://discuss.python.org/t/29905
|
|
Status: Draft
|
|
Type: Standards Track
|
|
Topic: Packaging
|
|
Content-Type: text/x-rst
|
|
Created: 19-Jul-2023
|
|
Post-History: `19-Jul-2023 <https://discuss.python.org/t/29905>`__
|
|
|
|
|
|
Abstract
|
|
========
|
|
|
|
This PEP specifies a format for including 3rd-party dependencies in a
|
|
single-file Python script.
|
|
|
|
|
|
Motivation
|
|
==========
|
|
|
|
Not all Python code is structured as a "project", in the sense of having its own
|
|
directory complete with a ``pyproject.toml`` file, and being built into an
|
|
installable distribution package. Python is also routinely used as a scripting
|
|
language, with Python scripts as a (better) alternative to shell scripts, batch
|
|
files, etc. When used to create scripts, Python code is typically stored as a
|
|
single file, often in a directory dedicated to such "utility scripts", which
|
|
might be in a mix of languages with Python being only one possibility among
|
|
many. Such scripts may be shared, often by something as simple as email, or a
|
|
link to a URL such as a Github gist. But they are typically *not* "distributed"
|
|
or "installed" as part of a normal workflow.
|
|
|
|
One problem when using Python as a scripting language in this way is how to run
|
|
the script in an environment that contains whatever third party dependencies are
|
|
required by the script. There is currently no standard tool that addresses this
|
|
issue, and this PEP does *not* attempt to define one. However, any tool that
|
|
*does* address this issue will need to know what 3rd party dependencies a script
|
|
requires. By defining a standard format for storing such data, existing tools,
|
|
as well as any future tools, will be able to obtain that information without
|
|
requiring users to include tool-specific metadata in their scripts.
|
|
|
|
|
|
Rationale
|
|
=========
|
|
|
|
Because a key requirement is writing single-file scripts, and simple sharing by
|
|
giving someone a copy of the script, the PEP defines a mechanism for embedding
|
|
dependency data *within the script itself*, and not in an external file.
|
|
|
|
We define the concept of a *metadata block* that contains information about a
|
|
script. The only type of metadata defined here is dependency information, but
|
|
making the concept general allows expansion in the future, should it be needed.
|
|
|
|
In order to identify metadata blocks, the script can simply be read as a text
|
|
file. This is deliberate, as Python syntax changes over time, so attempting to
|
|
parse the script as Python code would require choosing a specific version of
|
|
Python syntax. Also, it is likely that at least some tools will not be written
|
|
in Python, and expecting them to implement a Python parser is too much of a
|
|
burden.
|
|
|
|
However, to avoid needing changes to core Python, the format is designed to
|
|
appear as comments to the Python parser. It is possible to write code where a
|
|
metadata block is *not* interpreted as a comment (for example, by embedding it
|
|
in a Python multi-line string), but such uses are discouraged and can easily be
|
|
avoided assuming you are not deliberately trying to create a pathological
|
|
example.
|
|
|
|
A `review <language survey_>`_ of how other languages allow scripts to specify
|
|
their dependencies shows that a "structured comment" like this is a
|
|
commonly-used approach.
|
|
|
|
Specification
|
|
=============
|
|
|
|
Any Python script may contain one or more *metadata blocks*. Metadata blocks are
|
|
identified by reading the script *as a text file* (i.e., the file is not parsed
|
|
as Python source code), looking for contiguous blocks of lines that start with
|
|
the identifying characters ``##``. Whitespace is not allowed before the
|
|
identifying ``##``. More than one metadata block may exist in a Python file.
|
|
|
|
Tools reading metadata blocks MAY respect the standard Python encoding
|
|
declaration. If they choose not to do so, they MUST process the file as UTF-8.
|
|
|
|
Within a metadata block, whitespace after the ``##`` and at the end of the line
|
|
is ignored, and blank lines are ignored. The first line of a metadata block is
|
|
special, and identifies the type of block. This line MUST contain a colon
|
|
character, ``:``. If the colon is not present, the block is not considered to be
|
|
a metadata block, and tools MUST ignore it. The block type is all of the text on
|
|
the initial line, up to the colon. There must be no whitespace before the colon.
|
|
The initial line MAY contain text after the colon. How this is interpreted
|
|
depends on the block type. Block types MUST be treated as case sensitive.
|
|
|
|
The interpretation of any lines in a metadata block after the initial
|
|
identifying line, is defined by the type of block.
|
|
|
|
Tools MUST ignore any blocks with types they do not handle.
|
|
|
|
Block types starting with the characters ``X-`` are reserved for the user, and
|
|
MUST NOT be given a meaning in any future standard.
|
|
|
|
Otherwise, the only defined block type is ``Script Dependencies``. For this
|
|
block type,
|
|
|
|
1. Text after the colon on the initial line is NOT allowed.
|
|
2. All subsequent lines MUST contain :pep:`508` requirement
|
|
specifiers, one per line.
|
|
|
|
There SHOULD only be a single ``Script Dependencies`` block in the file. Tools
|
|
consuming dependency data MAY simply process the first such block found. This
|
|
avoids the need for tools to process more data than is necessary.
|
|
|
|
Consumers MUST validate that at a minimum, all dependencies start with a
|
|
``name`` as defined in :pep:`508`, and they MAY validate that all dependencies
|
|
conform fully to :pep:`508`. They MUST fail with an error if they find an
|
|
invalid specifier.
|
|
|
|
Example
|
|
-------
|
|
|
|
The following is an example of a script with an embedded dependency block::
|
|
|
|
# In order to run, this script needs the following 3rd party libraries
|
|
#
|
|
## Script Dependencies:
|
|
## requests
|
|
## rich
|
|
|
|
import requests
|
|
from rich.pretty import pprint
|
|
|
|
resp = requests.get("https://peps.python.org/api/peps.json")
|
|
data = resp.json()
|
|
pprint([(k, v["title"]) for k, v in data.items()][:10])
|
|
|
|
|
|
Backwards Compatibility
|
|
=======================
|
|
|
|
As metadata blocks take the form of a structured comment, they can be added
|
|
without altering the meaning of existing code.
|
|
|
|
It is possible that a comment may already exist which matches the form of a
|
|
metadata block. While the use of a double ``#`` prefix is intended to minimise
|
|
this risk, it is still possible.
|
|
|
|
Because tools must ignore unrecognised metadata types, the only potential issue
|
|
we need to consider is script dependencies. In that case, a tool might read the
|
|
wrong dependencies. In practice, though, this is unlikely to happen, as (a) the
|
|
header text (``Script Dependencies:``) is fairly unusual, and (b) any following
|
|
lines are unlikely to conform to :pep:`508` unless they *are* dependencies.
|
|
|
|
In the rare case where an existing comment would be interpreted incorrectly as a
|
|
dependency block, this can be addressed by adding an actual dependency block
|
|
(which can be empty if the script has no dependencies) earlier in the code.
|
|
|
|
|
|
Security Implications
|
|
=====================
|
|
|
|
If a script containing a dependency block is run using a tool that automatically
|
|
installs dependencies, this could cause arbitrary code to be downloaded and
|
|
installed in the user's environment.
|
|
|
|
The risk here is part of the functionality of the tool being used to run the
|
|
script, and as such should already be addressed by the tool itself. The only
|
|
additional risk introduced by this PEP is if an untrusted script with a
|
|
dependency block is run, when a potentially malicious dependency might be
|
|
installed. This risk is addressed by the normal good practice of reviewing code
|
|
before running it.
|
|
|
|
|
|
How to Teach This
|
|
=================
|
|
|
|
The format is intended to be close to how a developer might already specify
|
|
script dependencies in an explanatory comment. The required structure is
|
|
deliberately minimal, and the concept of using a special comment marker (``##``
|
|
in this case) is not unusual (the "shebang" line in a Unix shell script is an
|
|
example).
|
|
|
|
Users will need to know how to write Python dependency specifiers. This is
|
|
covered by :pep:`508`, but for simple examples (which is expected to be the norm
|
|
for inexperienced users) the syntax is either just a package name, or a name and
|
|
a version restriction, which is fairly well-understood syntax.
|
|
|
|
Users will also know how to *run* a script using a tool that interprets
|
|
dependency data. This is not covered by this PEP, as it is the responsibility of
|
|
such a tool to document how it should be used.
|
|
|
|
Note that the core Python interpreter does *not* interpret dependency blocks.
|
|
This may be a point of confusion for beginners, who try to run ``python
|
|
some_script.py`` and do not understand why it fails. This is no different than
|
|
the current status quo, though, where running a script without its dependencies
|
|
present will give an error.
|
|
|
|
In general, it is assumed that if a beginner is given a script with dependencies
|
|
(regardless of whether they are specified in a dependency block), the person
|
|
supplying the script should explain how to run that script, and if that involves
|
|
using a script runner tool, that should be noted.
|
|
|
|
|
|
Recommendations
|
|
===============
|
|
|
|
This section is non-normative and simply describes "good practices" when using
|
|
metadata blocks.
|
|
|
|
Scripts should, in general, place metadata blocks at the top of the file,
|
|
either immediately after any shebang line, or straight after the script
|
|
docstring. In particular, the metadata block should always be placed before
|
|
any executable code in the file. This makes it easy for the human reader to
|
|
locate the metadata block, and allows tools to only read the minimum necessary
|
|
to identify them.
|
|
|
|
|
|
Reference Implementation
|
|
========================
|
|
|
|
Code to implement this proposal in Python is fairly straightforward, so the
|
|
reference implementation can be included here.
|
|
|
|
A parser that reads *only* the script dependency metadata.
|
|
|
|
.. code:: python
|
|
|
|
import tokenize
|
|
from packaging.requirements import Requirement
|
|
|
|
DEPENDENCY_BLOCK_MARKER = "Script Dependencies:"
|
|
|
|
def read_dependency_block(filename):
|
|
# Use the tokenize module to handle any encoding declaration.
|
|
with tokenize.open(filename) as f:
|
|
for line in f:
|
|
if line.startswith("##"):
|
|
line = line[2:].strip()
|
|
if line == DEPENDENCY_BLOCK_MARKER:
|
|
for line in f:
|
|
if not line.startswith("##"):
|
|
break
|
|
line = line[2:].strip()
|
|
if not line:
|
|
continue
|
|
# Try to convert to a requirement. This will raise
|
|
# an error if the line is not a PEP 508 requirement
|
|
yield Requirement(line)
|
|
break
|
|
|
|
A full metadata block parser that returns all metadata blocks in a script.
|
|
|
|
.. code:: python
|
|
|
|
import tokenize
|
|
from packaging.requirements import Requirement
|
|
|
|
def read_metadata_blocks(filename):
|
|
# Use the tokenize module to handle any encoding declaration.
|
|
with tokenize.open(filename) as f:
|
|
for line in f:
|
|
if line.startswith("##"):
|
|
block_type, sep, extra = line[2:].strip().partition(":")
|
|
if not sep:
|
|
continue
|
|
block_data = []
|
|
for line in f:
|
|
if not line.startswith("##"):
|
|
break
|
|
line = line[2:].strip()
|
|
if not line:
|
|
continue
|
|
block_data.append(line)
|
|
yield block_type, extra, block_data
|
|
|
|
A format similar to the one proposed here is already supported `in pipx
|
|
<https://github.com/pypa/pipx/pull/916>`__ and in `pip-run
|
|
<https://pypi.org/project/pip-run/>`__.
|
|
|
|
|
|
Rejected Ideas
|
|
==============
|
|
|
|
Why not include other metadata?
|
|
-------------------------------
|
|
|
|
The "metadata block" format is designed to allow additional metadata types, but
|
|
none are defined at this time. Currently, the only data used by tools is
|
|
dependency information, and therefore this is the only information required by
|
|
this standard. If, in future, a need is identified for other data to be
|
|
standardised, adding further metadata types is straightforward.
|
|
|
|
By reserving metadata types starting with ``X-``, the specification allows
|
|
experimentation with additional data *before* standardising.
|
|
|
|
Two particular cases are a script version number, and the version of Python
|
|
needed to run the script.
|
|
|
|
In the case of the version number, there are no known tools that try to extract
|
|
version information from scripts, so there is no immediate benefit to having the
|
|
version as metadata, rather than, for example, as a normal comment or a
|
|
``__version__`` attribute (see :pep:`396`). If it becomes common for tools to
|
|
want to introspect script versions, this could be added at a later date.
|
|
|
|
In the case of the Python version, existing tools provide a means for the *user*
|
|
to specify what Python interpreter to use when running the script (for example,
|
|
``pipx run`` provides the ``--python`` command line option), but they do not
|
|
typically allow the *script* to define a version range, and then automatically
|
|
pick an interpreter based on that. Having a "supported version" for a script may
|
|
allow the tool to provide better error messages when run with an inappropriate
|
|
interpreter, but currently, this is largely a theoretical benefit. Again, it is
|
|
something that can be added later if it becomes a commonly requested feature.
|
|
|
|
|
|
Why not use a more standard data format (e.g., TOML)?
|
|
-----------------------------------------------------
|
|
|
|
First of all, the only practical choice for an alternative format is TOML.
|
|
Python packaging has standardised on TOML for structured data, and using a
|
|
different format, such as YAML or JSON, would add complexity and confusion for
|
|
no real benefit.
|
|
|
|
So the question is essentially, "why not use TOML?"
|
|
|
|
The key idea behind the "metadata block" format is to define something that
|
|
reads naturally as a comment in the script. Dependency data is useful both for
|
|
tools and for the human reader, so having a human readable format is beneficial.
|
|
On the other hand, TOML of necessity has a syntax of its own, which distracts
|
|
from the underlying data.
|
|
|
|
It is important to remember that developers who *write* scripts in Python are
|
|
often *not* experienced in Python, or Python packaging. They are often systems
|
|
administrators, or data analysts, who may simply be using Python as a "better
|
|
batch file". For such users, the TOML format is extremely likely to be
|
|
unfamiliar, and the syntax will be obscure to them, and not particularly
|
|
intuitive. Such developers may well be copying dependency specifiers from
|
|
sources such as Stack Overflow, without really understanding them. Having to
|
|
embed such a requirement into a TOML structure is an additional complexity --
|
|
and it is important to remember that the goal here is to make using 3rd party
|
|
libraries *easy* for such users.
|
|
|
|
Furthermore, TOML, by its nature, is a flexible format intended to support very
|
|
general data structures. There are *many* ways of writing a simple list of
|
|
strings in it, and it will not be clear to inexperienced users which form to use.
|
|
|
|
And finally, there will be tools that expect to *write* dependency data into
|
|
scripts -- for example, an IDE with a feature that automatically adds an import
|
|
and a dependency specifier when you reference a library function. While
|
|
libraries exist that allow editing TOML data, they are not always good at
|
|
preserving the user's layout, which could include comments, specific formatting,
|
|
etc. Even if libraries exist which do an effective job at this, expecting all
|
|
tools to use such a library is a significant imposition on code supporting this
|
|
PEP.
|
|
|
|
By choosing a simple, line-based format with no quoting rules, dependency data
|
|
is easy to read (for humans and tools) and easy to write. The format doesn't
|
|
have the flexibility of something like TOML, but the use case simply doesn't
|
|
demand that sort of flexibility.
|
|
|
|
|
|
Why not embed a ``pyproject.toml`` file in the script?
|
|
------------------------------------------------------
|
|
|
|
First of all, ``pyproject.toml`` is a TOML based format, so all of the previous
|
|
concerns around TOML as a format apply. However, ``pyproject.toml`` is a
|
|
standard used by Python packaging, and re-using an existing standard is a
|
|
reasonable suggestion that deserves to be addressed on its own merits.
|
|
|
|
The first issue is that the suggestion rarely implies that *all* of
|
|
``pyproject.toml`` is to be supported for scripts. A script is not intended to
|
|
be "built" into any sort of distributable artifact like a wheel (see below for
|
|
more on this point), so the ``[build-system]`` section of ``pyproject.toml``
|
|
makes little sense, for example. And while the tool-specific sections of
|
|
``pyproject.toml`` might be useful for scripts, it's not at all clear that a
|
|
tool like `ruff <https://beta.ruff.rs/docs/>`__ would want to support per-file
|
|
configuration in this way, leading to confusion when users *expect* it to work,
|
|
but it doesn't. Furthermore, this sort of tool-specific configuration is just as
|
|
useful for individual files in a larger project, so we have to consider what it
|
|
would mean to embed a ``pyproject.toml`` into a single file in a larger project
|
|
that has its own ``pyproject.toml``.
|
|
|
|
In addition, ``pyproject.toml`` is currently focused on projects that are to be
|
|
built into wheels. There is `an ongoing discussion <pyproject without wheels_>`_
|
|
about how to use ``pyproject.toml`` for projects that are not intended to be
|
|
built as wheels, and until that question is resolved (which will likely require
|
|
some PEPs of its own) it seems premature to be discussing embedding
|
|
``pyproject.toml`` into scripts, which are *definitely* not intended to be built
|
|
and distributed in that manner.
|
|
|
|
The conclusion, therefore (which has been stated explicitly in some, but not
|
|
all, cases) is that this proposal is intended to mean that we would embed *part
|
|
of* ``pyproject.toml``. Typically this is the ``[project]`` section from
|
|
:pep:`621`, or even just the ``dependencies`` item from that section.
|
|
|
|
At this point, the first issue is that by framing the proposal as "embedding
|
|
``pyproject.toml``", we would be encouraging the sort of confusion discussed in
|
|
the previous paragraphs - developers will expect the full capabilities of
|
|
``pyproject.toml``, and be confused when there are differences and limitations.
|
|
It would be better, therefore, to consider this suggestion as simply being a
|
|
proposal to use an embedded TOML format, but specifically re-using the
|
|
*structure* of a particular part of ``pyproject.toml``. The problem then becomes
|
|
how we describe that structure, *without* causing confusion for people familiar
|
|
with ``pyproject.toml``. If we describe it with reference to ``pyproject.toml``,
|
|
the link is still there. But if we describe it in isolation, people will be
|
|
confused by the "similar but different" nature of the structure.
|
|
|
|
It is also important to remember that a key part of the target audience for this
|
|
proposal is developers who are simply using Python as a "better batch file"
|
|
solution. These developers will generally not be familiar with Python packaging
|
|
and its conventions, and are often the people most critical of the "complexity"
|
|
and "difficulty" of packaging solutions. As a result, proposals based on those
|
|
existing solutions are likely to be unwelcome to that audience, and could easily
|
|
result in people simply continuing to use existing adhoc solutions, and ignoring
|
|
the standard that was intended to make their lives easier.
|
|
|
|
Why not just set up a Python project with a ``pyproject.toml``?
|
|
---------------------------------------------------------------
|
|
|
|
Again, a key issue here is that the target audience for this proposal is people
|
|
writing scripts which aren't intended for distribution. Sometimes scripts will
|
|
be "shared", but this is far more informal than "distribution" - it typically
|
|
involves sending a script via an email with some written instructions on how to
|
|
run it, or passing someone a link to a gist.
|
|
|
|
Expecting such users to learn the complexities of Python packaging is a
|
|
significant step up in complexity, and would almost certainly give the
|
|
impression that "Python is too hard for scripts".
|
|
|
|
In addition, if the expectation here is that the ``pyproject.toml`` will somehow
|
|
be designed for running scripts in place, that's a new feature of the standard
|
|
that doesn't currently exist. At a minimum, this isn't a reasonable suggestion
|
|
until the `current discussion on Discourse <pyproject without wheels_>`_ about
|
|
using ``pyproject.toml`` for projects that won't be distributed as wheels is
|
|
resolved. And even then, it doesn't address the "sending someone a script in a
|
|
gist or email" use case.
|
|
|
|
Why not use a requirements file for dependencies?
|
|
-------------------------------------------------
|
|
|
|
Putting your requirements in a requirements file, doesn't require a PEP. You can
|
|
do that right now, and in fact it's quite likely that many adhoc solutions do
|
|
this. However, without a standard, there's no way of knowing how to locate a
|
|
script's dependency data. And furthermore, the requirements file format is
|
|
pip-specific, so tools relying on it are depending on a pip implementation
|
|
detail.
|
|
|
|
So in order to make a standard, two things would be required:
|
|
|
|
1. A standardised replacement for the requirements file format.
|
|
2. A standard for how to locate the requiements file for a given script.
|
|
|
|
The first item is a significant undertaking. It has been discussed on a number
|
|
of occasions, but so far no-one has attempted to actually do it. The most likely
|
|
approach would be for standards to be developed for individual use cases
|
|
currently addressed with requirements files. One option here would be for this
|
|
PEP to simply define a new file format which is simply a text file containing
|
|
:pep:`508` requirements, one per line. That would just leave the question of how
|
|
to locate that file.
|
|
|
|
The "obvious" solution here would be to do something like name the file the same
|
|
as the script, but with a ``.reqs`` extension (or something similar). However,
|
|
this still requires *two* files, where currently only a single file is needed,
|
|
and as such, does not match the "better batch file" model (shell scripts and
|
|
batch files are typically self-contained). It requires the developer to remember
|
|
to keep the two files together, and this may not always be possible. For
|
|
example, system administration policies may require that *all* files in a
|
|
certain directory are executable (the Linux filesystem standards require this of
|
|
``/usr/bin``, for example). And some methods of sharing a script (for example,
|
|
publishing it on a text file sharing service like Github's gist, or a corporate
|
|
intranet) may not allow for deriving the location of an associated requirements
|
|
file from the script's location (tools like ``pipx`` support running a script
|
|
directly from a URL, so "download and unpack a zip of the script and its
|
|
dependencies" may not be an appropriate requirement).
|
|
|
|
Essentially, though, the issue here is that there is an explicitly stated
|
|
requirement that the format supports storing dependency data *in the script file
|
|
itself*. Solutions that don't do that are simply ignoring that requirement.
|
|
|
|
Why not use (possibly restricted) Python syntax?
|
|
------------------------------------------------
|
|
|
|
This would typically involve storing the dependencies as a (runtime) list
|
|
variable with a conventional name, such as::
|
|
|
|
__requires__ = [
|
|
"requests",
|
|
"click",
|
|
]
|
|
|
|
Other suggestions include a static multi-line string, or including the
|
|
dependencies in the script's docstring.
|
|
|
|
The most significant problem with this proposal is that it requires all
|
|
consumers of the dependency data to implement a Python parser. Even if the
|
|
syntax is restricted, the *rest* of the script will use the full Python syntax,
|
|
and trying to define a syntax which can be successfully parsed in isolation from
|
|
the surrounding code is likely to be extremely difficult and error-prone.
|
|
|
|
Furthermore, Python's syntax changes in every release. If extracting dependency
|
|
data needs a Python parser, the parser will need to know which version of Python
|
|
the script is written for, and the overhead for a generic tool of having a
|
|
parser that can handle *multiple* versions of Python is unsustainable.
|
|
|
|
Even if the above issues could be addressed, the format would give the
|
|
impression that the data could be altered at runtime. However, this is not the
|
|
case in general, and code that tries to do so will encounter unexpected and
|
|
confusing behaviour.
|
|
|
|
And finally, there is no evidence that having dependency data available at
|
|
runtime is of any practical use. Should such a use be found, it is simple enough
|
|
to get the data by parsing the source - ``read_dependency_block(__file__)``.
|
|
|
|
It is worth noting, though, that the ``pip-run`` utility does implement (an
|
|
extended form of) this approach. `Further discussion <pip-run issue_>`_ of
|
|
the ``pip-run`` design is available on the project's issue tracker.
|
|
|
|
Should scripts be able to specify a package index?
|
|
--------------------------------------------------
|
|
|
|
Dependency metadata is about *what* package the code depends on, and not *where*
|
|
that package comes from. There is no difference here between metadata for
|
|
scripts, and metadata for distribution packages (as defined in
|
|
``pyproject.toml``). In both cases, dependencies are given in "abstract" form,
|
|
without specifying how they are obtained.
|
|
|
|
Some tools that use the dependency information may, of course, need to locate
|
|
concrete dependency artifacts - for example if they expect to create an
|
|
environment containing those dependencies. But the way they choose to do that
|
|
will be closely linked to the tool's UI in general, and this PEP does not try to
|
|
dictate the UI for tools.
|
|
|
|
There is more discussion of this point, and in particular of the UI choices made
|
|
by the ``pip-run`` tool, in `the previously mentioned pip-run issue <pip-run
|
|
issue_>`_.
|
|
|
|
What about local dependencies?
|
|
------------------------------
|
|
|
|
These can be handled without needing special metadata and tooling, simply by
|
|
adding the location of the dependencies to ``sys.path``. This PEP simply isn't
|
|
needed for this case. If, on the other hand, the "local dependencies" are actual
|
|
distributions which are published locally, they can be specified as usual with a
|
|
:pep:`508` requirement, and the local package index specified when running a
|
|
tool by using the tool's UI for that.
|
|
|
|
Open Issues
|
|
===========
|
|
|
|
None at this point.
|
|
|
|
|
|
References
|
|
==========
|
|
|
|
.. _pip-run issue: https://github.com/jaraco/pip-run/issues/44
|
|
.. _language survey: https://dbohdan.com/scripts-with-dependencies
|
|
.. _pyproject without wheels: https://discuss.python.org/t/projects-that-arent-meant-to-generate-a-wheel-and-pyproject-toml/29684
|
|
|
|
Copyright
|
|
=========
|
|
|
|
This document is placed in the public domain or under the
|
|
CC0-1.0-Universal license, whichever is more permissive.
|