forked from github.com/pypiserver
Merge pull request #140 from dpkp/pep_503
PEP 503 Package Name Normalization and URL Redirects
This commit is contained in:
commit
0e3da62a5b
@ -188,9 +188,11 @@ def update():
|
||||
|
||||
|
||||
@app.route("/simple")
|
||||
@app.route("/simple/:prefix")
|
||||
@app.route('/packages')
|
||||
@auth("list")
|
||||
def simpleindex_redirect():
|
||||
return redirect(request.fullpath + "/")
|
||||
def pep_503_redirects(prefix=None):
|
||||
return redirect(request.fullpath + "/", 301)
|
||||
|
||||
|
||||
@app.route("/simple/")
|
||||
@ -213,13 +215,13 @@ def simpleindex():
|
||||
return template(tmpl, links=links)
|
||||
|
||||
|
||||
@app.route("/simple/:prefix")
|
||||
@app.route("/simple/:prefix/")
|
||||
@auth("list")
|
||||
def simple(prefix=""):
|
||||
fp = request.fullpath
|
||||
if not fp.endswith("/"):
|
||||
fp += "/"
|
||||
# PEP 503: require normalized prefix
|
||||
normalized = core.normalize_pkgname(prefix)
|
||||
if prefix != normalized:
|
||||
return redirect('/simple/{0}/'.format(normalized), 301)
|
||||
|
||||
files = sorted(core.find_packages(packages(), prefix=prefix),
|
||||
key=lambda x: (x.parsed_version, x.relfn))
|
||||
@ -228,6 +230,7 @@ def simple(prefix=""):
|
||||
return redirect("%s/%s/" % (config.fallback_url.rstrip("/"), prefix))
|
||||
return HTTPError(404)
|
||||
|
||||
fp = request.fullpath
|
||||
links = [(os.path.basename(f.relfn),
|
||||
urljoin(fp, "../../packages/%s#%s" % (f.relfn_unix,
|
||||
|
||||
@ -249,14 +252,10 @@ def simple(prefix=""):
|
||||
return template(tmpl, prefix=prefix, links=links)
|
||||
|
||||
|
||||
@app.route('/packages')
|
||||
@app.route('/packages/')
|
||||
@auth("list")
|
||||
def list_packages():
|
||||
fp = request.fullpath
|
||||
if not fp.endswith("/"):
|
||||
fp += "/"
|
||||
|
||||
files = sorted(core.find_packages(packages()),
|
||||
key=lambda x: (os.path.dirname(x.relfn),
|
||||
x.pkgname,
|
||||
|
@ -181,7 +181,8 @@ def guess_pkgname_and_version(path):
|
||||
|
||||
|
||||
def normalize_pkgname(name):
|
||||
return name.lower().replace("-", "_")
|
||||
"""Perform PEP 503 normalization"""
|
||||
return re.sub(r"[-_.]+", "-", name).lower()
|
||||
|
||||
|
||||
def is_allowed_path(path_part):
|
||||
@ -258,23 +259,11 @@ def find_packages(pkgs, prefix=""):
|
||||
|
||||
|
||||
def get_prefixes(pkgs):
|
||||
pkgnames = set()
|
||||
normalized_pkgnames = set()
|
||||
eggs = set()
|
||||
|
||||
for x in pkgs:
|
||||
if x.pkgname:
|
||||
if x.relfn.endswith(".egg"):
|
||||
eggs.add(x.pkgname)
|
||||
else:
|
||||
pkgnames.add(x.pkgname)
|
||||
normalized_pkgnames.add(x.pkgname_norm)
|
||||
|
||||
for x in eggs:
|
||||
if x not in normalized_pkgnames:
|
||||
pkgnames.add(x)
|
||||
|
||||
return pkgnames
|
||||
normalized_pkgnames.add(x.pkgname_norm)
|
||||
return normalized_pkgnames
|
||||
|
||||
|
||||
def exists(root, filename):
|
||||
|
@ -133,8 +133,15 @@ def test_root_remove_not_found_msg_antiXSS(testapp):
|
||||
resp.mustcontain("alert", "somehost.org", no="<alert>")
|
||||
|
||||
|
||||
def test_packages_empty(testapp):
|
||||
def test_packages_redirect(testapp):
|
||||
resp = testapp.get("/packages")
|
||||
assert resp.status_code >= 300
|
||||
assert resp.status_code < 400
|
||||
assert resp.location.endswith('/packages/')
|
||||
|
||||
|
||||
def test_packages_empty(testapp):
|
||||
resp = testapp.get("/packages/")
|
||||
assert len(resp.html("a")) == 0
|
||||
|
||||
|
||||
@ -165,6 +172,13 @@ def test_packages_list_no_dotfiles(root, testapp):
|
||||
assert "foo" not in resp
|
||||
|
||||
|
||||
def test_simple_redirect(testapp):
|
||||
resp = testapp.get("/simple")
|
||||
assert resp.status_code >= 300
|
||||
assert resp.status_code < 400
|
||||
assert resp.location.endswith('/simple/')
|
||||
|
||||
|
||||
def test_simple_list_no_dotfiles(root, testapp):
|
||||
root.join(".foo-1.0.zip").write("secret")
|
||||
resp = testapp.get("/simple/")
|
||||
@ -200,13 +214,34 @@ def test_simple_list_no_dotdir2(root, testapp):
|
||||
assert resp.html("a") == []
|
||||
|
||||
|
||||
def test_simple_name_redirect(testapp):
|
||||
resp = testapp.get("/simple/foobar")
|
||||
assert resp.status_code >= 300
|
||||
assert resp.status_code < 400
|
||||
assert resp.location.endswith('/simple/foobar/')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('package,normalized', [
|
||||
('FooBar', 'foobar'),
|
||||
('Foo.Bar', 'foo-bar'),
|
||||
('foo_bar', 'foo-bar'),
|
||||
('Foo-Bar', 'foo-bar'),
|
||||
('foo--_.bar', 'foo-bar'),
|
||||
])
|
||||
def test_simple_normalized_name_redirect(testapp, package, normalized):
|
||||
resp = testapp.get("/simple/{0}/".format(package))
|
||||
assert resp.status_code >= 300
|
||||
assert resp.status_code < 400
|
||||
assert resp.location.endswith('/simple/{0}/'.format(normalized))
|
||||
|
||||
|
||||
def test_simple_index(root, testapp):
|
||||
root.join("foobar-1.0.zip").write("")
|
||||
root.join("foobar-1.1.zip").write("")
|
||||
root.join("foobarbaz-1.1.zip").write("")
|
||||
root.join("foobar.baz-1.1.zip").write("")
|
||||
|
||||
resp = testapp.get("/simple/foobar")
|
||||
resp = testapp.get("/simple/foobar/")
|
||||
assert len(resp.html("a")) == 2
|
||||
|
||||
|
||||
@ -223,7 +258,7 @@ def test_simple_index_list(root, testapp):
|
||||
def test_simple_index_case(root, testapp):
|
||||
root.join("FooBar-1.0.zip").write("")
|
||||
root.join("FooBar-1.1.zip").write("")
|
||||
resp = testapp.get("/simple/foobar")
|
||||
resp = testapp.get("/simple/foobar/")
|
||||
assert len(resp.html("a")) == 2
|
||||
|
||||
|
||||
@ -234,23 +269,18 @@ def test_nonroot_root(testpriv):
|
||||
|
||||
def test_nonroot_simple_index(root, testpriv):
|
||||
root.join("foobar-1.0.zip").write("")
|
||||
|
||||
for path in ["/priv/simple/foobar",
|
||||
"/priv/simple/foobar/"]:
|
||||
resp = testpriv.get(path)
|
||||
links = resp.html("a")
|
||||
assert len(links) == 1
|
||||
assert links[0]["href"].startswith("/priv/packages/foobar-1.0.zip#")
|
||||
resp = testpriv.get("/priv/simple/foobar/")
|
||||
links = resp.html("a")
|
||||
assert len(links) == 1
|
||||
assert links[0]["href"].startswith("/priv/packages/foobar-1.0.zip#")
|
||||
|
||||
|
||||
def test_nonroot_simple_packages(root, testpriv):
|
||||
root.join("foobar-1.0.zip").write("123")
|
||||
for path in ["/priv/packages",
|
||||
"/priv/packages/"]:
|
||||
resp = testpriv.get(path)
|
||||
links = resp.html("a")
|
||||
assert len(links) == 1
|
||||
assert links[0]["href"].startswith("/priv/packages/foobar-1.0.zip#")
|
||||
resp = testpriv.get("/priv/packages/")
|
||||
links = resp.html("a")
|
||||
assert len(links) == 1
|
||||
assert links[0]["href"].startswith("/priv/packages/foobar-1.0.zip#")
|
||||
|
||||
|
||||
def test_root_no_relative_paths(testpriv):
|
||||
@ -276,14 +306,14 @@ def test_simple_index_list_name_with_underscore(root, testapp):
|
||||
resp = testapp.get("/simple/")
|
||||
assert len(resp.html("a")) == 1
|
||||
hrefs = [x["href"] for x in resp.html("a")]
|
||||
assert hrefs == ["foo_bar/"]
|
||||
assert hrefs == ["foo-bar/"]
|
||||
|
||||
|
||||
def test_simple_index_egg_and_tarball(root, testapp):
|
||||
root.join("foo-bar-1.0.tar.gz").write("")
|
||||
root.join("foo_bar-1.0-py2.7.egg").write("")
|
||||
|
||||
resp = testapp.get("/simple/foo-bar")
|
||||
resp = testapp.get("/simple/foo-bar/")
|
||||
assert len(resp.html("a")) == 2
|
||||
|
||||
|
||||
@ -292,9 +322,9 @@ def test_simple_index_list_name_with_underscore_no_egg(root, testapp):
|
||||
root.join("foo-bar-1.1.tar.gz").write("")
|
||||
|
||||
resp = testapp.get("/simple/")
|
||||
assert len(resp.html("a")) == 2
|
||||
assert len(resp.html("a")) == 1
|
||||
hrefs = set([x["href"] for x in resp.html("a")])
|
||||
assert hrefs == set(["foo_bar/", "foo-bar/"])
|
||||
assert hrefs == set(["foo-bar/"])
|
||||
|
||||
|
||||
def test_no_cache_control_set(root, _app, testapp):
|
||||
|
Loading…
Reference in New Issue
Block a user