Rework standalone generation using ZIP.

+ Standalone executable based on wheel.
+ Properly use `pkg_resources` so reading `welcome-msg` file
  works even from within zip.
  + Mark `zip_safe=True` in setup.py.
+ Delete forgotten distutils startup script.
+ Build standalone before installing anything else,
  to check if any deps are missing.
+ Restore py25 in Travis.
This commit is contained in:
ankostis on tokoti 2015-09-16 23:54:41 +02:00
parent 542ffad6b0
commit 133afe28f7
15 changed files with 77 additions and 206 deletions

View File

@ -2,6 +2,7 @@ sudo: false
language: python
python: 2.7
env:
- TOX_ENV=py25
- TOX_ENV=py26
- TOX_ENV=py27
- TOX_ENV=py32
@ -9,12 +10,11 @@ env:
- TOX_ENV=py34
- TOX_ENV=pypy
install:
- pip install tox
script:
- tox -e $TOX_ENV
- python -m pip install setuptools pip -U
- ./bin/test_standalone.sh
- pip install tox
- tox -e $TOX_ENV
branches:
except:

View File

@ -10,7 +10,7 @@ pypiserver - minimal PyPI server for use with pip/easy_install
:Version: 1.1.9-dev.0
:Date: 2015-03-8
:Source: https://github.com/pypiserver/pypiserver
:PyPI: https://pypi.python.org/pypi/pypiserver#downloads
:PyPI: https://pypi.python.org/pypi/pypiserver
:Travis: https://travis-ci.org/pypiserver/pypiserver
:License: zlib/libpng + MIT
@ -308,7 +308,7 @@ Running ``pypi-server -h`` will print a detailed usage message::
-u
allow updating to unstable version (alpha, beta, rc, dev versions)
Visit https://pypi.python.org/pypi/pypiserver for more information.
Visit https://github.com/pypiserver/pypiserver for more information.
@ -448,11 +448,6 @@ unstable packages on different paths::
Sources
=======
Python-packages with source releases can be downloaded from
https://pypi.python.org/pypi/pypiserver
The in-development sources are hosted at https://github.com/pypiserver/pypiserver.
Use::
git clone https://github.com/pypiserver/pypiserver.git

View File

