Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add mapping between classifiers and versions #4

Merged
merged 9 commits into from

6 participants

@toutpt
Owner

It is really boring that we specify in setup.py the version of Plone supported using classifiers and need to do it by hand in the plonesoftware center.

Here I have added a mapping to support this directly. I would be glade that someone review this change and apply it. If no changes and no discussion happens I will merge it myself in one week.

Any feedback welcomed

@keul
Collaborator

This is long long long wanted feature!

@davisagli
Owner

This assumes any pypi classifier with a number in it corresponds to a Plone version. Shouldn't we limit it to the classifiers that start with "Framework :: Plone ::" ?

@aclark4life
Owner

AFAICT what @davisagli says is true, however getClassifiers appears to get the classifiers for a particular PSC release (vs. going through every classifier on PyPI) so I'm not too worried about limiting to "Framework :: Plone ::" in this case. Athough it probably couldn't hurt, and it may make the intention of the code much clearer. +1 for merge.

@davisagli
Owner

Actually I don't understand this pull request at all. It doesn't look like PSC releases have a field called classifiers, and even if they did, I assume it would be the full PyPI trove classifiers rather than anything that could be converted to a Decimal.

@toutpt
Owner

@davisagli classifiers are provided with acquissition from psc configuration and default is a decimal. this code is the minimal to work without change any data on plone.org

and it works on a clean empty setup in localhost with all configuration to default.

@mauritsvanrees
Collaborator

I have tried this locally and it seems to work fine.

Ah, a problem can be if there are rows in the classifiers field of the PSC object that have nothing to do with Plone but that have an id that is a Plone version. Case in point is this one:

3|3|Programming Language :: Python :: 3

So if your setup.py uses the Python 3 classifier, then the 3 classifier will get set on the Project. And with this pull request Plone 3 will get set in the compatibility field. I tried this locally and this is indeed the case, which is of course not wanted.

@toutpt: It may be better to get self.request.form.get('classifiers') in your method instead of release.getClassifiers(). Then iterate over that list and check for items of the form Framework :: Plone :: X.Y

@toutpt
Owner

I have done some change to use the request as mentionned and filter classifiers on 'Framework :: Plone :: '

Is this good now ?

@mauritsvanrees
Collaborator

+1 for merging. Looks good to me and works when I try it locally. I have wanted this for a long time. Thanks!

I am not sure what the normal procedure here is, as a release would have to be made and plone.org would need to be updated too, after updating a test server, and I can't do that.

@aclark4life
Owner

It's being discussed on the product-developers list, please chime in there.

@aclark4life aclark4life merged commit b7532f0 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 4, 2012
  1. @toutpt

    - Add mapping between classifiers and Plone release version when rele…

    toutpt authored
    …ase using pypi api [toutpt]
Commits on Nov 16, 2012
  1. @jaroel @toutpt

    Whitespace

    jaroel authored toutpt committed
  2. @jaroel @toutpt

    Up the limit to 100M instead of 5M. Refs #5

    jaroel authored toutpt committed
  3. @jaroel @toutpt

    Add MANIFEST.in

    jaroel authored toutpt committed
  4. @jaroel @toutpt

    Explicitly ignore *.pyc

    jaroel authored toutpt committed
  5. @jaroel @toutpt

    Preparing release 1.6.4

    jaroel authored toutpt committed
  6. @jaroel @toutpt

    Back to development: 1.6.5

    jaroel authored toutpt committed
  7. @toutpt

    Add stub pypi_view

    Alex Clark authored toutpt committed
  8. @toutpt
