Загрузить файлы в «venv/Lib/site-packages/pip/_vendor/distlib»
This commit is contained in:
284
venv/Lib/site-packages/pip/_vendor/distlib/LICENSE.txt
Normal file
284
venv/Lib/site-packages/pip/_vendor/distlib/LICENSE.txt
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
A. HISTORY OF THE SOFTWARE
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Python was created in the early 1990s by Guido van Rossum at Stichting
|
||||||
|
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
|
||||||
|
as a successor of a language called ABC. Guido remains Python's
|
||||||
|
principal author, although it includes many contributions from others.
|
||||||
|
|
||||||
|
In 1995, Guido continued his work on Python at the Corporation for
|
||||||
|
National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
|
||||||
|
in Reston, Virginia where he released several versions of the
|
||||||
|
software.
|
||||||
|
|
||||||
|
In May 2000, Guido and the Python core development team moved to
|
||||||
|
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
|
||||||
|
year, the PythonLabs team moved to Digital Creations (now Zope
|
||||||
|
Corporation, see http://www.zope.com). In 2001, the Python Software
|
||||||
|
Foundation (PSF, see http://www.python.org/psf/) was formed, a
|
||||||
|
non-profit organization created specifically to own Python-related
|
||||||
|
Intellectual Property. Zope Corporation is a sponsoring member of
|
||||||
|
the PSF.
|
||||||
|
|
||||||
|
All Python releases are Open Source (see http://www.opensource.org for
|
||||||
|
the Open Source Definition). Historically, most, but not all, Python
|
||||||
|
releases have also been GPL-compatible; the table below summarizes
|
||||||
|
the various releases.
|
||||||
|
|
||||||
|
Release Derived Year Owner GPL-
|
||||||
|
from compatible? (1)
|
||||||
|
|
||||||
|
0.9.0 thru 1.2 1991-1995 CWI yes
|
||||||
|
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
|
||||||
|
1.6 1.5.2 2000 CNRI no
|
||||||
|
2.0 1.6 2000 BeOpen.com no
|
||||||
|
1.6.1 1.6 2001 CNRI yes (2)
|
||||||
|
2.1 2.0+1.6.1 2001 PSF no
|
||||||
|
2.0.1 2.0+1.6.1 2001 PSF yes
|
||||||
|
2.1.1 2.1+2.0.1 2001 PSF yes
|
||||||
|
2.2 2.1.1 2001 PSF yes
|
||||||
|
2.1.2 2.1.1 2002 PSF yes
|
||||||
|
2.1.3 2.1.2 2002 PSF yes
|
||||||
|
2.2.1 2.2 2002 PSF yes
|
||||||
|
2.2.2 2.2.1 2002 PSF yes
|
||||||
|
2.2.3 2.2.2 2003 PSF yes
|
||||||
|
2.3 2.2.2 2002-2003 PSF yes
|
||||||
|
2.3.1 2.3 2002-2003 PSF yes
|
||||||
|
2.3.2 2.3.1 2002-2003 PSF yes
|
||||||
|
2.3.3 2.3.2 2002-2003 PSF yes
|
||||||
|
2.3.4 2.3.3 2004 PSF yes
|
||||||
|
2.3.5 2.3.4 2005 PSF yes
|
||||||
|
2.4 2.3 2004 PSF yes
|
||||||
|
2.4.1 2.4 2005 PSF yes
|
||||||
|
2.4.2 2.4.1 2005 PSF yes
|
||||||
|
2.4.3 2.4.2 2006 PSF yes
|
||||||
|
2.4.4 2.4.3 2006 PSF yes
|
||||||
|
2.5 2.4 2006 PSF yes
|
||||||
|
2.5.1 2.5 2007 PSF yes
|
||||||
|
2.5.2 2.5.1 2008 PSF yes
|
||||||
|
2.5.3 2.5.2 2008 PSF yes
|
||||||
|
2.6 2.5 2008 PSF yes
|
||||||
|
2.6.1 2.6 2008 PSF yes
|
||||||
|
2.6.2 2.6.1 2009 PSF yes
|
||||||
|
2.6.3 2.6.2 2009 PSF yes
|
||||||
|
2.6.4 2.6.3 2009 PSF yes
|
||||||
|
2.6.5 2.6.4 2010 PSF yes
|
||||||
|
3.0 2.6 2008 PSF yes
|
||||||
|
3.0.1 3.0 2009 PSF yes
|
||||||
|
3.1 3.0.1 2009 PSF yes
|
||||||
|
3.1.1 3.1 2009 PSF yes
|
||||||
|
3.1.2 3.1 2010 PSF yes
|
||||||
|
3.2 3.1 2010 PSF yes
|
||||||
|
|
||||||
|
Footnotes:
|
||||||
|
|
||||||
|
(1) GPL-compatible doesn't mean that we're distributing Python under
|
||||||
|
the GPL. All Python licenses, unlike the GPL, let you distribute
|
||||||
|
a modified version without making your changes open source. The
|
||||||
|
GPL-compatible licenses make it possible to combine Python with
|
||||||
|
other software that is released under the GPL; the others don't.
|
||||||
|
|
||||||
|
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
|
||||||
|
because its license has a choice of law clause. According to
|
||||||
|
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
|
||||||
|
is "not incompatible" with the GPL.
|
||||||
|
|
||||||
|
Thanks to the many outside volunteers who have worked under Guido's
|
||||||
|
direction to make these releases possible.
|
||||||
|
|
||||||
|
|
||||||
|
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
|
||||||
|
===============================================================
|
||||||
|
|
||||||
|
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||||
|
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||||
|
otherwise using this software ("Python") in source or binary form and
|
||||||
|
its associated documentation.
|
||||||
|
|
||||||
|
2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
||||||
|
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
|
||||||
|
analyze, test, perform and/or display publicly, prepare derivative works,
|
||||||
|
distribute, and otherwise use Python alone or in any derivative version,
|
||||||
|
provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
||||||
|
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
|
||||||
|
Python Software Foundation; All Rights Reserved" are retained in Python alone or
|
||||||
|
in any derivative version prepared by Licensee.
|
||||||
|
|
||||||
|
3. In the event Licensee prepares a derivative work that is based on
|
||||||
|
or incorporates Python or any part thereof, and wants to make
|
||||||
|
the derivative work available to others as provided herein, then
|
||||||
|
Licensee hereby agrees to include in any such work a brief summary of
|
||||||
|
the changes made to Python.
|
||||||
|
|
||||||
|
4. PSF is making Python available to Licensee on an "AS IS"
|
||||||
|
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||||
|
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
||||||
|
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||||
|
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
||||||
|
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||||
|
|
||||||
|
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||||
|
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||||
|
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
||||||
|
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||||
|
|
||||||
|
6. This License Agreement will automatically terminate upon a material
|
||||||
|
breach of its terms and conditions.
|
||||||
|
|
||||||
|
7. Nothing in this License Agreement shall be deemed to create any
|
||||||
|
relationship of agency, partnership, or joint venture between PSF and
|
||||||
|
Licensee. This License Agreement does not grant permission to use PSF
|
||||||
|
trademarks or trade name in a trademark sense to endorse or promote
|
||||||
|
products or services of Licensee, or any third party.
|
||||||
|
|
||||||
|
8. By copying, installing or otherwise using Python, Licensee
|
||||||
|
agrees to be bound by the terms and conditions of this License
|
||||||
|
Agreement.
|
||||||
|
|
||||||
|
|
||||||
|
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
|
||||||
|
|
||||||
|
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
|
||||||
|
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
|
||||||
|
Individual or Organization ("Licensee") accessing and otherwise using
|
||||||
|
this software in source or binary form and its associated
|
||||||
|
documentation ("the Software").
|
||||||
|
|
||||||
|
2. Subject to the terms and conditions of this BeOpen Python License
|
||||||
|
Agreement, BeOpen hereby grants Licensee a non-exclusive,
|
||||||
|
royalty-free, world-wide license to reproduce, analyze, test, perform
|
||||||
|
and/or display publicly, prepare derivative works, distribute, and
|
||||||
|
otherwise use the Software alone or in any derivative version,
|
||||||
|
provided, however, that the BeOpen Python License is retained in the
|
||||||
|
Software, alone or in any derivative version prepared by Licensee.
|
||||||
|
|
||||||
|
3. BeOpen is making the Software available to Licensee on an "AS IS"
|
||||||
|
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||||
|
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
|
||||||
|
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||||
|
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
|
||||||
|
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||||
|
|
||||||
|
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
|
||||||
|
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
|
||||||
|
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
|
||||||
|
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||||
|
|
||||||
|
5. This License Agreement will automatically terminate upon a material
|
||||||
|
breach of its terms and conditions.
|
||||||
|
|
||||||
|
6. This License Agreement shall be governed by and interpreted in all
|
||||||
|
respects by the law of the State of California, excluding conflict of
|
||||||
|
law provisions. Nothing in this License Agreement shall be deemed to
|
||||||
|
create any relationship of agency, partnership, or joint venture
|
||||||
|
between BeOpen and Licensee. This License Agreement does not grant
|
||||||
|
permission to use BeOpen trademarks or trade names in a trademark
|
||||||
|
sense to endorse or promote products or services of Licensee, or any
|
||||||
|
third party. As an exception, the "BeOpen Python" logos available at
|
||||||
|
http://www.pythonlabs.com/logos.html may be used according to the
|
||||||
|
permissions granted on that web page.
|
||||||
|
|
||||||
|
7. By copying, installing or otherwise using the software, Licensee
|
||||||
|
agrees to be bound by the terms and conditions of this License
|
||||||
|
Agreement.
|
||||||
|
|
||||||
|
|
||||||
|
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
1. This LICENSE AGREEMENT is between the Corporation for National
|
||||||
|
Research Initiatives, having an office at 1895 Preston White Drive,
|
||||||
|
Reston, VA 20191 ("CNRI"), and the Individual or Organization
|
||||||
|
("Licensee") accessing and otherwise using Python 1.6.1 software in
|
||||||
|
source or binary form and its associated documentation.
|
||||||
|
|
||||||
|
2. Subject to the terms and conditions of this License Agreement, CNRI
|
||||||
|
hereby grants Licensee a nonexclusive, royalty-free, world-wide
|
||||||
|
license to reproduce, analyze, test, perform and/or display publicly,
|
||||||
|
prepare derivative works, distribute, and otherwise use Python 1.6.1
|
||||||
|
alone or in any derivative version, provided, however, that CNRI's
|
||||||
|
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
|
||||||
|
1995-2001 Corporation for National Research Initiatives; All Rights
|
||||||
|
Reserved" are retained in Python 1.6.1 alone or in any derivative
|
||||||
|
version prepared by Licensee. Alternately, in lieu of CNRI's License
|
||||||
|
Agreement, Licensee may substitute the following text (omitting the
|
||||||
|
quotes): "Python 1.6.1 is made available subject to the terms and
|
||||||
|
conditions in CNRI's License Agreement. This Agreement together with
|
||||||
|
Python 1.6.1 may be located on the Internet using the following
|
||||||
|
unique, persistent identifier (known as a handle): 1895.22/1013. This
|
||||||
|
Agreement may also be obtained from a proxy server on the Internet
|
||||||
|
using the following URL: http://hdl.handle.net/1895.22/1013".
|
||||||
|
|
||||||
|
3. In the event Licensee prepares a derivative work that is based on
|
||||||
|
or incorporates Python 1.6.1 or any part thereof, and wants to make
|
||||||
|
the derivative work available to others as provided herein, then
|
||||||
|
Licensee hereby agrees to include in any such work a brief summary of
|
||||||
|
the changes made to Python 1.6.1.
|
||||||
|
|
||||||
|
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
|
||||||
|
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||||
|
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
|
||||||
|
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||||
|
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
|
||||||
|
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||||
|
|
||||||
|
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||||
|
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||||
|
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
|
||||||
|
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||||
|
|
||||||
|
6. This License Agreement will automatically terminate upon a material
|
||||||
|
breach of its terms and conditions.
|
||||||
|
|
||||||
|
7. This License Agreement shall be governed by the federal
|
||||||
|
intellectual property law of the United States, including without
|
||||||
|
limitation the federal copyright law, and, to the extent such
|
||||||
|
U.S. federal law does not apply, by the law of the Commonwealth of
|
||||||
|
Virginia, excluding Virginia's conflict of law provisions.
|
||||||
|
Notwithstanding the foregoing, with regard to derivative works based
|
||||||
|
on Python 1.6.1 that incorporate non-separable material that was
|
||||||
|
previously distributed under the GNU General Public License (GPL), the
|
||||||
|
law of the Commonwealth of Virginia shall govern this License
|
||||||
|
Agreement only as to issues arising under or with respect to
|
||||||
|
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
|
||||||
|
License Agreement shall be deemed to create any relationship of
|
||||||
|
agency, partnership, or joint venture between CNRI and Licensee. This
|
||||||
|
License Agreement does not grant permission to use CNRI trademarks or
|
||||||
|
trade name in a trademark sense to endorse or promote products or
|
||||||
|
services of Licensee, or any third party.
|
||||||
|
|
||||||
|
8. By clicking on the "ACCEPT" button where indicated, or by copying,
|
||||||
|
installing or otherwise using Python 1.6.1, Licensee agrees to be
|
||||||
|
bound by the terms and conditions of this License Agreement.
|
||||||
|
|
||||||
|
ACCEPT
|
||||||
|
|
||||||
|
|
||||||
|
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
|
||||||
|
--------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
|
||||||
|
The Netherlands. All rights reserved.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software and its
|
||||||
|
documentation for any purpose and without fee is hereby granted,
|
||||||
|
provided that the above copyright notice appear in all copies and that
|
||||||
|
both that copyright notice and this permission notice appear in
|
||||||
|
supporting documentation, and that the name of Stichting Mathematisch
|
||||||
|
Centrum or CWI not be used in advertising or publicity pertaining to
|
||||||
|
distribution of the software without specific, written prior
|
||||||
|
permission.
|
||||||
|
|
||||||
|
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||||
|
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
|
||||||
|
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
|
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
33
venv/Lib/site-packages/pip/_vendor/distlib/__init__.py
Normal file
33
venv/Lib/site-packages/pip/_vendor/distlib/__init__.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (C) 2012-2024 Vinay Sajip.
|
||||||
|
# Licensed to the Python Software Foundation under a contributor agreement.
|
||||||
|
# See LICENSE.txt and CONTRIBUTORS.txt.
|
||||||
|
#
|
||||||
|
import logging
|
||||||
|
|
||||||
|
__version__ = '0.4.0'
|
||||||
|
|
||||||
|
|
||||||
|
class DistlibException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from logging import NullHandler
|
||||||
|
except ImportError: # pragma: no cover
|
||||||
|
|
||||||
|
class NullHandler(logging.Handler):
|
||||||
|
|
||||||
|
def handle(self, record):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def createLock(self):
|
||||||
|
self.lock = None
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
logger.addHandler(NullHandler())
|
||||||
1137
venv/Lib/site-packages/pip/_vendor/distlib/compat.py
Normal file
1137
venv/Lib/site-packages/pip/_vendor/distlib/compat.py
Normal file
File diff suppressed because it is too large
Load Diff
358
venv/Lib/site-packages/pip/_vendor/distlib/resources.py
Normal file
358
venv/Lib/site-packages/pip/_vendor/distlib/resources.py
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013-2017 Vinay Sajip.
|
||||||
|
# Licensed to the Python Software Foundation under a contributor agreement.
|
||||||
|
# See LICENSE.txt and CONTRIBUTORS.txt.
|
||||||
|
#
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import bisect
|
||||||
|
import io
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import pkgutil
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
import zipimport
|
||||||
|
|
||||||
|
from . import DistlibException
|
||||||
|
from .util import cached_property, get_cache_base, Cache
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
cache = None # created when needed
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceCache(Cache):
|
||||||
|
def __init__(self, base=None):
|
||||||
|
if base is None:
|
||||||
|
# Use native string to avoid issues on 2.x: see Python #20140.
|
||||||
|
base = os.path.join(get_cache_base(), str('resource-cache'))
|
||||||
|
super(ResourceCache, self).__init__(base)
|
||||||
|
|
||||||
|
def is_stale(self, resource, path):
|
||||||
|
"""
|
||||||
|
Is the cache stale for the given resource?
|
||||||
|
|
||||||
|
:param resource: The :class:`Resource` being cached.
|
||||||
|
:param path: The path of the resource in the cache.
|
||||||
|
:return: True if the cache is stale.
|
||||||
|
"""
|
||||||
|
# Cache invalidation is a hard problem :-)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get(self, resource):
|
||||||
|
"""
|
||||||
|
Get a resource into the cache,
|
||||||
|
|
||||||
|
:param resource: A :class:`Resource` instance.
|
||||||
|
:return: The pathname of the resource in the cache.
|
||||||
|
"""
|
||||||
|
prefix, path = resource.finder.get_cache_info(resource)
|
||||||
|
if prefix is None:
|
||||||
|
result = path
|
||||||
|
else:
|
||||||
|
result = os.path.join(self.base, self.prefix_to_dir(prefix), path)
|
||||||
|
dirname = os.path.dirname(result)
|
||||||
|
if not os.path.isdir(dirname):
|
||||||
|
os.makedirs(dirname)
|
||||||
|
if not os.path.exists(result):
|
||||||
|
stale = True
|
||||||
|
else:
|
||||||
|
stale = self.is_stale(resource, path)
|
||||||
|
if stale:
|
||||||
|
# write the bytes of the resource to the cache location
|
||||||
|
with open(result, 'wb') as f:
|
||||||
|
f.write(resource.bytes)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceBase(object):
|
||||||
|
def __init__(self, finder, name):
|
||||||
|
self.finder = finder
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
|
||||||
|
class Resource(ResourceBase):
|
||||||
|
"""
|
||||||
|
A class representing an in-package resource, such as a data file. This is
|
||||||
|
not normally instantiated by user code, but rather by a
|
||||||
|
:class:`ResourceFinder` which manages the resource.
|
||||||
|
"""
|
||||||
|
is_container = False # Backwards compatibility
|
||||||
|
|
||||||
|
def as_stream(self):
|
||||||
|
"""
|
||||||
|
Get the resource as a stream.
|
||||||
|
|
||||||
|
This is not a property to make it obvious that it returns a new stream
|
||||||
|
each time.
|
||||||
|
"""
|
||||||
|
return self.finder.get_stream(self)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def file_path(self):
|
||||||
|
global cache
|
||||||
|
if cache is None:
|
||||||
|
cache = ResourceCache()
|
||||||
|
return cache.get(self)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def bytes(self):
|
||||||
|
return self.finder.get_bytes(self)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def size(self):
|
||||||
|
return self.finder.get_size(self)
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceContainer(ResourceBase):
|
||||||
|
is_container = True # Backwards compatibility
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def resources(self):
|
||||||
|
return self.finder.get_resources(self)
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceFinder(object):
|
||||||
|
"""
|
||||||
|
Resource finder for file system resources.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if sys.platform.startswith('java'):
|
||||||
|
skipped_extensions = ('.pyc', '.pyo', '.class')
|
||||||
|
else:
|
||||||
|
skipped_extensions = ('.pyc', '.pyo')
|
||||||
|
|
||||||
|
def __init__(self, module):
|
||||||
|
self.module = module
|
||||||
|
self.loader = getattr(module, '__loader__', None)
|
||||||
|
self.base = os.path.dirname(getattr(module, '__file__', ''))
|
||||||
|
|
||||||
|
def _adjust_path(self, path):
|
||||||
|
return os.path.realpath(path)
|
||||||
|
|
||||||
|
def _make_path(self, resource_name):
|
||||||
|
# Issue #50: need to preserve type of path on Python 2.x
|
||||||
|
# like os.path._get_sep
|
||||||
|
if isinstance(resource_name, bytes): # should only happen on 2.x
|
||||||
|
sep = b'/'
|
||||||
|
else:
|
||||||
|
sep = '/'
|
||||||
|
parts = resource_name.split(sep)
|
||||||
|
parts.insert(0, self.base)
|
||||||
|
result = os.path.join(*parts)
|
||||||
|
return self._adjust_path(result)
|
||||||
|
|
||||||
|
def _find(self, path):
|
||||||
|
return os.path.exists(path)
|
||||||
|
|
||||||
|
def get_cache_info(self, resource):
|
||||||
|
return None, resource.path
|
||||||
|
|
||||||
|
def find(self, resource_name):
|
||||||
|
path = self._make_path(resource_name)
|
||||||
|
if not self._find(path):
|
||||||
|
result = None
|
||||||
|
else:
|
||||||
|
if self._is_directory(path):
|
||||||
|
result = ResourceContainer(self, resource_name)
|
||||||
|
else:
|
||||||
|
result = Resource(self, resource_name)
|
||||||
|
result.path = path
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_stream(self, resource):
|
||||||
|
return open(resource.path, 'rb')
|
||||||
|
|
||||||
|
def get_bytes(self, resource):
|
||||||
|
with open(resource.path, 'rb') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
def get_size(self, resource):
|
||||||
|
return os.path.getsize(resource.path)
|
||||||
|
|
||||||
|
def get_resources(self, resource):
|
||||||
|
def allowed(f):
|
||||||
|
return (f != '__pycache__' and not
|
||||||
|
f.endswith(self.skipped_extensions))
|
||||||
|
return set([f for f in os.listdir(resource.path) if allowed(f)])
|
||||||
|
|
||||||
|
def is_container(self, resource):
|
||||||
|
return self._is_directory(resource.path)
|
||||||
|
|
||||||
|
_is_directory = staticmethod(os.path.isdir)
|
||||||
|
|
||||||
|
def iterator(self, resource_name):
|
||||||
|
resource = self.find(resource_name)
|
||||||
|
if resource is not None:
|
||||||
|
todo = [resource]
|
||||||
|
while todo:
|
||||||
|
resource = todo.pop(0)
|
||||||
|
yield resource
|
||||||
|
if resource.is_container:
|
||||||
|
rname = resource.name
|
||||||
|
for name in resource.resources:
|
||||||
|
if not rname:
|
||||||
|
new_name = name
|
||||||
|
else:
|
||||||
|
new_name = '/'.join([rname, name])
|
||||||
|
child = self.find(new_name)
|
||||||
|
if child.is_container:
|
||||||
|
todo.append(child)
|
||||||
|
else:
|
||||||
|
yield child
|
||||||
|
|
||||||
|
|
||||||
|
class ZipResourceFinder(ResourceFinder):
|
||||||
|
"""
|
||||||
|
Resource finder for resources in .zip files.
|
||||||
|
"""
|
||||||
|
def __init__(self, module):
|
||||||
|
super(ZipResourceFinder, self).__init__(module)
|
||||||
|
archive = self.loader.archive
|
||||||
|
self.prefix_len = 1 + len(archive)
|
||||||
|
# PyPy doesn't have a _files attr on zipimporter, and you can't set one
|
||||||
|
if hasattr(self.loader, '_files'):
|
||||||
|
self._files = self.loader._files
|
||||||
|
else:
|
||||||
|
self._files = zipimport._zip_directory_cache[archive]
|
||||||
|
self.index = sorted(self._files)
|
||||||
|
|
||||||
|
def _adjust_path(self, path):
|
||||||
|
return path
|
||||||
|
|
||||||
|
def _find(self, path):
|
||||||
|
path = path[self.prefix_len:]
|
||||||
|
if path in self._files:
|
||||||
|
result = True
|
||||||
|
else:
|
||||||
|
if path and path[-1] != os.sep:
|
||||||
|
path = path + os.sep
|
||||||
|
i = bisect.bisect(self.index, path)
|
||||||
|
try:
|
||||||
|
result = self.index[i].startswith(path)
|
||||||
|
except IndexError:
|
||||||
|
result = False
|
||||||
|
if not result:
|
||||||
|
logger.debug('_find failed: %r %r', path, self.loader.prefix)
|
||||||
|
else:
|
||||||
|
logger.debug('_find worked: %r %r', path, self.loader.prefix)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_cache_info(self, resource):
|
||||||
|
prefix = self.loader.archive
|
||||||
|
path = resource.path[1 + len(prefix):]
|
||||||
|
return prefix, path
|
||||||
|
|
||||||
|
def get_bytes(self, resource):
|
||||||
|
return self.loader.get_data(resource.path)
|
||||||
|
|
||||||
|
def get_stream(self, resource):
|
||||||
|
return io.BytesIO(self.get_bytes(resource))
|
||||||
|
|
||||||
|
def get_size(self, resource):
|
||||||
|
path = resource.path[self.prefix_len:]
|
||||||
|
return self._files[path][3]
|
||||||
|
|
||||||
|
def get_resources(self, resource):
|
||||||
|
path = resource.path[self.prefix_len:]
|
||||||
|
if path and path[-1] != os.sep:
|
||||||
|
path += os.sep
|
||||||
|
plen = len(path)
|
||||||
|
result = set()
|
||||||
|
i = bisect.bisect(self.index, path)
|
||||||
|
while i < len(self.index):
|
||||||
|
if not self.index[i].startswith(path):
|
||||||
|
break
|
||||||
|
s = self.index[i][plen:]
|
||||||
|
result.add(s.split(os.sep, 1)[0]) # only immediate children
|
||||||
|
i += 1
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _is_directory(self, path):
|
||||||
|
path = path[self.prefix_len:]
|
||||||
|
if path and path[-1] != os.sep:
|
||||||
|
path += os.sep
|
||||||
|
i = bisect.bisect(self.index, path)
|
||||||
|
try:
|
||||||
|
result = self.index[i].startswith(path)
|
||||||
|
except IndexError:
|
||||||
|
result = False
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
_finder_registry = {
|
||||||
|
type(None): ResourceFinder,
|
||||||
|
zipimport.zipimporter: ZipResourceFinder
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# In Python 3.6, _frozen_importlib -> _frozen_importlib_external
|
||||||
|
try:
|
||||||
|
import _frozen_importlib_external as _fi
|
||||||
|
except ImportError:
|
||||||
|
import _frozen_importlib as _fi
|
||||||
|
_finder_registry[_fi.SourceFileLoader] = ResourceFinder
|
||||||
|
_finder_registry[_fi.FileFinder] = ResourceFinder
|
||||||
|
# See issue #146
|
||||||
|
_finder_registry[_fi.SourcelessFileLoader] = ResourceFinder
|
||||||
|
del _fi
|
||||||
|
except (ImportError, AttributeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def register_finder(loader, finder_maker):
|
||||||
|
_finder_registry[type(loader)] = finder_maker
|
||||||
|
|
||||||
|
|
||||||
|
_finder_cache = {}
|
||||||
|
|
||||||
|
|
||||||
|
def finder(package):
|
||||||
|
"""
|
||||||
|
Return a resource finder for a package.
|
||||||
|
:param package: The name of the package.
|
||||||
|
:return: A :class:`ResourceFinder` instance for the package.
|
||||||
|
"""
|
||||||
|
if package in _finder_cache:
|
||||||
|
result = _finder_cache[package]
|
||||||
|
else:
|
||||||
|
if package not in sys.modules:
|
||||||
|
__import__(package)
|
||||||
|
module = sys.modules[package]
|
||||||
|
path = getattr(module, '__path__', None)
|
||||||
|
if path is None:
|
||||||
|
raise DistlibException('You cannot get a finder for a module, '
|
||||||
|
'only for a package')
|
||||||
|
loader = getattr(module, '__loader__', None)
|
||||||
|
finder_maker = _finder_registry.get(type(loader))
|
||||||
|
if finder_maker is None:
|
||||||
|
raise DistlibException('Unable to locate finder for %r' % package)
|
||||||
|
result = finder_maker(module)
|
||||||
|
_finder_cache[package] = result
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
_dummy_module = types.ModuleType(str('__dummy__'))
|
||||||
|
|
||||||
|
|
||||||
|
def finder_for_path(path):
|
||||||
|
"""
|
||||||
|
Return a resource finder for a path, which should represent a container.
|
||||||
|
|
||||||
|
:param path: The path.
|
||||||
|
:return: A :class:`ResourceFinder` instance for the path.
|
||||||
|
"""
|
||||||
|
result = None
|
||||||
|
# calls any path hooks, gets importer into cache
|
||||||
|
pkgutil.get_importer(path)
|
||||||
|
loader = sys.path_importer_cache.get(path)
|
||||||
|
finder = _finder_registry.get(type(loader))
|
||||||
|
if finder:
|
||||||
|
module = _dummy_module
|
||||||
|
module.__file__ = os.path.join(path, '')
|
||||||
|
module.__loader__ = loader
|
||||||
|
result = finder(module)
|
||||||
|
return result
|
||||||
447
venv/Lib/site-packages/pip/_vendor/distlib/scripts.py
Normal file
447
venv/Lib/site-packages/pip/_vendor/distlib/scripts.py
Normal file
@@ -0,0 +1,447 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013-2023 Vinay Sajip.
|
||||||
|
# Licensed to the Python Software Foundation under a contributor agreement.
|
||||||
|
# See LICENSE.txt and CONTRIBUTORS.txt.
|
||||||
|
#
|
||||||
|
from io import BytesIO
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from zipfile import ZipInfo
|
||||||
|
|
||||||
|
from .compat import sysconfig, detect_encoding, ZipFile
|
||||||
|
from .resources import finder
|
||||||
|
from .util import (FileOperator, get_export_entry, convert_path, get_executable, get_platform, in_venv)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
_DEFAULT_MANIFEST = '''
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
|
<assemblyIdentity version="1.0.0.0"
|
||||||
|
processorArchitecture="X86"
|
||||||
|
name="%s"
|
||||||
|
type="win32"/>
|
||||||
|
|
||||||
|
<!-- Identify the application security requirements. -->
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges>
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
</assembly>'''.strip()
|
||||||
|
|
||||||
|
# check if Python is called on the first line with this expression
|
||||||
|
FIRST_LINE_RE = re.compile(b'^#!.*pythonw?[0-9.]*([ \t].*)?$')
|
||||||
|
SCRIPT_TEMPLATE = r'''# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from %(module)s import %(import_name)s
|
||||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||||
|
sys.exit(%(func)s())
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Pre-fetch the contents of all executable wrapper stubs.
|
||||||
|
# This is to address https://github.com/pypa/pip/issues/12666.
|
||||||
|
# When updating pip, we rename the old pip in place before installing the
|
||||||
|
# new version. If we try to fetch a wrapper *after* that rename, the finder
|
||||||
|
# machinery will be confused as the package is no longer available at the
|
||||||
|
# location where it was imported from. So we load everything into memory in
|
||||||
|
# advance.
|
||||||
|
|
||||||
|
if os.name == 'nt' or (os.name == 'java' and os._name == 'nt'):
|
||||||
|
# Issue 31: don't hardcode an absolute package name, but
|
||||||
|
# determine it relative to the current package
|
||||||
|
DISTLIB_PACKAGE = __name__.rsplit('.', 1)[0]
|
||||||
|
|
||||||
|
WRAPPERS = {
|
||||||
|
r.name: r.bytes
|
||||||
|
for r in finder(DISTLIB_PACKAGE).iterator("")
|
||||||
|
if r.name.endswith(".exe")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def enquote_executable(executable):
|
||||||
|
if ' ' in executable:
|
||||||
|
# make sure we quote only the executable in case of env
|
||||||
|
# for example /usr/bin/env "/dir with spaces/bin/jython"
|
||||||
|
# instead of "/usr/bin/env /dir with spaces/bin/jython"
|
||||||
|
# otherwise whole
|
||||||
|
if executable.startswith('/usr/bin/env '):
|
||||||
|
env, _executable = executable.split(' ', 1)
|
||||||
|
if ' ' in _executable and not _executable.startswith('"'):
|
||||||
|
executable = '%s "%s"' % (env, _executable)
|
||||||
|
else:
|
||||||
|
if not executable.startswith('"'):
|
||||||
|
executable = '"%s"' % executable
|
||||||
|
return executable
|
||||||
|
|
||||||
|
|
||||||
|
# Keep the old name around (for now), as there is at least one project using it!
|
||||||
|
_enquote_executable = enquote_executable
|
||||||
|
|
||||||
|
|
||||||
|
class ScriptMaker(object):
|
||||||
|
"""
|
||||||
|
A class to copy or create scripts from source scripts or callable
|
||||||
|
specifications.
|
||||||
|
"""
|
||||||
|
script_template = SCRIPT_TEMPLATE
|
||||||
|
|
||||||
|
executable = None # for shebangs
|
||||||
|
|
||||||
|
def __init__(self, source_dir, target_dir, add_launchers=True, dry_run=False, fileop=None):
|
||||||
|
self.source_dir = source_dir
|
||||||
|
self.target_dir = target_dir
|
||||||
|
self.add_launchers = add_launchers
|
||||||
|
self.force = False
|
||||||
|
self.clobber = False
|
||||||
|
# It only makes sense to set mode bits on POSIX.
|
||||||
|
self.set_mode = (os.name == 'posix') or (os.name == 'java' and os._name == 'posix')
|
||||||
|
self.variants = set(('', 'X.Y'))
|
||||||
|
self._fileop = fileop or FileOperator(dry_run)
|
||||||
|
|
||||||
|
self._is_nt = os.name == 'nt' or (os.name == 'java' and os._name == 'nt')
|
||||||
|
self.version_info = sys.version_info
|
||||||
|
|
||||||
|
def _get_alternate_executable(self, executable, options):
|
||||||
|
if options.get('gui', False) and self._is_nt: # pragma: no cover
|
||||||
|
dn, fn = os.path.split(executable)
|
||||||
|
fn = fn.replace('python', 'pythonw')
|
||||||
|
executable = os.path.join(dn, fn)
|
||||||
|
return executable
|
||||||
|
|
||||||
|
if sys.platform.startswith('java'): # pragma: no cover
|
||||||
|
|
||||||
|
def _is_shell(self, executable):
|
||||||
|
"""
|
||||||
|
Determine if the specified executable is a script
|
||||||
|
(contains a #! line)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(executable) as fp:
|
||||||
|
return fp.read(2) == '#!'
|
||||||
|
except (OSError, IOError):
|
||||||
|
logger.warning('Failed to open %s', executable)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _fix_jython_executable(self, executable):
|
||||||
|
if self._is_shell(executable):
|
||||||
|
# Workaround for Jython is not needed on Linux systems.
|
||||||
|
import java
|
||||||
|
|
||||||
|
if java.lang.System.getProperty('os.name') == 'Linux':
|
||||||
|
return executable
|
||||||
|
elif executable.lower().endswith('jython.exe'):
|
||||||
|
# Use wrapper exe for Jython on Windows
|
||||||
|
return executable
|
||||||
|
return '/usr/bin/env %s' % executable
|
||||||
|
|
||||||
|
def _build_shebang(self, executable, post_interp):
|
||||||
|
"""
|
||||||
|
Build a shebang line. In the simple case (on Windows, or a shebang line
|
||||||
|
which is not too long or contains spaces) use a simple formulation for
|
||||||
|
the shebang. Otherwise, use /bin/sh as the executable, with a contrived
|
||||||
|
shebang which allows the script to run either under Python or sh, using
|
||||||
|
suitable quoting. Thanks to Harald Nordgren for his input.
|
||||||
|
|
||||||
|
See also: http://www.in-ulm.de/~mascheck/various/shebang/#length
|
||||||
|
https://hg.mozilla.org/mozilla-central/file/tip/mach
|
||||||
|
"""
|
||||||
|
if os.name != 'posix':
|
||||||
|
simple_shebang = True
|
||||||
|
elif getattr(sys, "cross_compiling", False):
|
||||||
|
# In a cross-compiling environment, the shebang will likely be a
|
||||||
|
# script; this *must* be invoked with the "safe" version of the
|
||||||
|
# shebang, or else using os.exec() to run the entry script will
|
||||||
|
# fail, raising "OSError 8 [Errno 8] Exec format error".
|
||||||
|
simple_shebang = False
|
||||||
|
else:
|
||||||
|
# Add 3 for '#!' prefix and newline suffix.
|
||||||
|
shebang_length = len(executable) + len(post_interp) + 3
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
max_shebang_length = 512
|
||||||
|
else:
|
||||||
|
max_shebang_length = 127
|
||||||
|
simple_shebang = ((b' ' not in executable) and (shebang_length <= max_shebang_length))
|
||||||
|
|
||||||
|
if simple_shebang:
|
||||||
|
result = b'#!' + executable + post_interp + b'\n'
|
||||||
|
else:
|
||||||
|
result = b'#!/bin/sh\n'
|
||||||
|
result += b"'''exec' " + executable + post_interp + b' "$0" "$@"\n'
|
||||||
|
result += b"' '''\n"
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _get_shebang(self, encoding, post_interp=b'', options=None):
|
||||||
|
enquote = True
|
||||||
|
if self.executable:
|
||||||
|
executable = self.executable
|
||||||
|
enquote = False # assume this will be taken care of
|
||||||
|
elif not sysconfig.is_python_build():
|
||||||
|
executable = get_executable()
|
||||||
|
elif in_venv(): # pragma: no cover
|
||||||
|
executable = os.path.join(sysconfig.get_path('scripts'), 'python%s' % sysconfig.get_config_var('EXE'))
|
||||||
|
else: # pragma: no cover
|
||||||
|
if os.name == 'nt':
|
||||||
|
# for Python builds from source on Windows, no Python executables with
|
||||||
|
# a version suffix are created, so we use python.exe
|
||||||
|
executable = os.path.join(sysconfig.get_config_var('BINDIR'),
|
||||||
|
'python%s' % (sysconfig.get_config_var('EXE')))
|
||||||
|
else:
|
||||||
|
executable = os.path.join(
|
||||||
|
sysconfig.get_config_var('BINDIR'),
|
||||||
|
'python%s%s' % (sysconfig.get_config_var('VERSION'), sysconfig.get_config_var('EXE')))
|
||||||
|
if options:
|
||||||
|
executable = self._get_alternate_executable(executable, options)
|
||||||
|
|
||||||
|
if sys.platform.startswith('java'): # pragma: no cover
|
||||||
|
executable = self._fix_jython_executable(executable)
|
||||||
|
|
||||||
|
# Normalise case for Windows - COMMENTED OUT
|
||||||
|
# executable = os.path.normcase(executable)
|
||||||
|
# N.B. The normalising operation above has been commented out: See
|
||||||
|
# issue #124. Although paths in Windows are generally case-insensitive,
|
||||||
|
# they aren't always. For example, a path containing a ẞ (which is a
|
||||||
|
# LATIN CAPITAL LETTER SHARP S - U+1E9E) is normcased to ß (which is a
|
||||||
|
# LATIN SMALL LETTER SHARP S' - U+00DF). The two are not considered by
|
||||||
|
# Windows as equivalent in path names.
|
||||||
|
|
||||||
|
# If the user didn't specify an executable, it may be necessary to
|
||||||
|
# cater for executable paths with spaces (not uncommon on Windows)
|
||||||
|
if enquote:
|
||||||
|
executable = enquote_executable(executable)
|
||||||
|
# Issue #51: don't use fsencode, since we later try to
|
||||||
|
# check that the shebang is decodable using utf-8.
|
||||||
|
executable = executable.encode('utf-8')
|
||||||
|
# in case of IronPython, play safe and enable frames support
|
||||||
|
if (sys.platform == 'cli' and '-X:Frames' not in post_interp and
|
||||||
|
'-X:FullFrames' not in post_interp): # pragma: no cover
|
||||||
|
post_interp += b' -X:Frames'
|
||||||
|
shebang = self._build_shebang(executable, post_interp)
|
||||||
|
# Python parser starts to read a script using UTF-8 until
|
||||||
|
# it gets a #coding:xxx cookie. The shebang has to be the
|
||||||
|
# first line of a file, the #coding:xxx cookie cannot be
|
||||||
|
# written before. So the shebang has to be decodable from
|
||||||
|
# UTF-8.
|
||||||
|
try:
|
||||||
|
shebang.decode('utf-8')
|
||||||
|
except UnicodeDecodeError: # pragma: no cover
|
||||||
|
raise ValueError('The shebang (%r) is not decodable from utf-8' % shebang)
|
||||||
|
# If the script is encoded to a custom encoding (use a
|
||||||
|
# #coding:xxx cookie), the shebang has to be decodable from
|
||||||
|
# the script encoding too.
|
||||||
|
if encoding != 'utf-8':
|
||||||
|
try:
|
||||||
|
shebang.decode(encoding)
|
||||||
|
except UnicodeDecodeError: # pragma: no cover
|
||||||
|
raise ValueError('The shebang (%r) is not decodable '
|
||||||
|
'from the script encoding (%r)' % (shebang, encoding))
|
||||||
|
return shebang
|
||||||
|
|
||||||
|
def _get_script_text(self, entry):
|
||||||
|
return self.script_template % dict(
|
||||||
|
module=entry.prefix, import_name=entry.suffix.split('.')[0], func=entry.suffix)
|
||||||
|
|
||||||
|
manifest = _DEFAULT_MANIFEST
|
||||||
|
|
||||||
|
def get_manifest(self, exename):
|
||||||
|
base = os.path.basename(exename)
|
||||||
|
return self.manifest % base
|
||||||
|
|
||||||
|
def _write_script(self, names, shebang, script_bytes, filenames, ext):
|
||||||
|
use_launcher = self.add_launchers and self._is_nt
|
||||||
|
if not use_launcher:
|
||||||
|
script_bytes = shebang + script_bytes
|
||||||
|
else: # pragma: no cover
|
||||||
|
if ext == 'py':
|
||||||
|
launcher = self._get_launcher('t')
|
||||||
|
else:
|
||||||
|
launcher = self._get_launcher('w')
|
||||||
|
stream = BytesIO()
|
||||||
|
with ZipFile(stream, 'w') as zf:
|
||||||
|
source_date_epoch = os.environ.get('SOURCE_DATE_EPOCH')
|
||||||
|
if source_date_epoch:
|
||||||
|
date_time = time.gmtime(int(source_date_epoch))[:6]
|
||||||
|
zinfo = ZipInfo(filename='__main__.py', date_time=date_time)
|
||||||
|
zf.writestr(zinfo, script_bytes)
|
||||||
|
else:
|
||||||
|
zf.writestr('__main__.py', script_bytes)
|
||||||
|
zip_data = stream.getvalue()
|
||||||
|
script_bytes = launcher + shebang + zip_data
|
||||||
|
for name in names:
|
||||||
|
outname = os.path.join(self.target_dir, name)
|
||||||
|
if use_launcher: # pragma: no cover
|
||||||
|
n, e = os.path.splitext(outname)
|
||||||
|
if e.startswith('.py'):
|
||||||
|
outname = n
|
||||||
|
outname = '%s.exe' % outname
|
||||||
|
try:
|
||||||
|
self._fileop.write_binary_file(outname, script_bytes)
|
||||||
|
except Exception:
|
||||||
|
# Failed writing an executable - it might be in use.
|
||||||
|
logger.warning('Failed to write executable - trying to '
|
||||||
|
'use .deleteme logic')
|
||||||
|
dfname = '%s.deleteme' % outname
|
||||||
|
if os.path.exists(dfname):
|
||||||
|
os.remove(dfname) # Not allowed to fail here
|
||||||
|
os.rename(outname, dfname) # nor here
|
||||||
|
self._fileop.write_binary_file(outname, script_bytes)
|
||||||
|
logger.debug('Able to replace executable using '
|
||||||
|
'.deleteme logic')
|
||||||
|
try:
|
||||||
|
os.remove(dfname)
|
||||||
|
except Exception:
|
||||||
|
pass # still in use - ignore error
|
||||||
|
else:
|
||||||
|
if self._is_nt and not outname.endswith('.' + ext): # pragma: no cover
|
||||||
|
outname = '%s.%s' % (outname, ext)
|
||||||
|
if os.path.exists(outname) and not self.clobber:
|
||||||
|
logger.warning('Skipping existing file %s', outname)
|
||||||
|
continue
|
||||||
|
self._fileop.write_binary_file(outname, script_bytes)
|
||||||
|
if self.set_mode:
|
||||||
|
self._fileop.set_executable_mode([outname])
|
||||||
|
filenames.append(outname)
|
||||||
|
|
||||||
|
variant_separator = '-'
|
||||||
|
|
||||||
|
def get_script_filenames(self, name):
|
||||||
|
result = set()
|
||||||
|
if '' in self.variants:
|
||||||
|
result.add(name)
|
||||||
|
if 'X' in self.variants:
|
||||||
|
result.add('%s%s' % (name, self.version_info[0]))
|
||||||
|
if 'X.Y' in self.variants:
|
||||||
|
result.add('%s%s%s.%s' % (name, self.variant_separator, self.version_info[0], self.version_info[1]))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _make_script(self, entry, filenames, options=None):
|
||||||
|
post_interp = b''
|
||||||
|
if options:
|
||||||
|
args = options.get('interpreter_args', [])
|
||||||
|
if args:
|
||||||
|
args = ' %s' % ' '.join(args)
|
||||||
|
post_interp = args.encode('utf-8')
|
||||||
|
shebang = self._get_shebang('utf-8', post_interp, options=options)
|
||||||
|
script = self._get_script_text(entry).encode('utf-8')
|
||||||
|
scriptnames = self.get_script_filenames(entry.name)
|
||||||
|
if options and options.get('gui', False):
|
||||||
|
ext = 'pyw'
|
||||||
|
else:
|
||||||
|
ext = 'py'
|
||||||
|
self._write_script(scriptnames, shebang, script, filenames, ext)
|
||||||
|
|
||||||
|
def _copy_script(self, script, filenames):
|
||||||
|
adjust = False
|
||||||
|
script = os.path.join(self.source_dir, convert_path(script))
|
||||||
|
outname = os.path.join(self.target_dir, os.path.basename(script))
|
||||||
|
if not self.force and not self._fileop.newer(script, outname):
|
||||||
|
logger.debug('not copying %s (up-to-date)', script)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Always open the file, but ignore failures in dry-run mode --
|
||||||
|
# that way, we'll get accurate feedback if we can read the
|
||||||
|
# script.
|
||||||
|
try:
|
||||||
|
f = open(script, 'rb')
|
||||||
|
except IOError: # pragma: no cover
|
||||||
|
if not self.dry_run:
|
||||||
|
raise
|
||||||
|
f = None
|
||||||
|
else:
|
||||||
|
first_line = f.readline()
|
||||||
|
if not first_line: # pragma: no cover
|
||||||
|
logger.warning('%s is an empty file (skipping)', script)
|
||||||
|
return
|
||||||
|
|
||||||
|
match = FIRST_LINE_RE.match(first_line.replace(b'\r\n', b'\n'))
|
||||||
|
if match:
|
||||||
|
adjust = True
|
||||||
|
post_interp = match.group(1) or b''
|
||||||
|
|
||||||
|
if not adjust:
|
||||||
|
if f:
|
||||||
|
f.close()
|
||||||
|
self._fileop.copy_file(script, outname)
|
||||||
|
if self.set_mode:
|
||||||
|
self._fileop.set_executable_mode([outname])
|
||||||
|
filenames.append(outname)
|
||||||
|
else:
|
||||||
|
logger.info('copying and adjusting %s -> %s', script, self.target_dir)
|
||||||
|
if not self._fileop.dry_run:
|
||||||
|
encoding, lines = detect_encoding(f.readline)
|
||||||
|
f.seek(0)
|
||||||
|
shebang = self._get_shebang(encoding, post_interp)
|
||||||
|
if b'pythonw' in first_line: # pragma: no cover
|
||||||
|
ext = 'pyw'
|
||||||
|
else:
|
||||||
|
ext = 'py'
|
||||||
|
n = os.path.basename(outname)
|
||||||
|
self._write_script([n], shebang, f.read(), filenames, ext)
|
||||||
|
if f:
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dry_run(self):
|
||||||
|
return self._fileop.dry_run
|
||||||
|
|
||||||
|
@dry_run.setter
|
||||||
|
def dry_run(self, value):
|
||||||
|
self._fileop.dry_run = value
|
||||||
|
|
||||||
|
if os.name == 'nt' or (os.name == 'java' and os._name == 'nt'): # pragma: no cover
|
||||||
|
# Executable launcher support.
|
||||||
|
# Launchers are from https://bitbucket.org/vinay.sajip/simple_launcher/
|
||||||
|
|
||||||
|
def _get_launcher(self, kind):
|
||||||
|
if struct.calcsize('P') == 8: # 64-bit
|
||||||
|
bits = '64'
|
||||||
|
else:
|
||||||
|
bits = '32'
|
||||||
|
platform_suffix = '-arm' if get_platform() == 'win-arm64' else ''
|
||||||
|
name = '%s%s%s.exe' % (kind, bits, platform_suffix)
|
||||||
|
if name not in WRAPPERS:
|
||||||
|
msg = ('Unable to find resource %s in package %s' %
|
||||||
|
(name, DISTLIB_PACKAGE))
|
||||||
|
raise ValueError(msg)
|
||||||
|
return WRAPPERS[name]
|
||||||
|
|
||||||
|
# Public API follows
|
||||||
|
|
||||||
|
def make(self, specification, options=None):
|
||||||
|
"""
|
||||||
|
Make a script.
|
||||||
|
|
||||||
|
:param specification: The specification, which is either a valid export
|
||||||
|
entry specification (to make a script from a
|
||||||
|
callable) or a filename (to make a script by
|
||||||
|
copying from a source location).
|
||||||
|
:param options: A dictionary of options controlling script generation.
|
||||||
|
:return: A list of all absolute pathnames written to.
|
||||||
|
"""
|
||||||
|
filenames = []
|
||||||
|
entry = get_export_entry(specification)
|
||||||
|
if entry is None:
|
||||||
|
self._copy_script(specification, filenames)
|
||||||
|
else:
|
||||||
|
self._make_script(entry, filenames, options=options)
|
||||||
|
return filenames
|
||||||
|
|
||||||
|
def make_multiple(self, specifications, options=None):
|
||||||
|
"""
|
||||||
|
Take a list of specifications and make scripts from them,
|
||||||
|
:param specifications: A list of specifications.
|
||||||
|
:return: A list of all absolute pathnames written to,
|
||||||
|
"""
|
||||||
|
filenames = []
|
||||||
|
for specification in specifications:
|
||||||
|
filenames.extend(self.make(specification, options))
|
||||||
|
return filenames
|
||||||
Reference in New Issue
Block a user