forked from github.com/pypiserver
FIX #102: uploading pkgs with +!
chars in version.
+ Use `content.raw_filename` for allowing PEP0440 chars. + Add upload app-TCs. + Improve parse-pkg core-TC. + Update CHANGES on forthcomming release.
This commit is contained in:
parent
031f7a4289
commit
6b904db6c5
11
CHANGES.rst
11
CHANGES.rst
@ -1,6 +1,17 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
1.X.X (2016-01-xx)
|
||||
------------------
|
||||
- Package-versions parsing:
|
||||
|
||||
- TODO: #104: Stopped parsing invalid package-versions prefixed with `v`; they are
|
||||
invalid according to :pep-reference:`0440`.
|
||||
- TODO: Support versions with epochs separated by `!` like `package-1!1.1.0`.
|
||||
- #102: FIX regression on uploading packages with `+` char in their version
|
||||
caused by recent bottle-upgrade.
|
||||
|
||||
|
||||
1.1.9 (2015-12-21)
|
||||
------------------
|
||||
"Ssss-elections" bug-fix & maintenance release.
|
||||
|
@ -137,16 +137,17 @@ def update():
|
||||
except KeyError:
|
||||
raise HTTPError(400, "content file field not found")
|
||||
|
||||
if "/" in content.filename:
|
||||
if not core.is_valid_pkg_filename(content.raw_filename):
|
||||
raise HTTPError(400, "bad filename")
|
||||
|
||||
if not config.overwrite and core.exists(packages.root, content.filename):
|
||||
if not config.overwrite and core.exists(packages.root, content.raw_filename):
|
||||
log.warn("Cannot upload package(%s) since it already exists! \n" +
|
||||
" You may use `--overwrite` option when starting server to disable this check. ",
|
||||
content.filename)
|
||||
raise HTTPError(409, "file already exists")
|
||||
content.raw_filename)
|
||||
msg = "Package already exists! Use `--overwrite` option on server."
|
||||
raise HTTPError(409, msg)
|
||||
|
||||
core.store(packages.root, content.filename, content.save)
|
||||
core.store(packages.root, content.raw_filename, content.save)
|
||||
return ""
|
||||
|
||||
|
||||
|
@ -180,6 +180,11 @@ def is_allowed_path(path_part):
|
||||
p = path_part.replace("\\", "/")
|
||||
return not (p.startswith(".") or "/." in p)
|
||||
|
||||
_bottle_upload_filename_re = re.compile(r'^[a-z0-9_.!+-]+$', re.I)
|
||||
|
||||
def is_valid_pkg_filename(fname):
|
||||
return _bottle_upload_filename_re.match(fname) is not None
|
||||
|
||||
|
||||
class PkgFile(object):
|
||||
|
||||
@ -211,7 +216,7 @@ class PkgFile(object):
|
||||
if not hasattr(self, '_hash'):
|
||||
self._hash = '%s=%.32s' % (hash_algo, digest_file(self.fn, hash_algo))
|
||||
return self._hash
|
||||
|
||||
|
||||
|
||||
def _listdir(root):
|
||||
root = os.path.abspath(root)
|
||||
|
@ -4,12 +4,13 @@ import contextlib
|
||||
import glob
|
||||
import logging
|
||||
import os
|
||||
from pypiserver import __main__, bottle
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
import webtest
|
||||
|
||||
from pypiserver import __main__, bottle
|
||||
import test_core
|
||||
|
||||
|
||||
# Enable logging to detect any problems with it
|
||||
@ -305,3 +306,70 @@ def test_cache_control_set(root):
|
||||
resp = app_with_cache.get("/packages/foo_bar-1.0.tar.gz")
|
||||
assert "Cache-Control" in resp.headers
|
||||
assert resp.headers["Cache-Control"] == 'public, max-age=%s' % AGE
|
||||
|
||||
def test_upload_noAction(root, testapp):
|
||||
resp = testapp.post("/", expect_errors=1)
|
||||
assert resp.status == '400 Bad Request'
|
||||
assert ":action field not found" in resp.text
|
||||
|
||||
def test_upload_badAction(root, testapp):
|
||||
resp = testapp.post("/", params={':action': 'BAD'}, expect_errors=1)
|
||||
assert resp.status == '400 Bad Request'
|
||||
assert "action not supported: BAD" in resp.text
|
||||
|
||||
@pytest.mark.parametrize(("package"), [f[0] for f in test_core.files if f[1]])
|
||||
def test_upload(package, root, testapp):
|
||||
resp = testapp.post("/", params={':action': 'file_upload'},
|
||||
upload_files=[('content', package, b'')])
|
||||
assert resp.status_int == 200
|
||||
uploaded_pkgs = [f.basename for f in root.listdir()]
|
||||
assert len(uploaded_pkgs) == 1
|
||||
assert uploaded_pkgs[0].lower() == package.lower()
|
||||
|
||||
@pytest.mark.parametrize(("package"), [
|
||||
f[0] for f in test_core.files
|
||||
if f[1] is None])
|
||||
def test_upload_badFilename(package, root, testapp):
|
||||
resp = testapp.post("/", params={':action': 'file_upload'},
|
||||
upload_files=[('content', package, b'')],
|
||||
expect_errors=1)
|
||||
assert resp.status == '400 Bad Request'
|
||||
assert "bad filename" in resp.text
|
||||
|
||||
def test_remove_pkg_missingNaveVersion(root, testapp):
|
||||
resp = testapp.post("/", expect_errors=1,
|
||||
params={
|
||||
':action': 'remove_pkg',
|
||||
'version': '',
|
||||
})
|
||||
assert resp.status == '400 Bad Request'
|
||||
assert "Name or version not specified" in resp.text
|
||||
|
||||
resp = testapp.post("/", expect_errors=1,
|
||||
params={
|
||||
':action': 'remove_pkg',
|
||||
'name': '',
|
||||
})
|
||||
assert resp.status == '400 Bad Request'
|
||||
assert "Name or version not specified" in resp.text
|
||||
|
||||
resp = testapp.post("/", expect_errors=1,
|
||||
params={
|
||||
':action': 'remove_pkg',
|
||||
})
|
||||
assert resp.status == '400 Bad Request'
|
||||
assert "Name or version not specified" in resp.text
|
||||
|
||||
def test_remove_pkg_notFound(root, testapp):
|
||||
resp = testapp.post("/", expect_errors=1,
|
||||
params={
|
||||
':action': 'remove_pkg',
|
||||
'name': 'foo',
|
||||
'version': '123',
|
||||
})
|
||||
assert resp.status == '404 Not Found'
|
||||
assert "foo (123) not found" in resp.text
|
||||
|
||||
@pytest.mark.xfail()
|
||||
def test_remove_pkg(root, testapp):
|
||||
assert 0
|
14
tests/test_core.py
Executable file → Normal file
14
tests/test_core.py
Executable file → Normal file
@ -1,4 +1,5 @@
|
||||
#! /usr/bin/env py.test
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import pytest
|
||||
from pypiserver import __main__, core
|
||||
@ -13,6 +14,7 @@ files = [
|
||||
("pytz-2012b.tar.bz2", "pytz", "2012b"),
|
||||
("pytz-2012b.tgz", "pytz", "2012b"),
|
||||
("pytz-2012b.ZIP", "pytz", "2012b"),
|
||||
("pytz-2012a.zip", "pytz", "2012a"),
|
||||
("gevent-1.0b1.win32-py2.6.exe", "gevent", "1.0b1"),
|
||||
("gevent-1.0b1.win32-py2.7.msi", "gevent", "1.0b1"),
|
||||
("greenlet-0.3.4-py3.1-win-amd64.egg", "greenlet", "0.3.4"),
|
||||
@ -20,7 +22,6 @@ files = [
|
||||
("greenlet-0.3.4-py3.2-win32.egg", "greenlet", "0.3.4"),
|
||||
("greenlet-0.3.4-py2.7-linux-x86_64.egg", "greenlet", "0.3.4"),
|
||||
("pep8-0.6.0.zip", "pep8", "0.6.0"),
|
||||
("pytz-2012b.zip", "pytz", "2012b"),
|
||||
("ABC12-34_V1X-1.2.3.zip", "ABC12-34_V1X", "1.2.3"),
|
||||
("A100-200-XYZ-1.2.3.zip", "A100-200-XYZ", "1.2.3"),
|
||||
("flup-1.0.3.dev-20110405.tar.gz", "flup", "1.0.3.dev-20110405"),
|
||||
@ -43,12 +44,21 @@ files = [
|
||||
("package-name-0.0.1.dev0.linux-x86_64.tar.gz", "package-name", "0.0.1.dev0"),
|
||||
("package-name-0.0.1.dev0.macosx-10.10-intel.tar.gz", "package-name", "0.0.1.dev0"),
|
||||
("package-name-0.0.1.alpha.1.win-amd64-py3.2.exe", "package-name", "0.0.1.alpha.1"),
|
||||
("pkg-3!1.0-0.1.tgz", 'pkg-3!1.0', '0.1'), # TO BE FIXED
|
||||
("pkg-3!1+.0-0.1.tgz", 'pkg-3!1+.0', '0.1'), # TO BE FIXED
|
||||
|
||||
("a-γρεεκ-package-1.0", None, None),
|
||||
("some/pkg-1.0", None, None),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("filename", "pkgname", "version"), files)
|
||||
def test_guess_pkgname_and_version(filename, pkgname, version):
|
||||
assert core.guess_pkgname_and_version(filename) == (pkgname, version)
|
||||
if pkgname is None:
|
||||
exp = None
|
||||
else:
|
||||
exp = (pkgname, version)
|
||||
assert core.guess_pkgname_and_version(filename) == exp
|
||||
|
||||
|
||||
def test_listdir_bad_name(tmpdir):
|
||||
|
Loading…
Reference in New Issue
Block a user