Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
steiza committed Jun 10, 2011
0 parents commit 8b57e9c
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 0 deletions.
32 changes: 32 additions & 0 deletions README
@@ -0,0 +1,32 @@
This is a really, really, simple HTTP PyPI-like server.

It is intended to be used for companies or organizations that need a private
PyPi.

It literally supports four functions:

- Allows uploading of packages
- Downloading by package (or package and version)
- A / page that is navigatable with a web browser
- /pypi/ listing

It does not support:

- Stopping you from overwriting a package with the same name / version
- Registering packages
- Any sort of ACLs

To use it run "simplepypi". You can upload packages by:

- Adding this to your ~/.pypirc

[local]
username: <whatever>
password: <doesn't matter, see above>
repository: http://127.0.0.1:8000

- Running this on the setup.py of your favorite package:

python setup.py sdist upload -r local

And that's it!
137 changes: 137 additions & 0 deletions bin/simplepypi
@@ -0,0 +1,137 @@
#!/usr/bin/env python

import os.path

from twisted.internet import reactor
import twisted.python.log
from twisted.web.server import Site
from twisted.web.static import File

import txroutes


def return404(request):
request.setResponseCode(404)
return '<html><head><title>404 Not Found</title></head><body><h1>Not found</h1></body></html>'

def get_package_path(package, version=None, want_file=False):
path = os.path.join(os.path.dirname(__file__), 'packages', package)

if version:
path = os.path.join(path, version)

if want_file:
path = os.path.join(path, '%s-%s.tar.gz' % (package, version))

return path


class Controller(object):

def get_index(self, request):
return '<html><head><title>simplepypi</title></head><body><a href="/packages">packages</a></body></html>'

def post_index(self, request):
name = request.args.get('name', [None])[0]
version = request.args.get('version', [None])[0]
action = request.args.get(':action', [None])[0]
content = request.args.get('content', [None])[0]

if name is not None and version is not None and content is not None \
and action == 'file_upload':

name = name.lower()

path = get_package_path(name, version, want_file=True)

if not os.path.exists(os.path.dirname(path)):
os.makedirs(os.path.dirname(path))

fd = open(path, 'wb')
fd.write(content)
fd.close()

return ''

def get_pypi(self, request):
body = '<html><body>\n'

packages_path = os.path.join(os.path.dirname(__file__), 'packages')
packages_list = os.listdir(packages_path)

for each in packages_list:
versions_path = os.path.join(packages_path, each)
versions_list = os.listdir(versions_path)
versions_list.sort()

if len(versions_list) > 0:
body += '<a href="/packages/%(package)s/%(version)s/%(package)s-%(version)s.tar.gz">%(package)s-%(version)s</a>' % {'package': each, 'version': versions_list[-1]}

body += '</body></html>'

return body

def package(self, request, package):
package = package.lower()

package_path = get_package_path(package)

if not os.path.exists(package_path):
return return404(request)

versions = os.listdir(package_path)
versions.sort()

if len(versions) == 0:
return return404(request)

if os.path.exists(package_path):
return '<html><body><a href="/packages/%(package)s/%(version)s/%(package)s-%(version)s.tar.gz">%(package)s-%(version)s.tar.gz</a></body></html>' % {'package': package, 'version': versions[-1]}

else:
return return404(request)

def package_and_version(self, request, package, version):
package = package.lower()

path = get_package_path(package, version, want_file=True)

if os.path.exists(path):
return '<html><body><a href="/packages/%(package)s/%(version)s/%(package)s-%(version)s.tar.gz">%(package)s-%(version)s.tar.gz</a></body></html>' % {'package': package, 'version': version}

else:
return return404(request)

def set_up_server(port):
observer = twisted.python.log.PythonLoggingObserver()
observer.start()

dispatcher = txroutes.Dispatcher()
controller = Controller()

dispatcher.connect('get_index', '/', controller=controller,
action='get_index', conditions=dict(method=['GET']))

dispatcher.connect('post_index', '/', controller=controller,
action='post_index', conditions=dict(method=['POST']))

dispatcher.connect('get_pypi', '/pypi/', controller=controller,
action='get_pypi', conditions=dict(method=['GET']))

dispatcher.connect('package', '/pypi/{package}/', controller=controller,
action='package')

dispatcher.connect('package_and_version', '/pypi/{package}/{version}',
controller=controller, action='package_and_version')

package_path = os.path.join(os.path.dirname(__file__), 'packages')
dispatcher.putChild('packages', File(package_path))

factory = Site(dispatcher)
reactor.listenTCP(port, factory)

print 'Running on %d' % port
reactor.run()

if __name__ == '__main__':
set_up_server(8000)
16 changes: 16 additions & 0 deletions setup.py
@@ -0,0 +1,16 @@
from setuptools import setup

setup(name='simplepypi',
version='0.0.1',
author='Zach Steindler',
author_email='steiza@coffeehousecoders.org',
url='http://github.com/steiza/simplepypi',
description='A really, really, simple HTTP PyPI-like server',
long_description='''It is intended to be used for companies or organizations that need a private PyPi''',
keywords='pypi, replacement, cheeseshop',
classifiers=['Programming Language :: Python', 'License :: OSI Approved :: BSD License'],
license='BSD',
packages=['simplepypi'],
scripts=['bin/simplepypi'],
install_requires=['twisted', 'txroutes'],
)

0 comments on commit 8b57e9c

Please sign in to comment.