Merge branch 'master' of git://github.com/ask/chishop

This commit is contained in:
Rune Halvorsen
2009-05-06 00:24:34 +02:00
7 changed files with 142 additions and 108 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
Ask Solem <askh@opera.com>
Rune Halvorsen <runeh@opera.com>
Russel Sim <russel.sim@jcu.edu.au>
Russell Sim <russell.sim@gmail.com>
+44 -3
View File
@@ -15,6 +15,8 @@ First you have to install the dependencies::
Initial configuration
---------------------
::
$ cd chipshop/
$ $EDITOR settings.py
@@ -23,6 +25,7 @@ Initial configuration
Run the PyPI server
-------------------
::
$ python manage.py runserver
@@ -30,8 +33,46 @@ Run the PyPI server
Please note that ``chishop/media/dists`` has to be writable by the
user the web-server is running as.
Contact Information
====================
askh@opera.com
Using Setuptools
================
Add the following to your ``~/.pypirc`` file::
[distutils]
index-servers =
pypi
local
[pypi]
username:user
password:secret
[local]
username:user
password:secret
repository:http://localhost:8000
Uploading a package: Python >=2.6
--------------------------------------------
To push the package to the local pypi::
$ python setup.py register sdist upload -r local
Uploading a package: Python <2.6
-------------------------------------------
If you don't have Python 2.6 please run the command below to install the backport of the extension::
$ easy_install -U collective.dist
instead of using register and dist command, you can use "mregister" and "mupload", that are a backport of python 2.6 register and upload commands, that supports multiple servers.
To push the package to the local pypi::
$ python setup.py mregister sdist mupload -r local
.. # vim: syntax=rst expandtab tabstop=4 shiftwidth=4 shiftround
+4
View File
@@ -7,3 +7,7 @@
* Maybe add a permission "can upload new release", so more than one
user can change the same project.
* Should a project have co-owners?
- One possible solution:
http://github.com/initcrash/django-object-permissions/tree
* Script to populate classifiers from
http://pypi.python.org/pypi?%3Aaction=list_classifiers
+1
View File
@@ -11,6 +11,7 @@ ADMINS = (
# The default on PyPI is to not allow this, but it can be real handy
# if you're sloppy.
DJANGOPYPI_ALLOW_VERSION_OVERWRITE = False
DJANGOPYPI_RELEASE_UPLOAD_TO = 'dists'
MANAGERS = ADMINS
+8 -77
View File
@@ -37,83 +37,14 @@ from djangopypi.models import Project, Classifier, Release
from django.utils.translation import ugettext_lazy as _
class PermissionDeniedError(Exception):
"""The user did not have the privileges to execute an action."""
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
exclude = ['owner', 'classifiers']
class AlreadyExistsError(Exception):
"""Filename already exists."""
class ReleaseForm(forms.ModelForm):
class Meta:
model = Release
exclude = ['project']
ALREADY_EXISTS_FMT = _("""A file named "%s" already exists for %s. To fix """
+ "problems with that you should create a new release.")
class ProjectRegisterForm(forms.Form):
name = forms.CharField()
license = forms.CharField(required=False)
metadata_version = forms.CharField(initial="1.0")
author = forms.CharField(required=False)
home_page = forms.CharField(required=False)
download_url = forms.CharField(required=False)
summary = forms.CharField(required=False)
description = forms.CharField(required=False)
author_email = forms.CharField(required=False)
version = forms.CharField()
platform = forms.CharField(required=False)
PermissionDeniedError = PermissionDeniedError
AlreadyExistsError = AlreadyExistsError
def save(self, classifiers, user, file=None):
values = dict(self.cleaned_data)
name = values["name"]
version = values.pop("version")
platform = values.pop("platform", "UNKNOWN")
values["owner"] = user
try:
project = Project.objects.get(name=name)
except Project.DoesNotExist:
project = Project.objects.create(**values)
else:
# If the project already exists,
# be sure that the current user owns this object.
if project.owner != user:
raise self.PermissionDeniedError(
"%s doesn't own that project." % user.username)
[setattr(project, field_name, field_value)
for field_name, field_value in values.items()]
project.save()
for classifier in classifiers:
project.classifiers.add(
Classifier.objects.get_or_create(name=classifier)[0])
# If the old file already exists, django will append a _ after the
# filename, however with .tar.gz files django does the "wrong" thing
# and saves it as project-0.1.2.tar_.gz. So remove it before
# django sees anything.
allow_overwrite = getattr(settings,
"DJANGOPYPI_ALLOW_VERSION_OVERWRITE", False)
if file:
try:
release = Release.objects.get(version=version,
platform=platform, project=project)
if os.path.exists(release.distribution.path):
if not allow_overwrite:
raise self.AlreadyExistsError(ALREADY_EXISTS_FMT % (
release.filename, release))
os.remove(release.distribution.path)
release.delete()
except (Release.DoesNotExist, ValueError):
pass
release, created = Release.objects.get_or_create(version=version,
platform=platform,
project=project)
if file:
release.distribution.save(file.name, file, save=True)
release.save()
+5 -2
View File
@@ -31,6 +31,7 @@ POSSIBILITY OF SUCH DAMAGE.
"""
import os
from django.conf import settings
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User
@@ -63,6 +64,8 @@ ARCHITECTURES = (
("ultrasparc", "UltraSparc"),
)
UPLOAD_TO = getattr(settings,
"DJANGOPYPI_RELEASE_UPLOAD_TO", 'dist')
class Classifier(models.Model):
name = models.CharField(max_length=255, unique=True)
@@ -77,7 +80,7 @@ class Classifier(models.Model):
class Project(models.Model):
name = models.CharField(max_length=255, unique=True)
license = models.CharField(max_length=255, blank=True)
license = models.TextField(blank=True)
metadata_version = models.CharField(max_length=64, default=1.0)
author = models.CharField(max_length=128, blank=True)
home_page = models.URLField(verify_exists=False, blank=True, null=True)
@@ -113,7 +116,7 @@ class Project(models.Model):
class Release(models.Model):
version = models.CharField(max_length=128)
distribution = models.FileField(upload_to="dists")
distribution = models.FileField(upload_to=UPLOAD_TO)
md5_digest = models.CharField(max_length=255, blank=True)
platform = models.CharField(max_length=255, blank=True)
signature = models.CharField(max_length=128, blank=True)
+79 -25
View File
@@ -30,19 +30,27 @@ POSSIBILITY OF SUCH DAMAGE.
"""
import os
from django.conf import settings
from django.http import Http404, HttpResponse, HttpResponseBadRequest
from django.http import QueryDict, HttpResponseForbidden
from django.shortcuts import render_to_response
from djangopypi.models import Project
from djangopypi.forms import ProjectRegisterForm
from djangopypi.models import Project, Classifier, Release, UPLOAD_TO
from djangopypi.forms import ProjectForm, ReleaseForm
from django.template import RequestContext
from django.utils.datastructures import MultiValueDict
from django.utils.translation import ugettext_lazy as _
from django.core.files.uploadedfile import SimpleUploadedFile
from django.contrib.auth import authenticate, login
from djangopypi.http import HttpResponseNotImplemented
from djangopypi.http import HttpResponseUnauthorized
ALREADY_EXISTS_FMT = _("""A file named "%s" already exists for %s. To fix """
+ "problems with that you should create a new release.")
def parse_weird_post_data(raw_post_data):
""" For some reason Django can't parse the HTTP POST data
sent by ``distutils`` register/upload commands.
@@ -72,18 +80,18 @@ def parse_weird_post_data(raw_post_data):
if "filename" in headers:
file = SimpleUploadedFile(headers["filename"], content,
content_type="application/gzip")
files[headers["name"]] = file
files["distribution"] = [file]
elif headers["name"] in post_data:
post_data[headers["name"]].append(content)
else:
# Distutils sends UNKNOWN for empty fields (e.g platform)
# [russel.sim@jcu.edu.au]
# [russell.sim@gmail.com]
if content == 'UNKNOWN':
post_data[headers["name"]] = [None]
else:
post_data[headers["name"]] = [content]
return MultiValueDict(post_data), files
return MultiValueDict(post_data), MultiValueDict(files)
def login_basic_auth(request):
@@ -98,30 +106,76 @@ def login_basic_auth(request):
return authenticate(username=username, password=password)
def submit_project_or_release(user, post_data, files):
"""Registers/updates a project or release"""
try:
project = Project.objects.get(name=post_data['name'])
if project.owner != user:
return HttpResponseForbidden(
"That project is owned by someone else!")
except Project.DoesNotExist:
project = None
project_form = ProjectForm(post_data, instance=project)
if project_form.is_valid():
project = project_form.save(commit=False)
project.owner = user
project.save()
for c in post_data.getlist('classifiers'):
classifier, created = Classifier.objects.get_or_create(name=c)
project.classifiers.add(classifier)
if files:
allow_overwrite = getattr(settings,
"DJANGOPYPI_ALLOW_VERSION_OVERWRITE", False)
try:
release = Release.objects.get(version=post_data['version'],
project=project,
distribution=UPLOAD_TO + '/' +
files['distribution']._name)
if not allow_overwrite:
return HttpResponseForbidden(ALREADY_EXISTS_FMT % (
release.filename, release))
except Release.DoesNotExist:
release = None
# If the old file already exists, django will append a _ after the
# filename, however with .tar.gz files django does the "wrong"
# thing and saves it as project-0.1.2.tar_.gz. So remove it before
# django sees anything.
release_form = ReleaseForm(post_data, files, instance=release)
if release_form.is_valid():
if release and os.path.exists(release.distribution.path):
os.remove(release.distribution.path)
release = release_form.save(commit=False)
release.project = project
release.save()
else:
return HttpResponseBadRequest(
"ERRORS: %s" % release_form.errors)
else:
return HttpResponseBadRequest("ERRORS: %s" % project_form.errors)
return HttpResponse()
def simple(request, template_name="djangopypi/simple.html"):
if request.method == "POST":
user = login_basic_auth(request)
if not user:
return HttpResponseUnauthorized('PyPI')
login(request, user)
if not request.user.is_authenticated():
return HttpResponseForbidden(
"Not logged in, or invalid username/password.")
post_data, files = parse_weird_post_data(request.raw_post_data)
action = post_data.get(":action")
classifiers = post_data.getlist("classifiers")
register_form = ProjectRegisterForm(post_data.copy())
if register_form.is_valid():
try:
register_form.save(classifiers, request.user,
file=files.get("content"))
except register_form.PermissionDeniedError, e:
return HttpResonseForbidden(
"That project is owned by someone else!")
except register_form.AlreadyExistsError, e:
return HttpResponseForbidden(e)
return HttpResponse("Successfully registered.")
return HttpResponse("ERRORS: %s" % register_form.errors)
if action == 'file_upload':
user = login_basic_auth(request)
if not user:
return HttpResponseUnauthorized('PyPI')
login(request, user)
if not request.user.is_authenticated():
return HttpResponseForbidden(
"Not logged in, or invalid username/password.")
return submit_project_or_release(user, post_data, files)
return HttpResponseNotImplemented(
"The :action %s is not implemented" % action)
dists = Project.objects.all().order_by("name")
context = RequestContext(request, {