@ -1,7 +1,7 @@
#! /bin/sh
## Requires this script in your PATH:
## https://github.com/git/git/blob/master/contrib/workdir/git-new-workdir
##
## Create an executable file and add it into `standalone` branch
##
## Invoke it with any arg to avoid committing into `standalone` branch.
my_dir="$(dirname "$0")"
@ -18,13 +18,12 @@ fi
gitversion=$(git describe --tags)
rm -rf .standalone
if nwd_dump=$( "$git_wdir" . .standalone standalone 2>&1 ); then
./bin/gen-standalone.py
chmod a+xr ./pypi-server-standalone.py
./bin/gen-standalone.sh
cp -p pypi-server-standalone.py .standalone
cd .standalone
if [ $# -lt 1 ]; then
git add .
git commit -m "add pypi-server-standalone $gitversion"
git commit -m "Add pypi-server-standalone $gitversion"
fi
else
echo "git-new-workdir: failed due to: $nwd_dump"

View File

@ -1,66 +0,0 @@
#! /usr/bin/env python
"""generate a single file pypi-server script"""
from __future__ import unicode_literals
import os, zlib, base64, itertools
try:
import cPickle
except ImportError:
import pickle as cPickle
def find_files(path):
for dirpath, dirnames, filenames in os.walk(path):
for f in filenames:
yield os.path.join(dirpath, f)
def myExecFile(file, g, l):
try:
execfile(file, g, l)
except NameError:
with open(file) as fd:
txt = fd.read()
exec(txt, g, l)
def get_version():
d = {}
try:
myExecFile("pypiserver/__init__.py", d, d)
except (ImportError, RuntimeError):
pass
return d["__version__"]
def main():
name2src = {}
for f in itertools.chain(find_files("pypiserver"),
find_files("vendor")):
if not f.endswith(".py"):
continue
k = f.replace('/', '.')[:-3]
if k.startswith("vendor."):
k = k[len("vendor."):]
name2src[k] = open(f).read()
data = cPickle.dumps(name2src, 2)
data = zlib.compress(data, 9)
data = base64.encodestring(data)
try:
data = str(data, encoding='ascii')
except TypeError: # we were in PY2
data = '%s' % data
script = open("pypi-server-in.py").read()
script = script.replace("@VERSION@", get_version())
script = script.replace('@SOURCES@', data)
dst = "pypi-server-standalone.py"
open(dst, "wt").write(script)
os.chmod(dst, 755)
print("created %s"%dst)
if __name__ == '__main__':
main()

36
bin/gen-standalone.sh Executable file
View File

@ -0,0 +1,36 @@
#! /bin/sh
##
## Create an executable zip file.
exec_zip="./pypi-server-standalone.py"
my_dir="$(dirname "$0")"
cd $my_dir/..
rm -rf ./build/* ./dist/*
python setup.py bdist_wheel
wheel="./dist/pypiserver-*.whl"
## Modify `wheel` archive with `__main__.py` at root,
# prepend it with a python-flashbang, and
# add header-comments >= 10-lines so that
# ``head pypiserver*.py`` behaves politely.
#
gitversion=$(git describe --tags)
unzip -jo $wheel pypiserver/__main__.py -d ./dist
zip -d $wheel pypiserver/__main__.py
zip -mj $wheel ./dist/__main__.py
cat - $wheel > "$exec_zip" << EOF
#!/usr/bin/env python
##
## Standalone pypiserver-$gitversion
##
## Execute it like that:
## $exec_zip <packages_dir>
## To get more help, type:
## $exec_zip --help
##
## BINARY CONTENT FOLLOWS
EOF
chmod a+xr "$exec_zip"

View File

@ -1,9 +1,11 @@
#! /bin/sh
## Test standalone generation & execution.
##
my_dir="$(dirname "$0")"
cd $my_dir/..
git fetch origin standalone:origin/standalone
git fetch origin standalone:origin/standalone
git branch --track standalone origin/standalone
./bin/commit-standalone.sh no_commit
@ -11,4 +13,4 @@ git branch --track standalone origin/standalone
server_pid=$!
sleep 2
kill $server_pid # Killing fails if server failed starting-up.
kill $server_pid && echo "Server killed nicely." # Kill fails if server failed.

View File

@ -1,5 +0,0 @@
#! /usr/bin/env python
## Failback script if installed with `distutils`.
if __name__ == "__main__":
from pypiserver.core import main
main()

View File

@ -1,66 +0,0 @@
#! /usr/bin/env python
"""standalone pypi-server, version @VERSION@"""
sources = """
@SOURCES@"""
import sys, base64, zlib
class DictImporter(object):
sources = None
def find_module(self, fullname, path=None):
if fullname in self.sources:
return self
if fullname + '.__init__' in self.sources:
return self
return None
def load_module(self, fullname):
try:
s = self.sources[fullname]
is_pkg = False
except KeyError:
s = self.sources[fullname + '.__init__']
is_pkg = True
co = compile(s, fullname, 'exec')
try:
module = sys.modules.setdefault(fullname, type(sys)(fullname))
except TypeError: # jython?
import types
module = sys.modules.setdefault(fullname, types.ModuleType(fullname))
module.__file__ = __file__
module.__loader__ = self
if is_pkg:
module.__path__ = [fullname]
do_exec(co, module.__dict__)
return sys.modules[fullname]
def get_source(self, name):
res = self.sources.get(name)
if res is None:
res = self.sources.get(name + '.__init__')
return res
if sys.version_info >= (3, 0):
import pickle
exec("def do_exec(co, loc): exec(co, loc)\n")
sources = sources.encode("ascii") # ensure bytes
d = zlib.decompress(base64.decodebytes(sources))
sources = pickle.loads(zlib.decompress(base64.decodebytes(sources)), encoding="utf-8")
else:
import cPickle as pickle
exec("def do_exec(co, loc): exec co in loc\n")
sources = pickle.loads(zlib.decompress(base64.decodestring(sources)))
importer = DictImporter()
importer.sources = sources
sys.meta_path.append(importer)
if __name__ == "__main__":
from pypiserver import __main__
__main__.main()

View File

@ -21,7 +21,7 @@ def init_logging(level=None, frmt=None, filename=None):
def usage():
sys.stdout.write("""pypi-server [OPTIONS] [PACKAGES_DIRECTORY...]
return """pypi-server [OPTIONS] [PACKAGES_DIRECTORY...]
start PyPI compatible package server serving packages from
PACKAGES_DIRECTORY. If PACKAGES_DIRECTORY is not given on the
command line, it uses the default ~/packages. pypiserver scans this
@ -130,7 +130,7 @@ The following additional options can be specified with -U:
allow updating to unstable version (alpha, beta, rc, dev versions)
Visit https://pypi.python.org/pypi/pypiserver for more information.
""")
"""
def main(argv=None):
@ -241,7 +241,7 @@ def main(argv=None):
elif k == "-v":
verbosity += 1
elif k in ("-h", "--help"):
usage()
print(usage())
sys.exit(0)
if password_file and password_file != '.' and not authenticated:

View File

@ -126,37 +126,16 @@ def configure(root=None,
#
try:
if not welcome_file:
welcome_file = pkg_resources.resource_filename( # @UndefinedVariable
__name__, "welcome.html") # @UndefinedVariable
config.welcome_file = welcome_file
with io.open(config.welcome_file, 'r', encoding='utf-8') as fd:
config.welcome_msg = fd.read()
config.welcome_file = "welcome.html"
config.welcome_msg = pkg_resources.resource_string( # @UndefinedVariable
__name__, "welcome.html").decode("utf-8") # @UndefinedVariable
else:
config.welcome_file = welcome_file
with io.open(welcome_file, 'r', encoding='utf-8') as fd:
config.welcome_msg = fd.read()
except Exception:
log.warning(
"Could not load welcome-file(%s)!", welcome_file, exc_info=1)
if not config.welcome_msg:
from textwrap import dedent
config.welcome_msg = dedent("""\
<html><head><title>Welcome to pypiserver!</title></head><body>
<h1>Welcome to pypiserver!</h1>
<p>This is a PyPI compatible package index serving {{NUMPKGS}} packages.</p>
<p> To use this server with pip, run the the following command:
<blockquote><pre>
pip install -i {{URL}}simple/ PACKAGE [PACKAGE2...]
</pre></blockquote></p>
<p> To use this server with easy_install, run the the following command:
<blockquote><pre>
easy_install -i {{URL}}simple/ PACKAGE
</pre></blockquote></p>
<p>The complete list of all packages can be found <a href="{{PACKAGES}}">here</a> or via the <a href="{{SIMPLE}}">simple</a> index.</p>
<p>This instance is running version {{VERSION}} of the <a href="http://pypi.python.org/pypi/pypiserver">pypiserver</a> software.</p>
</body></html>\
""")
config.log_req_frmt = log_req_frmt
config.log_res_frmt = log_res_frmt
config.log_err_frmt = log_err_frmt

View File

@ -48,18 +48,16 @@ setup(name="pypiserver",
"Operating System :: POSIX",
"Operating System :: OS Independent",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.5",
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.0",
"Programming Language :: Python :: 3.1",
"Programming Language :: Python :: 3.2",
"Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Topic :: Software Development :: Build Tools",
"Topic :: System :: Software Distribution"],
zip_safe=False,
zip_safe=True,
entry_points={
'paste.app_factory': ['main=pypiserver:paste_app_factory'],
'console_scripts': ['pypi-server=pypiserver.__main__:main']

View File

@ -1,11 +1,11 @@
#! /usr/bin/env py.test
from pypiserver import __main__, bottle # do no remove. needed for bottle
from pypiserver import __main__, bottle
import pytest, webtest
import logging
## Enable logging to detect any problems with it
##
import logging
__main__.init_logging(level=logging.NOTSET)
@pytest.fixture()
@ -42,17 +42,17 @@ def testpriv(priv):
@pytest.fixture(params=[" ", ## Mustcontain test below fails when string is empty.
"Hey there!",
"Hey there!",
"<html><body>Hey there!</body></html>",
])
def welcome_file_no_vars(request, root):
wfile = root.join("testwelcome.html")
wfile.write(request.param)
return wfile
@pytest.fixture()
@pytest.fixture()
def welcome_file_all_vars(request, root):
msg ="""
{{URL}}
@ -63,7 +63,7 @@ def welcome_file_all_vars(request, root):
"""
wfile = root.join("testwelcome.html")
wfile.write(msg)
return wfile
@ -95,7 +95,7 @@ def test_root_welcome_msg_all_vars(root, welcome_file_all_vars):
app = app(root=root.strpath, welcome_file=welcome_file_all_vars.strpath)
testapp = webtest.TestApp(app)
resp = testapp.get("/")
from pypiserver import __version__ as pver
resp.mustcontain(pver)

View File

@ -1,11 +1,11 @@
#! /usr/bin/env py.test
import pytest
from pypiserver import core, __main__
from pypiserver import __main__, core
import logging
## Enable logging to detect any problems with it
##
import logging
__main__.init_logging(level=logging.NOTSET)

View File

@ -1,7 +1,7 @@
#! /usr/bin/env py.test
import sys, os, pytest, logging
from pypiserver import core, __main__
from pypiserver import __main__, core
try:
from unittest import mock
except ImportError:
@ -96,14 +96,14 @@ def logfile(tmpdir):
def test_logging(main, logfile):
main(["-v", "--log-file", logfile.strpath])
assert logfile.check(), logfile
def test_logging_verbosity(main):
main([])
assert logging.getLogger().level == logging.WARN
assert logging.getLogger().level == logging.WARN
main(["-v"])
assert logging.getLogger().level == logging.INFO
assert logging.getLogger().level == logging.INFO
main(["-v", "-v"])
assert logging.getLogger().level == logging.DEBUG
assert logging.getLogger().level == logging.DEBUG
main(["-v", "-v", "-v"])
assert logging.getLogger().level == logging.NOTSET

1
vendor

@ -1 +0,0 @@
Subproject commit f1b8d33f3ee3279ac4d8a27c921ebd3a122ec594