From python-checkins at python.org Sun Apr 4 19:54:47 2010 From: python-checkins at python.org (martin.von.loewis) Date: Sun, 4 Apr 2010 19:54:47 +0200 (CEST) Subject: [Pypi-checkins] r754 - trunk/pypi Message-ID: <20100404175447.4CCBAF7F0@mail.python.org> Author: martin.von.loewis Date: Sun Apr 4 19:54:47 2010 New Revision: 754 Modified: trunk/pypi/standalone.py Log: Add serversig to supported URLs. Modified: trunk/pypi/standalone.py ============================================================================== --- trunk/pypi/standalone.py (original) +++ trunk/pypi/standalone.py Sun Apr 4 19:54:47 2010 @@ -9,7 +9,7 @@ self.send_header('Content-Type', content_type) def run(self): - for scriptname in ('/mirrors', '/simple', '/pypi'): + for scriptname in ('/mirrors', '/simple', '/pypi', '/serversig'): if self.path.startswith(scriptname): rest = self.path[len(scriptname):] break From python-checkins at python.org Tue Apr 13 00:34:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Tue, 13 Apr 2010 00:34:46 +0200 (CEST) Subject: [Pypi-checkins] r755 - branches/tarek-pep-345-support Message-ID: <20100412223446.1834DEEAD9@mail.python.org> Author: tarek.ziade Date: Tue Apr 13 00:34:45 2010 New Revision: 755 Modified: branches/tarek-pep-345-support/README Log: added distutils2 in the dependency list Modified: branches/tarek-pep-345-support/README ============================================================================== --- branches/tarek-pep-345-support/README (original) +++ branches/tarek-pep-345-support/README Tue Apr 13 00:34:45 2010 @@ -14,6 +14,7 @@ - M2Crypto - BeautifulSoup - docutils +- distutils2 Quick development setup ----------------------- From python-checkins at python.org Tue Apr 13 01:11:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Tue, 13 Apr 2010 01:11:45 +0200 (CEST) Subject: [Pypi-checkins] r756 - branches/tarek-pep-345-support Message-ID: <20100412231145.AD512EEA4C@mail.python.org> Author: tarek.ziade Date: Tue Apr 13 01:11:45 2010 New Revision: 756 Modified: branches/tarek-pep-345-support/config.ini branches/tarek-pep-345-support/webui.py Log: now checking the metadata 1.2 using distutils2 checker Modified: branches/tarek-pep-345-support/config.ini ============================================================================== --- branches/tarek-pep-345-support/config.ini (original) +++ branches/tarek-pep-345-support/config.ini Tue Apr 13 01:11:45 2010 @@ -1,26 +1,27 @@ [database] name = packages user = pypi -files_dir = /tmp/files -docs_dir = /tmp/docs +files_dir = /MacDev/svn.python.org/pypi-pep345/files +docs_dir = /MacDev/svn.python.org/pypi-pep345/docs [webui] mailhost = mail.commonground.com.au adminemail = richard at commonground.com.au -url = http://localhost/cgi-bin/pypi.cgi +replyto = richard at commonground.com.au +url = http://localhost:8000 pydotorg = http://www.python.org/ simple_script = /simple files_url = http://localhost/pypi_files -rss_file = /tmp/pypi_rss.xml +rss_file = /MacDev/svn.python.org/pypi-pep345/pypi_rss.xml debug_mode = yes cheesecake_password = secret privkey = privkey simple_sign_script = /serversig [logging] -file = -mailhost = -fromaddr = +file = +mailhost = +fromaddr = toaddrs = [mirrors] Modified: branches/tarek-pep-345-support/webui.py ============================================================================== --- branches/tarek-pep-345-support/webui.py (original) +++ branches/tarek-pep-345-support/webui.py Tue Apr 13 01:11:45 2010 @@ -4,6 +4,7 @@ import re, zipfile, logging, pprint, sets, shutil, Cookie, subprocess from zope.pagetemplate.pagetemplatefile import PageTemplateFile from distutils.util import rfc822_escape +from distutils2.metadata import DistributionMetadata # Importing M2Crypto patches urllib; don't let them do that orig = urllib.URLopener.open_https.im_func @@ -1741,6 +1742,23 @@ self.write_template('message.pt', title='Package verification', message='Validated OK') + def _validate_metadata_1_2(self, data): + # loading the metadata into + # a DistributionMetadata instance + # so we can use its check() method + metadata = DistributionMetadata() + for key, value in data.items(): + metadata[key] = value + metadata['Metadata-Version'] = '1.2' + missing, warnings = metadata.check() + + # raising the first problem + if len(missing) > 0: + raise ValueError, '"%s" is missing' % missing[0] + + if len(warnings) > 0: + raise ValueError, warnings[0] + def validate_metadata(self, data): ''' Validate the contents of the metadata. ''' @@ -1749,7 +1767,10 @@ if not data.get('version', ''): raise ValueError, 'Missing required field "version"' if data.has_key('metadata_version'): + metadata_version = data['metadata_version'] del data['metadata_version'] + else: + metadata_version = '1.0' # default # Traditionally, package names are restricted only for # technical reasons; / is not allowed because it may be @@ -1775,8 +1796,10 @@ except ValueError, message: raise ValueError, 'Bad "provides" syntax: %s'%message - # XXX tarek todo : check PEP 345 fields - # + # check PEP 345 fields + if metadata_version == '1.2': + self._validate_metadata_1_2(data) + # check classifiers if data.has_key('classifiers'): d = {} From python-checkins at python.org Tue Apr 13 01:14:29 2010 From: python-checkins at python.org (tarek.ziade) Date: Tue, 13 Apr 2010 01:14:29 +0200 (CEST) Subject: [Pypi-checkins] r757 - branches/tarek-pep-345-support Message-ID: <20100412231429.582E8EB1E@mail.python.org> Author: tarek.ziade Date: Tue Apr 13 01:14:29 2010 New Revision: 757 Modified: branches/tarek-pep-345-support/config.ini Log: reverted unwanted change on config.ini, but also added missing variables Modified: branches/tarek-pep-345-support/config.ini ============================================================================== --- branches/tarek-pep-345-support/config.ini (original) +++ branches/tarek-pep-345-support/config.ini Tue Apr 13 01:14:29 2010 @@ -1,18 +1,18 @@ [database] name = packages user = pypi -files_dir = /MacDev/svn.python.org/pypi-pep345/files -docs_dir = /MacDev/svn.python.org/pypi-pep345/docs +files_dir = /tmp/files +docs_dir = /tmp/docs [webui] mailhost = mail.commonground.com.au adminemail = richard at commonground.com.au replyto = richard at commonground.com.au -url = http://localhost:8000 +url = http://localhost/cgi-bin/pypi.cgi pydotorg = http://www.python.org/ simple_script = /simple files_url = http://localhost/pypi_files -rss_file = /MacDev/svn.python.org/pypi-pep345/pypi_rss.xml +rss_file = /tmp/pypi_rss.xml debug_mode = yes cheesecake_password = secret privkey = privkey From python-checkins at python.org Sun Apr 18 10:37:08 2010 From: python-checkins at python.org (martin.von.loewis) Date: Sun, 18 Apr 2010 10:37:08 +0200 (CEST) Subject: [Pypi-checkins] r758 - trunk/pypi Message-ID: <20100418083708.A36A3EE984@mail.python.org> Author: martin.von.loewis Date: Sun Apr 18 10:37:08 2010 New Revision: 758 Modified: trunk/pypi/openid.py Log: Issue #2987103: support application/xhtml+xml documents for HTML-Based Discovery. Modified: trunk/pypi/openid.py ============================================================================== --- trunk/pypi/openid.py (original) +++ trunk/pypi/openid.py Sun Apr 18 10:37:08 2010 @@ -182,7 +182,7 @@ if xrds_loc and content_type != 'application/xrds+xml': return discover(xrds_loc) - if content_type == 'text/html': + if content_type in ('text/html', 'application/xhtml+xml'): soup = BeautifulSoup.BeautifulSoup(data) # Yadis 6.2.5 option 1: meta tag meta = soup.find('meta', {'http-equiv':lambda v:v and v.lower()=='x-xrds-location'}) @@ -212,7 +212,7 @@ # Discovery failed return None - if content_type == 'application/xrds+xml': + elif content_type == 'application/xrds+xml': # Yadis 6.2.5 option 4 doc = ElementTree.fromstring(data) for svc in doc.findall(".//{xri://$xrd*($v*2.0)}Service"): @@ -248,6 +248,9 @@ break else: return None # No OpenID 2.0 service found + else: + # unknown content type + return None return services, op_endpoint, op_local def is_compat_1x(services): From python-checkins at python.org Sat Apr 24 12:24:53 2010 From: python-checkins at python.org (martin.von.loewis) Date: Sat, 24 Apr 2010 12:24:53 +0200 (CEST) Subject: [Pypi-checkins] r760 - trunk/pypi/tools Message-ID: <20100424102453.F3878EEA50@mail.python.org> Author: martin.von.loewis Date: Sat Apr 24 12:24:53 2010 New Revision: 760 Modified: trunk/pypi/tools/sql-migrate-20100313.sql Log: Fix typo. Modified: trunk/pypi/tools/sql-migrate-20100313.sql ============================================================================== --- trunk/pypi/tools/sql-migrate-20100313.sql (original) +++ trunk/pypi/tools/sql-migrate-20100313.sql Sat Apr 24 12:24:53 2010 @@ -1,5 +1,5 @@ -- New fields -ALTER TABLE release ADD COLUMN requires_python TEXT; +ALTER TABLE releases ADD COLUMN requires_python TEXT; -- -- New tables From python-checkins at python.org Wed Apr 28 19:12:17 2010 From: python-checkins at python.org (tarek.ziade) Date: Wed, 28 Apr 2010 19:12:17 +0200 (CEST) Subject: [Pypi-checkins] r761 - in trunk/pypi: . templates Message-ID: <20100428171217.D6C44FACD@mail.python.org> Author: tarek.ziade Date: Wed Apr 28 19:12:17 2010 New Revision: 761 Modified: trunk/pypi/config.ini trunk/pypi/pypi.css trunk/pypi/templates/standard_template.pt Log: fixed the bad merge that was introduced with the PEP 345 merge:. The user box is scrolling again properly Modified: trunk/pypi/config.ini ============================================================================== --- trunk/pypi/config.ini (original) +++ trunk/pypi/config.ini Wed Apr 28 19:12:17 2010 @@ -1,18 +1,19 @@ [database] name = packages user = pypi -files_dir = /tmp/files -docs_dir = /tmp/docs +files_dir = /MacDev/svn.python.org/pypi-pep345/files +docs_dir = /MacDev/svn.python.org/pypi-pep345/docs [webui] mailhost = mail.commonground.com.au adminemail = richard at commonground.com.au replyto = richard at commonground.com.au -url = http://localhost/cgi-bin/pypi.cgi +url = http://localhost:8000 pydotorg = http://www.python.org/ + simple_script = /simple files_url = http://localhost/pypi_files -rss_file = /tmp/pypi_rss.xml +rss_file = /MacDev/svn.python.org/pypi-pep345/pypi_rss.xml debug_mode = yes cheesecake_password = secret privkey = privkey Modified: trunk/pypi/pypi.css ============================================================================== --- trunk/pypi/pypi.css (original) +++ trunk/pypi/pypi.css Wed Apr 28 19:12:17 2010 @@ -184,4 +184,99 @@ list-style-type: none; } - + + +.floating-box { +float: right; +} + +.homepage-box h4, #document-project-url h4, .externallinks h4 +{ +font-family: Verdana, Geneva, "Bitstream Vera Sans", Helvetica, sans-serif; +font-size: 88%; +font-weight: bold; +padding: 3px 0 4px 12px; +margin-bottom:8px; +border-bottom:3px solid #d5ad42; +background-color: #F4F4F4; +text-align: left; +z-index: 0; +-moz-border-radius-topleft: 16px; +} + +#document-project-url h4, .homepage-box h4, .externallinks h4 { +-moz-border-radius-topleft:11px; +background-color:#376A94; +color:#FFFFFF; +font-family:Arial,Verdana,Geneva,"Bitstream Vera Sans",Helvetica,sans-serif; +font-size:94%; +font-weight:bold; +margin:0; +padding:3px 0 4px 12px; +text-align:left; +z-index:0; +} + +#document-project-url +{ +font-family: Verdana, Geneva, "Bitstream Vera Sans", Helvetica, sans-serif; +font-size: 98%; +text-align: left; +color: #3C4B6B; +background-color: #F7F6F0; +width: 15em; +border: 1px solid #B7BECC; +z-index: 999; +float:right; +margin: 0 12px 12px 12px; +padding-bottom: 10px; +list-style:none; +-moz-border-radius-topleft: 12px; +-moz-border-radius-bottomright: 12px; +} + +#document-project-url ul +{ +list-style: none; +display: block; +border:0; +padding: 0; +margin: 0 3em 0 1.1em; +color: #3C4B6B; +background: none; +width: auto; +font-size: 103%; +font-family: Verdana, Geneva, "Bitstream Vera Sans", Helvetica, sans-serif; +} + +#document-project-url a:link, #document-project-url a:visited +{ +color:#3C4B6B; +text-decoration: none; +} + +#document-project-url a:hover +{ +color: #000000; +text-decoration: underline; +} + +#document-project-url h4 a:link, #document-project-url h4 a:visited +{ +color: #FFFFFF; +} + + +#document-project-url h4 a:hover +{ +color: #FFFFFF; + +} +#document-project-url form { +margin:12px; +} +#document-project-url input { +margin-top:3px; +} + + Modified: trunk/pypi/templates/standard_template.pt ============================================================================== --- trunk/pypi/templates/standard_template.pt (original) +++ trunk/pypi/templates/standard_template.pt Wed Apr 28 19:12:17 2010 @@ -12,7 +12,6 @@ - @@ -24,100 +23,6 @@ @@ -203,9 +108,9 @@ -
+
-
+