This page is out of date. Refresh to see the latest.
View
6 MANIFEST.in
@@ -0,0 +1,6 @@
+include README.rst
+
+recursive-include docs *
+recursive-include Products *
+
+global-exclude *.pyc
View
8 Products/PloneSoftwareCenter/browser/configure.zcml
@@ -172,4 +172,12 @@
file="fancyzoom.js"
/>
+ <!-- pypi view -->
+ <page
+ class=".pypi_view.PackageIndexView"
+ for="Products.PloneSoftwareCenter.interfaces.ISoftwareCenterContent"
+ name="pypi_view"
+ permission="zope2.View"
+ />
+
</configure>
View
75 Products/PloneSoftwareCenter/browser/pypi.py
@@ -3,6 +3,7 @@
"""
import re
import hashlib
+from decimal import Decimal, InvalidOperation
from AccessControl import getSecurityManager
from AccessControl import Unauthorized
@@ -24,7 +25,7 @@
from zope.annotation.interfaces import IAnnotations
from zope.component import queryUtility
-from zope.event import notify
+from zope.event import notify
safe_filenames = re.compile(r'.+?\.(exe|tar\.gz|bz2|egg|rpm|deb|zip|tgz)$', re.I)
@@ -36,7 +37,7 @@
'contactAddress': 'author_email',
'homepage': 'home_page',
}
-
+
RELEASE_MAP = {
'license': 'license',
# 'maturity': 'classifiers',
@@ -81,7 +82,7 @@ def _maybe_submit(self, project):
def _maybe_release(self, project, release):
"""Publishing the release, only of the project is published."""
if not self._is_published(project):
- return
+ return
wf = self._get_portal_workflow()
if wf is None:
return
@@ -94,13 +95,13 @@ def _maybe_release(self, project, release):
wf.doActionFor(release, 'release-alpha')
elif bool(re.compile("([0-9]+(-)?b([0-9]*))").search(self.data['version'])):
wf.doActionFor(release, 'release-beta')
- elif bool(re.compile("([0-9]+(-)?rc([0-9]*))").search(self.data['version'])):
+ elif bool(re.compile("([0-9]+(-)?rc([0-9]*))").search(self.data['version'])):
wf.doActionFor(release, 'release-candidate')
else:
wf.doActionFor(release, 'release-final')
except WorkflowException:
pass
-
+
def _validate_metadata(self, data):
""" Validate the contents of the metadata.
"""
@@ -145,7 +146,7 @@ def validate_version_predicates(col, sequence):
if data.has_key('classifiers'):
data['classifiers'] = filter(filter_, data['classifiers'])
-
+
def _is_published(self, project):
"""returns true if not publised"""
wf = self._get_portal_workflow()
@@ -168,7 +169,7 @@ def _store_package(self, name, version, data):
return self.fail(str(e), 401)
# Now, edit info
- self._edit_project(project, distutils_name=name, data=data,
+ self._edit_project(project, distutils_name=name, data=data,
msg=msg)
# Submit project if not submitted yet.
@@ -182,14 +183,14 @@ def _store_package(self, name, version, data):
if v in ('author_email',) and not value.startswith('mailto:'):
value = 'mailto:%s' % value
release_data[k] = value
-
+
if not user.allowed(release.update):
return self.fail('Unauthorized', 401)
msg.append('Updated Release: %s' % version)
release.update(**release_data)
-
+
# Now, check if there's a 'download_url', then create a file
# link
url = data.get('download_url')
@@ -221,19 +222,19 @@ def _store_package(self, name, version, data):
# notify we did add a file
notify(ObjectEditedEvent(rl))
-
+
# Make a release if not released yet.
self._maybe_release(project, release)
-
+
return '\n'.join(msg)
-
+
def _edit_project(self, project, distutils_name=None,
data=None, msg=None):
"""edit project infos"""
user = getSecurityManager().getUser()
if not user.allowed(project.update):
return self.fail('Unauthorized', 401)
-
+
if data is None:
data = self.data
project_data = {}
@@ -263,7 +264,7 @@ def _edit_project(self, project, distutils_name=None,
elif not (distutils_name == project.getDistutilsMainId()):
secondary_ids = project.getDistutilsSecondaryIds()
if distutils_name not in secondary_ids:
- secondary_ids = secondary_ids + (distutils_name,)
+ secondary_ids = secondary_ids + (distutils_name,)
project.setDistutilsSecondaryIds(secondary_ids)
else:
priority='m'
@@ -271,7 +272,7 @@ def _edit_project(self, project, distutils_name=None,
# setting the title in case it is empty
if project.Title() == '':
project_data['title'] = distutils_name
-
+
if priority == 'm':
project.update(**project_data)
@@ -298,7 +299,7 @@ def _get_package(self, normalized_name, name, version, msg=None):
if project.getDistutilsMainId() == '':
project.setDistutilsMainId(normalized_name)
else:
- # main id is taken, let's add it
+ # main id is taken, let's add it
# as a secondary id
secondids = project.getDistutilsSecondaryIds()
secondids = secondids + (normalized_name,)
@@ -315,7 +316,7 @@ def _get_package(self, normalized_name, name, version, msg=None):
else:
project_id = existing_projects[0]
project = sc[project_id]
-
+
self._edit_project(project, msg=msg, distutils_name=name)
versions = project.getVersionsVocab()
releases = project.getReleaseFolder()
@@ -327,7 +328,7 @@ def _get_package(self, normalized_name, name, version, msg=None):
i += 1
cid = '%s-%d' (root_id, i)
- releases = project.injectFolder('PSCReleaseFolder',
+ releases = project.injectFolder('PSCReleaseFolder',
id=cid)
is_secondary = name != project.distutilsMainId
@@ -347,8 +348,32 @@ def _get_package(self, normalized_name, name, version, msg=None):
(version, name))
release = releases._getOb(version)
+ self._map_classifiers_to_compatibility(project, release)
+
return project, release
+ def _map_classifiers_to_compatibility(self, project, release):
+ versions = []
+ CLASSIFIER_BASE = 'Framework :: Plone :: '
+ supported_versions = [classifier[len(CLASSIFIER_BASE):] for classifier in \
+ self.request.form.get('classifiers', [])\
+ if classifier.startswith(CLASSIFIER_BASE)]
+
+ for supported_version in supported_versions:
+ try:
+ Decimal(supported_version)
+ except InvalidOperation:
+ continue
+ versions.append('Plone %s' % supported_version)
+
+ vocab = release.getCompatibilityVocab()
+ compats = []
+ for version in versions:
+ if version in vocab:
+ compats.append(version)
+ if compats:
+ release.setCompatibility(compats)
+
def _get_classifiers(self):
"""returns current classifiers"""
sc = self.context
@@ -395,7 +420,7 @@ def verify(self, data=None):
"""
if getSecurityManager().getUser() in (nobody,):
return self.fail('Unauthorized', 401)
-
+
if data is None:
data = self.request.form
@@ -433,7 +458,7 @@ def file_upload(self, data=None):
name, version)
except Unauthorized, e:
return self.fail(str(e), 401)
-
+
# Submit project if not submitted yet.
self._maybe_submit(project)
@@ -461,9 +486,9 @@ def file_upload(self, data=None):
# XXX optimize for memory
content = content.read()
- # nothing over 5M please
+ # nothing over 100M please
size = len(content)
- if size > 5*1024*1024:
+ if size > 100*1024*1024:
err = 'Invalid file size: %s' % size
return self.fail(err)
@@ -502,7 +527,7 @@ def file_upload(self, data=None):
self._setPlatform(rf, filename)
# notify
notify(ObjectEditedEvent(rf))
-
+
# Make a release if not released yet.
self._maybe_release(project, release)
@@ -530,8 +555,8 @@ def normalizeName(self, text):
# Remove Products, as applicable
if text.startswith('Products.'):
text = text[9:]
-
+
# Convert to lowercase
text = text.lower()
-
+
return queryUtility(IFileNameNormalizer).normalize(text)
View
9 Products/PloneSoftwareCenter/browser/pypi_view.pt
@@ -0,0 +1,9 @@
+<div metal:use-macro="here/main_template/macros/master">
+
+ <div metal:fill-slot="main">
+
+ <h1>PyPI View</h1>
+
+ </div>
+
+</div>
View
13 Products/PloneSoftwareCenter/browser/pypi_view.py
@@ -0,0 +1,13 @@
+from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
+from zope.publisher.browser import BrowserPage
+
+
+class PackageIndexView(BrowserPage):
+ """
+ Display Plone releases from PyPI
+ """
+
+ template = ViewPageTemplateFile('pypi_view.pt')
+
+ def __call__(self):
+ return self.template()
View
1  Products/PloneSoftwareCenter/profiles/default/types/PloneSoftwareCenter.xml
@@ -22,6 +22,7 @@
<element value="plonesoftwarecenter_view"/>
<element value="plonesoftwarecenter_ploneorg"/>
<element value="psc_view_ploneorg"/>
+ <element value="pypi_view"/>
</property>
<property name="default_view_fallback">False</property>
<alias from="(Default)" to="(dynamic view)"/>
View
9 docs/HISTORY.txt
@@ -1,7 +1,13 @@
Changelog
=========
-1.6.4 (unreleased)
+1.6.5 (unreleased)
+------------------
+
+- Nothing changed yet.
+
+
+1.6.4 (2012-09-27)
------------------
- Default content and output format added to the StringField installation_instructions in content/root.py [andreasma]
- Change to psc_project_view.pt: more than one license will be displayed, if there is not only one for a release [andreasma]
@@ -11,6 +17,7 @@ Changelog
- Update classifiers [toutpt]
- Update TROVE.txt [toutpt]
- Update default available versions of Plone [toutpt]
+- Add mapping between classifiers and Plone release version when release using pypi api [toutpt]
1.6.3 (2011-06-19)
------------------
View
2  setup.py
@@ -1,7 +1,7 @@
import os
from setuptools import setup, find_packages
-version = '1.6.3'
+version = '1.6.5.dev0'
def read(*rnames):
Something went wrong with that request. Please try again.