Not Logged In

@@ -254,8 +159,10 @@
+
+ +
-

Project Links

    @@ -268,7 +175,6 @@
-
From python-checkins at python.org Wed Apr 28 19:13:38 2010 From: python-checkins at python.org (tarek.ziade) Date: Wed, 28 Apr 2010 19:13:38 +0200 (CEST) Subject: [Pypi-checkins] r762 - trunk/pypi Message-ID: <20100428171338.2732EEE990@mail.python.org> Author: tarek.ziade Date: Wed Apr 28 19:13:37 2010 New Revision: 762 Modified: trunk/pypi/config.ini Log: reverted unwanted commit in config.ini Modified: trunk/pypi/config.ini ============================================================================== --- trunk/pypi/config.ini (original) +++ trunk/pypi/config.ini Wed Apr 28 19:13:37 2010 @@ -8,12 +8,12 @@ mailhost = mail.commonground.com.au adminemail = richard at commonground.com.au replyto = richard at commonground.com.au -url = http://localhost:8000 +url = http://localhost/cgi-bin/pypi.cgi pydotorg = http://www.python.org/ simple_script = /simple files_url = http://localhost/pypi_files -rss_file = /MacDev/svn.python.org/pypi-pep345/pypi_rss.xml +rss_file = /tmp/pypi_rss.xml debug_mode = yes cheesecake_password = secret privkey = privkey From python-checkins at python.org Fri Apr 30 18:10:40 2010 From: python-checkins at python.org (martin.von.loewis) Date: Fri, 30 Apr 2010 18:10:40 +0200 (CEST) Subject: [Pypi-checkins] r763 - trunk/pypi Message-ID: <20100430161040.211E7EE9EB@mail.python.org> Author: martin.von.loewis Date: Fri Apr 30 18:10:39 2010 New Revision: 763 Modified: trunk/pypi/webui.py Log: Issue 2992721: Skip over creating directories that have already been created. Modified: trunk/pypi/webui.py ============================================================================== --- trunk/pypi/webui.py (original) +++ trunk/pypi/webui.py Fri Apr 30 18:10:39 2010 @@ -2292,7 +2292,8 @@ if not fpath.startswith(path): raise ValueError, "invalid path name:"+fname if fname.endswith("/"): - os.mkdir(fpath) + if not os.path.isdir(fpath): + os.mkdir(fpath) continue upperdirs = os.path.dirname(fpath) if not os.path.exists(upperdirs): From python-checkins at python.org Sat Apr 24 12:22:22 2010 From: python-checkins at python.org (martin.von.loewis) Date: Sat, 24 Apr 2010 10:22:22 -0000 Subject: [Pypi-checkins] r759 - in trunk/pypi: . templates tools Message-ID: <20100424102222.97E1BEEA50@mail.python.org> Author: martin.von.loewis Date: Sat Apr 24 12:22:22 2010 New Revision: 759 Added: trunk/pypi/tools/sql-migrate-20100313.sql - copied unchanged from r758, branches/tarek-pep-345-support/tools/sql-migrate-20100313.sql Modified: trunk/pypi/ (props changed) trunk/pypi/README trunk/pypi/config.ini trunk/pypi/pkgbase_schema.sql trunk/pypi/pypi.wsgi trunk/pypi/rpc.py trunk/pypi/store.py trunk/pypi/templates/display.pt trunk/pypi/templates/standard_template.pt trunk/pypi/tools/apache_count_dist.py (props changed) trunk/pypi/tools/sql-migrate-20090327.sql (props changed) trunk/pypi/webui.py Log: Merge tarek-pep-345-support. Modified: trunk/pypi/README ============================================================================== --- trunk/pypi/README (original) +++ trunk/pypi/README Sat Apr 24 12:22:22 2010 @@ -1,13 +1,44 @@ - Required packages ----------------------- +----------------- + +To run the PyPI software, you need PostgreSQL, and all +these packages located at PyPI: + +- cElementTree +- zope.interface +- zope.pagetemplate +- zope.tal +- zope.tales +- zope.i18nmessageid +- psycopg2 +- M2Crypto +- BeautifulSoup +- docutils +- distutils2 + +Quick development setup +----------------------- + +Make sure you read http://wiki.python.org/moin/CheeseShopDev#DevelopmentEnvironmentHints +and you have a working PostgreSQL DB. + +Make sure your config.ini is up-to-date. Change CONFIG_FILE at the begining +of pypi.wsgi, so it looks like this:: + + CONFIG_FILE = 'config.ini' + +Then, you can create a development environment like this, if you have +virtualenv installed:: + + $ virtualenv --no-site-packages --distribute . + $ bin/easy_install cElementTree zope.interface zope.pagetemplate + $ bin/easy_install zope.tal zope.tales zope.i18nmessageid psycopg2 + $ bin/easy_install M2Crypto BeautifulSoup docutils + +Then you can launch the server using the pypi.wsgi script:: + + $ bin/python pypi.wsgi + Serving on port 8000... + +PyPI will be available in your browser at http://localhost:8000 -To run the PyPI software, you need: - cElementTree - zope.interface (in PyPI) - zope.pagetemplates (in PyPI) - zope.tal - zope.tales - zope.i18nmessageid - psycopg2 and PostgreSQL - M2Crypto Modified: trunk/pypi/config.ini ============================================================================== --- trunk/pypi/config.ini (original) +++ trunk/pypi/config.ini Sat Apr 24 12:22:22 2010 @@ -7,6 +7,7 @@ [webui] mailhost = mail.commonground.com.au adminemail = richard at commonground.com.au +replyto = richard at commonground.com.au url = http://localhost/cgi-bin/pypi.cgi pydotorg = http://www.python.org/ simple_script = /simple @@ -18,9 +19,9 @@ simple_sign_script = /serversig [logging] -file = -mailhost = -fromaddr = +file = +mailhost = +fromaddr = toaddrs = [mirrors] Modified: trunk/pypi/pkgbase_schema.sql ============================================================================== --- trunk/pypi/pkgbase_schema.sql (original) +++ trunk/pypi/pkgbase_schema.sql Sat Apr 24 12:22:22 2010 @@ -1,10 +1,10 @@ -- Table structure for table: users -CREATE TABLE users ( - name TEXT PRIMARY KEY, - password TEXT, - email TEXT, +CREATE TABLE users ( + name TEXT PRIMARY KEY, + password TEXT, + email TEXT, gpg_keyid TEXT, - last_login TIMESTAMP, + last_login TIMESTAMP ); CREATE INDEX users_email_idx ON users(email); @@ -12,7 +12,7 @@ CREATE TABLE openids ( id TEXT PRIMARY KEY, - name TEXT REFERENCES users, + name TEXT REFERENCES users ); CREATE TABLE openid_sessions ( @@ -41,7 +41,7 @@ CREATE TABLE cookies ( cookie text PRIMARY KEY, name text references users, - last_seen timestamp, + last_seen timestamp ); CREATE INDEX cookies_last_seen ON cookies(last_seen); @@ -54,33 +54,33 @@ CREATE INDEX rego_otk_otk_idx ON rego_otk(otk); -- Table structure for table: rego_otk -CREATE TABLE rego_otk ( - name TEXT REFERENCES users, +CREATE TABLE rego_otk ( + name TEXT REFERENCES users, otk TEXT, date TIMESTAMP ); CREATE INDEX rego_otk_name_idx ON rego_otk(name); -- Table structure for table: journals -CREATE TABLE journals ( - name TEXT, - version TEXT, - action TEXT, - submitted_date TIMESTAMP, - submitted_by TEXT REFERENCES users, +CREATE TABLE journals ( + name TEXT, + version TEXT, + action TEXT, + submitted_date TIMESTAMP, + submitted_by TEXT REFERENCES users, submitted_from TEXT ); CREATE INDEX journals_name_idx ON journals(name); CREATE INDEX journals_version_idx ON journals(version); -CREATE INDEX journals_latest_releases ON - journals(submitted_date, name, version) +CREATE INDEX journals_latest_releases ON + journals(submitted_date, name, version) WHERE version IS NOT NULL AND action='new release'; -CREATE INDEX journals_changelog ON +CREATE INDEX journals_changelog ON journals(submitted_date, name, version, action); -- Table structure for table: packages -CREATE TABLE packages ( - name TEXT PRIMARY KEY, +CREATE TABLE packages ( + name TEXT PRIMARY KEY, stable_version TEXT, normalized_name TEXT, autohide BOOLEAN DEFAULT TRUE, @@ -122,7 +122,7 @@ -- Table structure for table: releases -CREATE TABLE releases ( +CREATE TABLE releases ( name TEXT REFERENCES packages ON UPDATE CASCADE, version TEXT, author TEXT, @@ -137,25 +137,22 @@ keywords TEXT, platform TEXT, download_url TEXT, + requires_python TEXT, cheesecake_installability_id INTEGER REFERENCES cheesecake_main_indices, cheesecake_documentation_id INTEGER REFERENCES cheesecake_main_indices, cheesecake_code_kwalitee_id INTEGER REFERENCES cheesecake_main_indices, _pypi_ordering INTEGER, _pypi_hidden BOOLEAN, - cheesecake_installability_id INTEGER REFERENCES cheesecake_main_indices, - cheesecake_documentation_id INTEGER REFERENCES cheesecake_main_indices, - cheesecake_code_kwalitee_id INTEGER REFERENCES cheesecake_main_indices, PRIMARY KEY (name, version) ); CREATE INDEX release_pypi_hidden_idx ON releases(_pypi_hidden); - -- Table structure for table: trove_classifiers -- l2, l3, l4, l5 is the corresponding parent; -- 0 if there is no parent on that level (each node is its -- own parent) -CREATE TABLE trove_classifiers ( - id INTEGER PRIMARY KEY, +CREATE TABLE trove_classifiers ( + id INTEGER PRIMARY KEY, classifier TEXT UNIQUE, l2 INTEGER, l3 INTEGER, @@ -219,12 +216,66 @@ CREATE INDEX rel_obs_version_id_idx ON release_obsoletes(version); CREATE INDEX rel_obs_name_version_idx ON release_obsoletes (name,version); +-- Table structure for table: release_requires_external +CREATE TABLE release_requires_external ( + name TEXT, + version TEXT, + specifier TEXT, + FOREIGN KEY (name, version) REFERENCES releases (name, version) ON UPDATE CASCADE +); +CREATE INDEX rel_req_ext_name_idx ON release_requires_external(name); +CREATE INDEX rel_req_ext_version_id_idx ON release_requires_external(version); +CREATE INDEX rel_req_ext_name_version_idx ON release_requires_external(name,version); + +-- Table structure for table: release_requires_dist +CREATE TABLE release_requires_dist ( + name TEXT, + version TEXT, + specifier TEXT, + FOREIGN KEY (name, version) REFERENCES releases (name, version) ON UPDATE CASCADE +); +CREATE INDEX rel_req_dist_name_idx ON release_requires_dist(name); +CREATE INDEX rel_req_dist_version_id_idx ON release_requires_dist(version); +CREATE INDEX rel_req_dist_name_version_idx ON release_requires_dist(name,version); + +-- Table structure for table: release_provides_dist +CREATE TABLE release_provides_dist ( + name TEXT, + version TEXT, + specifier TEXT, + FOREIGN KEY (name, version) REFERENCES releases (name, version) ON UPDATE CASCADE +); +CREATE INDEX rel_prov_dist_name_idx ON release_provides_dist(name); +CREATE INDEX rel_prov_dist_version_id_idx ON release_provides_dist(version); +CREATE INDEX rel_prov_dist_name_version_idx ON release_provides_dist(name,version); + +-- Table structure for table: release_obsoletes_dist +CREATE TABLE release_obsoletes_dist ( + name TEXT, + version TEXT, + specifier TEXT, + FOREIGN KEY (name, version) REFERENCES releases (name, version) ON UPDATE CASCADE +); +CREATE INDEX rel_obs_dist_name_idx ON release_obsoletes_dist(name); +CREATE INDEX rel_obs_dist_version_id_idx ON release_obsoletes_dist(version); +CREATE INDEX rel_obs_dist_name_version_idx ON release_obsoletes_dist(name,version); + +-- Table structure for table: release_project_url +CREATE TABLE release_project_url ( + name TEXT, + version TEXT, + specifier TEXT, + FOREIGN KEY (name, version) REFERENCES releases (name, version) ON UPDATE CASCADE +); +CREATE INDEX rel_proj_url_name_idx ON release_project_url(name); +CREATE INDEX rel_proj_url_version_id_idx ON release_project_url(version); +CREATE INDEX rel_proj_url_name_version_idx ON release_project_url(name,version); -- Table structure for table: package_files -- python version is only first two digits -- actual file path is constructed /// -- we remember filename because it can differ -CREATE TABLE release_files ( +CREATE TABLE release_files ( name TEXT, version TEXT, python_version TEXT, @@ -243,10 +294,10 @@ -- Table structure for table: package_urls -CREATE TABLE release_urls ( +CREATE TABLE release_urls ( name TEXT, version TEXT, - url TEXT, + url TEXT, packagetype TEXT, FOREIGN KEY (name, version) REFERENCES releases (name, version) ON UPDATE CASCADE ); @@ -266,9 +317,9 @@ -- Table structure for table: roles -- Note: roles are Maintainer, Admin, Owner -CREATE TABLE roles ( - role_name TEXT, - user_name TEXT REFERENCES users, +CREATE TABLE roles ( + role_name TEXT, + user_name TEXT REFERENCES users, package_name TEXT REFERENCES packages ON UPDATE CASCADE ); CREATE INDEX roles_pack_name_idx ON roles(package_name); @@ -287,7 +338,7 @@ -- Table structure for table: timestamps -- Note: stamp_name is ftp, http CREATE TABLE browse_tally ( - trove_id INTEGER PRIMARY KEY, + trove_id INTEGER PRIMARY KEY, tally INTEGER ); @@ -307,7 +358,7 @@ in_reply_to INTEGER REFERENCES comments ON DELETE CASCADE ); CREATE TABLE ratings( - id SERIAL UNIQUE; + id SERIAL UNIQUE, name TEXT, version TEXT, user_name TEXT REFERENCES users ON DELETE CASCADE, Modified: trunk/pypi/pypi.wsgi ============================================================================== --- trunk/pypi/pypi.wsgi (original) +++ trunk/pypi/pypi.wsgi Sat Apr 24 12:22:22 2010 @@ -6,18 +6,24 @@ store.keep_conn = True +CONFIG_FILE = 'config.ini' + class Request: def __init__(self, environ, start_response): self.start_response = start_response - self.rfile = cStringIO.StringIO(environ['wsgi.input'].read()) + try: + length = int(environ['CONTENT_LENGTH']) + except ValueError: + length = 0 + self.rfile = cStringIO.StringIO(environ['wsgi.input'].read(length)) self.wfile = cStringIO.StringIO() - self.config = config.Config('/data/pypi/config.ini') - + self.config = config.Config(CONFIG_FILE ) + def send_response(self, code, message='no details available'): self.status = '%s %s' % (code, message) self.headers = [] - + def send_header(self, keyword, value): self.headers.append((keyword, value)) @@ -47,3 +53,11 @@ r = Request(environ, start_response) webui.WebUI(r, environ).run() return [r.wfile.getvalue()] + +if __name__ == '__main__': + # very simple wsgi server so we can play locally + from wsgiref.simple_server import make_server + httpd = make_server('', 8000, application) + print "Serving on port 8000..." + httpd.serve_forever() + Modified: trunk/pypi/rpc.py ============================================================================== --- trunk/pypi/rpc.py (original) +++ trunk/pypi/rpc.py Sat Apr 24 12:22:22 2010 @@ -7,7 +7,7 @@ class RequestHandler(SimpleXMLRPCDispatcher): """A request dispatcher for the PyPI XML-RPC API.""" - + def __init__(self): SimpleXMLRPCDispatcher.__init__(self, True, 'utf-8') self.register_function(list_packages) @@ -23,7 +23,7 @@ self.register_function(ratings) self.register_introspection_functions() self.register_multicall_functions() - + def __call__(self, webui_obj): webui_obj.handler.send_response(200, 'OK') webui_obj.handler.send_header('Content-type', 'text/xml') @@ -77,7 +77,9 @@ def release_data(store, package_name, version): info = store.get_package(package_name, version).as_dict() del info['description_html'] - for col in ('requires', 'provides', 'obsoletes'): + for col in ('requires', 'provides', 'obsoletes', 'requires_dist', + 'obsoletes_dist', 'project_url', 'provides_dist', + 'requires_external'): rows = store.get_release_relationships(package_name, version, col) info[col] = [row['specifier'] for row in rows] classifiers = [r[0] for r in store.get_release_classifiers(package_name, Modified: trunk/pypi/store.py ============================================================================== --- trunk/pypi/store.py (original) +++ trunk/pypi/store.py Sat Apr 24 12:22:22 2010 @@ -79,6 +79,7 @@ def utf8get(fields): if fields[n] is None: return fields[n] return fields[n].decode('utf-8', 'replace') + return utf8get def itemgetter(n): @@ -199,7 +200,6 @@ journal entry. ''' date = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime()) - cursor = self.get_cursor() # see if we're inserting or updating a package if not self.has_package(name): @@ -268,7 +268,9 @@ old.append('classifiers') # get old classifiers list - for col in ('requires', 'provides', 'obsoletes'): + for col in ('requires', 'provides', 'obsoletes', 'requires_dist', + 'provides_dist', 'obsoletes_dist', + 'requires_external', 'project_url'): relationships[col] = self.get_release_relationships(name, version, col) relationships[col].sort() @@ -311,7 +313,10 @@ info['description_html'] = '' # perform the insert - cols = 'name version author author_email maintainer maintainer_email home_page license summary description description_html keywords platform download_url _pypi_ordering _pypi_hidden'.split() + cols = ('name version author author_email maintainer ' + 'maintainer_email home_page license summary description ' + 'description_html keywords platform requires_python ' + 'download_url _pypi_ordering _pypi_hidden').split() args = tuple([info.get(k, None) for k in cols]) params = ','.join(['%s']*len(cols)) scols = ','.join(cols) @@ -353,7 +358,9 @@ (name, version, trove_id)) # handle relationship specifiers - for col in ('requires', 'provides', 'obsoletes'): + for col in ('requires', 'provides', 'obsoletes', 'requires_dist', + 'provides_dist', 'obsoletes_dist', + 'requires_external', 'project_url'): if not info.has_key(col) or relationships.get(col, []) == info[col]: continue safe_execute(cursor, '''delete from release_%s where name=%%s @@ -455,8 +462,8 @@ _Package = FastResultRow('''name stable_version version author author_email maintainer maintainer_email home_page license summary description - description_html keywords platform download_url _pypi_ordering! - _pypi_hidden! cheesecake_installability_id! + description_html keywords platform requires_python download_url + _pypi_ordering! _pypi_hidden! cheesecake_installability_id! cheesecake_documentation_id! cheesecake_code_kwalitee_id!''') def get_package(self, name, version): ''' Retrieve info about the package from the database. @@ -467,7 +474,8 @@ sql = '''select packages.name as name, stable_version, version, author, author_email, maintainer, maintainer_email, home_page, license, summary, description, description_html, keywords, - platform, download_url, _pypi_ordering, _pypi_hidden, + platform, requires_python, download_url, _pypi_ordering, + _pypi_hidden, cheesecake_installability_id, cheesecake_documentation_id, cheesecake_code_kwalitee_id @@ -656,6 +664,64 @@ safe_execute(cursor, 'update packages set autohide=%s where name=%s', [value, name]) + def _get_package_url(self, name): + name = name.split()[0] + cursor = self.get_cursor() + sql = 'select * from packages where name=%s' + safe_execute(cursor, sql, (name, )) + exists = cursor.fetchone() is not None + if not exists: + return None + return self.config.url + '/' + name + + def get_package_requires_dist(self, name, version): + cursor = self.get_cursor() + safe_execute(cursor, '''select specifier from release_requires_dist + where name=%s and version=%s ''', (name, version)) + packages = [] + for package in cursor.fetchall(): + pack = {'name': package[0], + 'href': self._get_package_url(package[0])} + packages.append(pack) + return packages + + def get_package_provides_dist(self, name, version): + cursor = self.get_cursor() + safe_execute(cursor, '''select specifier from release_provides_dist + where name=%s and version=%s ''', (name, version)) + packages = [] + for package in cursor.fetchall(): + pack = {'name': package[0], + 'href': self._get_package_url(package[0])} + packages.append(pack) + return packages + + def get_package_obsoletes_dist(self, name, version): + cursor = self.get_cursor() + safe_execute(cursor, '''select specifier from release_obsoletes_dist + where name=%s and version=%s ''', (name, version)) + packages = [] + for package in cursor.fetchall(): + pack = {'name': package[0], + 'href': self._get_package_url(package[0])} + packages.append(pack) + return packages + + def get_package_requires_external(self, name, version): + cursor = self.get_cursor() + safe_execute(cursor, '''select specifier from release_requires_external + where name=%s and version=%s ''', (name, version)) + return [package[0] for package in cursor.fetchall()] + + def get_package_project_url(self, name, version): + cursor = self.get_cursor() + safe_execute(cursor, '''select specifier from release_project_url + where name=%s and version=%s ''', (name, version)) + project_urls = [] + for project in cursor.fetchall(): + project_urls.append(project[0].split(',')) + return project_urls + def get_package_comments(self, name): cursor = self.get_cursor() safe_execute(cursor, 'select comments from packages where name=%s', @@ -877,7 +943,9 @@ # delete ancillary table entries for tab in ('files', 'provides', 'requires', 'obsoletes', - 'classifiers'): + 'classifiers', 'requires_dist', 'provides_dist', + 'obsoletes_dist', 'requires_external', + 'project_url'): safe_execute(cursor, '''delete from release_%s where name=%%s and version=%%s'''%tab, (name, version)) safe_execute(cursor, 'delete from description_urls where name=%s and version=%s', @@ -891,7 +959,7 @@ safe_execute(cursor, '''insert into journals (name, version, action, submitted_date, submitted_by, submitted_from) values (%s, %s, %s, %s, %s, %s)''', (name, version, 'remove', date, - self.username, self.userip)) + self.username, self.userip)) def remove_package(self, name): ''' Delete an entire package from the database. @@ -904,7 +972,9 @@ # delete ancillary table entries for tab in ('files', 'provides', 'requires', 'obsoletes', - 'classifiers'): + 'classifiers', 'requires_dist', 'provides_dist', + 'obsoletes_dist', 'requires_external', + 'project_url'): safe_execute(cursor, 'delete from release_%s where name=%%s'%tab, (name, )) @@ -969,7 +1039,7 @@ '''Copy a user-s rating of package name from one version to another; return the comment if any''' cursor = self.get_cursor() - safe_execute(cursor, '''insert into ratings(name,version,user_name,date,rating) + safe_execute(cursor, '''insert into ratings(name,version,user_name,date,rating) select name,%s,user_name,now(),rating from ratings where name=%s and version=%s and user_name=%s''', (toversion, name, fromversion, self.username)) @@ -979,10 +1049,10 @@ if cid: cid = cid[0] safe_execute(cursor, '''insert into comments(rating, user_name, date, message, in_reply_to) - select currval('ratings_id_seq'), user_name, date, message, in_reply_to + select currval('ratings_id_seq'), user_name, date, message, in_reply_to from comments where id=%s''', (cid,)) safe_execute(cursor, '''insert into comments_journal(name, version, id, submitted_by, date, action) - values(%s, %s, currval('comments_id_seq'), %s, now(), %s)''', (name, toversion, + values(%s, %s, currval('comments_id_seq'), %s, now(), %s)''', (name, toversion, self.username, 'copied %s' % cid)) safe_execute(cursor, '''select message from comments @@ -996,7 +1066,7 @@ safe_execute(cursor, """insert into comments_journal(name, version, id, submitted_by, date, action) select %s, %s, id, %s, now(), 'deleted' from ratings where user_name=%s and name=%s and version=%s""", (name, version, self.username, self.username, name, version)) - safe_execute(cursor, "delete from ratings where user_name=%s and name=%s and version=%s", + safe_execute(cursor, "delete from ratings where user_name=%s and name=%s and version=%s", (self.username, name, version)) _Rating=FastResultRow('id! date! user rating!') @@ -1004,7 +1074,7 @@ def get_ratings(self, name, version): '''Return ratings,messages for a release.''' cursor = self.get_cursor() - safe_execute(cursor, '''select id, date, user_name, rating from ratings + safe_execute(cursor, '''select id, date, user_name, rating from ratings where name=%s and version=%s order by date''', (name, version)) res = cursor.fetchall() safe_execute(cursor, '''select c.id, c.rating, c.user_name, c.date, c.message, c.in_reply_to from @@ -1098,14 +1168,14 @@ def has_rating(self, name, version): '''Check whether user has rated this release''' cursor = self.get_cursor() - safe_execute(cursor, '''select count(*) from ratings + safe_execute(cursor, '''select count(*) from ratings where user_name=%s and name=%s and version=%s''', (self.username, name, version)) return cursor.fetchall()[0][0] def latest_rating(self, name): '''Return the user-s latest rating on any release, or None.''' cursor = self.get_cursor() - safe_execute(cursor, '''select version from ratings + safe_execute(cursor, '''select version from ratings where user_name=%s and name=%s order by date desc limit 1''', (self.username, name)) res = cursor.fetchone() if res: @@ -1321,7 +1391,7 @@ ''' cursor = self.get_cursor() safe_execute(cursor, 'insert into sshkeys(name, key) values(%s, %s)', (username, key)) - + def delete_sshkey(self, id): '''Delete an SSH key given by ID. ''' Modified: trunk/pypi/templates/display.pt ============================================================================== --- trunk/pypi/templates/display.pt (original) +++ trunk/pypi/templates/display.pt Sat Apr 24 12:22:22 2010 @@ -156,6 +156,64 @@ + + +
  • + Requires Distributions + +
  • + +
  • + Provides Distributions + +
  • + +
  • + Obsoletes Distributions + +
  • + +
  • + Requires Externals +
      +
    • + + +
    • +
    +
  • + +
  • + Requires Python: +
  • + +
  • Package Index Owner: Modified: trunk/pypi/templates/standard_template.pt ============================================================================== --- trunk/pypi/templates/standard_template.pt (original) +++ trunk/pypi/templates/standard_template.pt Sat Apr 24 12:22:22 2010 @@ -1,6 +1,6 @@ @@ -14,14 +14,111 @@ - - - - - - - - + + + + + + + + + + @@ -49,15 +146,15 @@
  • - + - +
    @@ -100,81 +197,96 @@ -
    - -

    Not Logged In

    - - +
    - +
    + +

    Not Logged In

    + + - +
    -

    Welcome

    -
  • - Your details -
  • + - - -
  • - Your packages: -
  • - -
    -
    - -
  • - Logout -
  • -
    + + + + +
  • + Logout +
  • + -
    + +
    +

    Project Links

    +
      + +
    • + +
    • +
      +
    +
    +
    + +
    +

    - + This template has not replaced its body slot. - + - +