launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #29054
[Merge] ~cjwatson/launchpad:stormify-specification into launchpad:master
Colin Watson has proposed merging ~cjwatson/launchpad:stormify-specification into launchpad:master.
Commit message:
Convert remaining lp.blueprints models to Storm
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/428840
This ports `Specification`, `SpecificationDependency`, and `SpecificationMessage` away from the deprecated SQLObject style.
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:stormify-specification into launchpad:master.
diff --git a/lib/lp/blueprints/doc/specification.rst b/lib/lp/blueprints/doc/specification.rst
index 47b6121..6c490cb 100644
--- a/lib/lp/blueprints/doc/specification.rst
+++ b/lib/lp/blueprints/doc/specification.rst
@@ -320,13 +320,13 @@ If there are dependencies between the specs, the method returns a
mapping between them.
>>> spec_a.createDependency(spec_b)
- <SpecificationDependency at ...>
+ <...SpecificationDependency object at ...>
>>> spec_a.createDependency(spec_c)
- <SpecificationDependency at ...>
+ <...SpecificationDependency object at ...>
>>> spec_c.createDependency(spec_d)
- <SpecificationDependency at ...>
+ <...SpecificationDependency object at ...>
>>> deps_dict = specset.getDependencyDict(
... [spec_a, spec_b, spec_c, spec_d])
diff --git a/lib/lp/blueprints/interfaces/specificationsubscription.py b/lib/lp/blueprints/interfaces/specificationsubscription.py
index c727957..eef862e 100644
--- a/lib/lp/blueprints/interfaces/specificationsubscription.py
+++ b/lib/lp/blueprints/interfaces/specificationsubscription.py
@@ -38,7 +38,6 @@ class ISpecificationSubscription(Interface):
)
personID = Attribute("db person value")
specification = Int(title=_("Specification"), required=True, readonly=True)
- specificationID = Attribute("db specification value")
essential = Bool(
title=_("Participation essential"),
required=True,
diff --git a/lib/lp/blueprints/model/specification.py b/lib/lp/blueprints/model/specification.py
index 707d397..cffe3d2 100644
--- a/lib/lp/blueprints/model/specification.py
+++ b/lib/lp/blueprints/model/specification.py
@@ -12,9 +12,23 @@ __all__ = [
import operator
+import pytz
from lazr.lifecycle.event import ObjectCreatedEvent
from lazr.lifecycle.objectdelta import ObjectDelta
-from storm.locals import SQL, Count, Desc, Join, Or, ReferenceSet, Store
+from storm.locals import (
+ SQL,
+ Bool,
+ Count,
+ DateTime,
+ Desc,
+ Int,
+ Join,
+ Or,
+ Reference,
+ ReferenceSet,
+ Store,
+ Unicode,
+)
from zope.component import getUtility
from zope.event import notify
from zope.interface import implementer
@@ -71,22 +85,13 @@ from lp.registry.interfaces.productseries import IProductSeries
from lp.registry.model.milestone import Milestone
from lp.services.database import bulk
from lp.services.database.constants import DEFAULT, UTC_NOW
-from lp.services.database.datetimecol import UtcDateTimeCol
from lp.services.database.enumcol import DBEnum
from lp.services.database.interfaces import IStore
from lp.services.database.sqlbase import (
- SQLBase,
convert_storm_clause_to_string,
sqlvalues,
)
-from lp.services.database.sqlobject import (
- BoolCol,
- ForeignKey,
- IntCol,
- SQLMultipleJoin,
- SQLRelatedJoin,
- StringCol,
-)
+from lp.services.database.stormbase import StormBase
from lp.services.mail.helpers import get_contact_email_addresses
from lp.services.propertycache import cachedproperty, get_property_cache
from lp.services.webapp.interfaces import ILaunchBag
@@ -160,15 +165,17 @@ SPECIFICATION_POLICY_DEFAULT_TYPES = {
@implementer(ISpecification, IBugLinkTarget, IInformationType)
-class Specification(SQLBase, BugLinkTargetMixin, InformationTypeMixin):
+class Specification(StormBase, BugLinkTargetMixin, InformationTypeMixin):
"""See ISpecification."""
- _defaultOrder = ["-priority", "definition_status", "name", "id"]
+ __storm_table__ = "Specification"
+ __storm_order__ = ("-priority", "definition_status", "name", "id")
# db field names
- name = StringCol(unique=True, notNull=True)
- title = StringCol(notNull=True)
- summary = StringCol(notNull=True)
+ id = Int(primary=True)
+ name = Unicode(allow_none=False)
+ title = Unicode(allow_none=False)
+ summary = Unicode(allow_none=False)
definition_status = DBEnum(
enum=SpecificationDefinitionStatus,
allow_none=False,
@@ -179,110 +186,94 @@ class Specification(SQLBase, BugLinkTargetMixin, InformationTypeMixin):
allow_none=False,
default=SpecificationPriority.UNDEFINED,
)
- _assignee = ForeignKey(
- dbName="assignee",
- notNull=False,
- foreignKey="Person",
- storm_validator=validate_public_person,
- default=None,
- )
- _drafter = ForeignKey(
- dbName="drafter",
- notNull=False,
- foreignKey="Person",
- storm_validator=validate_public_person,
- default=None,
- )
- _approver = ForeignKey(
- dbName="approver",
- notNull=False,
- foreignKey="Person",
- storm_validator=validate_public_person,
+ _assignee_id = Int(
+ name="assignee",
+ allow_none=True,
+ validator=validate_public_person,
default=None,
)
- owner = ForeignKey(
- dbName="owner",
- foreignKey="Person",
- storm_validator=validate_public_person,
- notNull=True,
- )
- datecreated = UtcDateTimeCol(notNull=True, default=DEFAULT)
- product = ForeignKey(
- dbName="product", foreignKey="Product", notNull=False, default=None
- )
- productseries = ForeignKey(
- dbName="productseries",
- foreignKey="ProductSeries",
- notNull=False,
+ _assignee = Reference(_assignee_id, "Person.id")
+ _drafter_id = Int(
+ name="drafter",
+ allow_none=True,
+ validator=validate_public_person,
default=None,
)
- distribution = ForeignKey(
- dbName="distribution",
- foreignKey="Distribution",
- notNull=False,
+ _drafter = Reference(_drafter_id, "Person.id")
+ _approver_id = Int(
+ name="approver",
+ allow_none=True,
+ validator=validate_public_person,
default=None,
)
- distroseries = ForeignKey(
- dbName="distroseries",
- foreignKey="DistroSeries",
- notNull=False,
- default=None,
+ _approver = Reference(_approver_id, "Person.id")
+ owner_id = Int(
+ name="owner", validator=validate_public_person, allow_none=False
)
+ owner = Reference(owner_id, "Person.id")
+ datecreated = DateTime(allow_none=False, default=DEFAULT, tzinfo=pytz.UTC)
+ product_id = Int(name="product", allow_none=True, default=None)
+ product = Reference(product_id, "Product.id")
+ productseries_id = Int(name="productseries", allow_none=True, default=None)
+ productseries = Reference(productseries_id, "ProductSeries.id")
+ distribution_id = Int(name="distribution", allow_none=True, default=None)
+ distribution = Reference(distribution_id, "Distribution.id")
+ distroseries_id = Int(name="distroseries", allow_none=True, default=None)
+ distroseries = Reference(distroseries_id, "DistroSeries.id")
goalstatus = DBEnum(
enum=SpecificationGoalStatus,
allow_none=False,
default=SpecificationGoalStatus.PROPOSED,
)
- goal_proposer = ForeignKey(
- dbName="goal_proposer",
- notNull=False,
- foreignKey="Person",
- storm_validator=validate_public_person,
+ goal_proposer_id = Int(
+ name="goal_proposer",
+ allow_none=True,
+ validator=validate_public_person,
default=None,
)
- date_goal_proposed = UtcDateTimeCol(notNull=False, default=None)
- goal_decider = ForeignKey(
- dbName="goal_decider",
- notNull=False,
- foreignKey="Person",
- storm_validator=validate_public_person,
+ goal_proposer = Reference(goal_proposer_id, "Person.id")
+ date_goal_proposed = DateTime(
+ allow_none=True, default=None, tzinfo=pytz.UTC
+ )
+ goal_decider_id = Int(
+ name="goal_decider",
+ allow_none=True,
+ validator=validate_public_person,
default=None,
)
- date_goal_decided = UtcDateTimeCol(notNull=False, default=None)
- milestone = ForeignKey(
- dbName="milestone", foreignKey="Milestone", notNull=False, default=None
+ goal_decider = Reference(goal_decider_id, "Person.id")
+ date_goal_decided = DateTime(
+ allow_none=True, default=None, tzinfo=pytz.UTC
)
- specurl = StringCol(notNull=False, default=None)
- whiteboard = StringCol(notNull=False, default=None)
- direction_approved = BoolCol(notNull=True, default=False)
- man_days = IntCol(notNull=False, default=None)
+ milestone_id = Int(name="milestone", allow_none=True, default=None)
+ milestone = Reference(milestone_id, "Milestone.id")
+ specurl = Unicode(allow_none=True, default=None)
+ whiteboard = Unicode(allow_none=True, default=None)
+ direction_approved = Bool(allow_none=False, default=False)
+ man_days = Int(allow_none=True, default=None)
implementation_status = DBEnum(
enum=SpecificationImplementationStatus,
allow_none=False,
default=SpecificationImplementationStatus.UNKNOWN,
)
- superseded_by = ForeignKey(
- dbName="superseded_by",
- foreignKey="Specification",
- notNull=False,
- default=None,
- )
- completer = ForeignKey(
- dbName="completer",
- notNull=False,
- foreignKey="Person",
- storm_validator=validate_public_person,
+ superseded_by_id = Int(name="superseded_by", allow_none=True, default=None)
+ superseded_by = Reference(superseded_by_id, "Specification.id")
+ completer_id = Int(
+ name="completer",
+ allow_none=True,
+ validator=validate_public_person,
default=None,
)
- date_completed = UtcDateTimeCol(notNull=False, default=None)
- starter = ForeignKey(
- dbName="starter",
- notNull=False,
- foreignKey="Person",
- storm_validator=validate_public_person,
+ completer = Reference(completer_id, "Person.id")
+ date_completed = DateTime(allow_none=True, default=None, tzinfo=pytz.UTC)
+ starter_id = Int(
+ name="starter",
+ allow_none=True,
+ validator=validate_public_person,
default=None,
)
- date_started = UtcDateTimeCol(notNull=False, default=None)
+ starter = Reference(starter_id, "Person.id")
+ date_started = DateTime(allow_none=True, default=None, tzinfo=pytz.UTC)
# useful joins
_subscriptions = ReferenceSet(
@@ -298,32 +289,59 @@ class Specification(SQLBase, BugLinkTargetMixin, InformationTypeMixin):
order_by=("Person.display_name", "Person.name"),
)
sprint_links = ReferenceSet(
- "<primary key>",
+ "id",
"SprintSpecification.specification_id",
order_by="SprintSpecification.id",
)
sprints = ReferenceSet(
- "<primary key>",
+ "id",
"SprintSpecification.specification_id",
"SprintSpecification.sprint_id",
"Sprint.id",
order_by="Sprint.name",
)
- spec_dependency_links = SQLMultipleJoin(
- "SpecificationDependency", joinColumn="specification", orderBy="id"
+ spec_dependency_links = ReferenceSet(
+ "id",
+ "SpecificationDependency.specification_id",
+ order_by="SpecificationDependency.id",
)
- dependencies = SQLRelatedJoin(
- "Specification",
- joinColumn="specification",
- otherColumn="dependency",
- orderBy="title",
- intermediateTable="SpecificationDependency",
+ dependencies = ReferenceSet(
+ "id",
+ "SpecificationDependency.specification_id",
+ "SpecificationDependency.dependency_id",
+ "Specification.id",
+ order_by="Specification.title",
)
information_type = DBEnum(
enum=InformationType, allow_none=False, default=InformationType.PUBLIC
)
+ def __init__(
+ self,
+ name,
+ title,
+ summary,
+ owner,
+ definition_status=DEFAULT,
+ assignee=None,
+ drafter=None,
+ approver=None,
+ specurl=None,
+ whiteboard=None,
+ ):
+ super().__init__()
+ self.name = name
+ self.title = title
+ self.summary = summary
+ self.owner = owner
+ self.definition_status = definition_status
+ self._assignee = assignee
+ self._drafter = drafter
+ self._approver = approver
+ self.specurl = specurl
+ self.whiteboard = whiteboard
+
@cachedproperty
def linked_branches(self):
return list(
@@ -355,44 +373,44 @@ class Specification(SQLBase, BugLinkTargetMixin, InformationTypeMixin):
def getDependencies(self, user=None):
return self._fetch_children_or_parents(
- SpecificationDependency.specificationID,
- SpecificationDependency.dependencyID,
+ SpecificationDependency.specification_id,
+ SpecificationDependency.dependency_id,
user,
)
def getBlockedSpecs(self, user=None):
return self._fetch_children_or_parents(
- SpecificationDependency.dependencyID,
- SpecificationDependency.specificationID,
+ SpecificationDependency.dependency_id,
+ SpecificationDependency.specification_id,
user,
)
- def set_assignee(self, person):
- self.subscribeIfAccessGrantNeeded(person)
- self._assignee = person
-
- def get_assignee(self):
+ @property
+ def assignee(self):
return self._assignee
- assignee = property(get_assignee, set_assignee)
-
- def set_drafter(self, person):
+ @assignee.setter
+ def assignee(self, person):
self.subscribeIfAccessGrantNeeded(person)
- self._drafter = person
+ self._assignee = person
- def get_drafter(self):
+ @property
+ def drafter(self):
return self._drafter
- drafter = property(get_drafter, set_drafter)
-
- def set_approver(self, person):
+ @drafter.setter
+ def drafter(self, person):
self.subscribeIfAccessGrantNeeded(person)
- self._approver = person
+ self._drafter = person
- def get_approver(self):
+ @property
+ def approver(self):
return self._approver
- approver = property(get_approver, set_approver)
+ @approver.setter
+ def approver(self, person):
+ self.subscribeIfAccessGrantNeeded(person)
+ self._approver = person
def subscribeIfAccessGrantNeeded(self, person):
"""Subscribe person if this specification is not public and if
@@ -777,22 +795,22 @@ class Specification(SQLBase, BugLinkTargetMixin, InformationTypeMixin):
"""See ISpecification."""
newstatus = None
if self.is_started:
- if self.starterID is None:
+ if self.starter_id is None:
newstatus = SpecificationLifecycleStatus.STARTED
self.date_started = UTC_NOW
self.starter = user
else:
- if self.starterID is not None:
+ if self.starter_id is not None:
newstatus = SpecificationLifecycleStatus.NOTSTARTED
self.date_started = None
self.starter = None
if self.is_complete:
- if self.completerID is None:
+ if self.completer_id is None:
newstatus = SpecificationLifecycleStatus.COMPLETE
self.date_completed = UTC_NOW
self.completer = user
else:
- if self.completerID is not None:
+ if self.completer_id is not None:
self.date_completed = None
self.completer = None
if self.is_started:
@@ -1025,8 +1043,8 @@ class Specification(SQLBase, BugLinkTargetMixin, InformationTypeMixin):
# see if a relevant dependency link exists, and if so, delete it
for deplink in self.spec_dependency_links:
if deplink.dependency.id == specification.id:
- SpecificationDependency.delete(deplink.id)
- return deplink
+ Store.of(deplink).remove(deplink)
+ return
def all_deps(self, user=None):
return list(
@@ -1253,7 +1271,7 @@ class SpecificationSet(HasSpecificationsMixin):
(Specification.implementation_status, Count()),
Or(
Specification.productseries == product_series,
- Specification.milestoneID.is_in(
+ Specification.milestone_id.is_in(
list(
product_series.all_milestones.values(Milestone.id)
)
@@ -1291,15 +1309,15 @@ class SpecificationSet(HasSpecificationsMixin):
def getByURL(self, url):
"""See ISpecificationSet."""
- return Specification.selectOneBy(specurl=url)
+ return IStore(Specification).find(Specification, specurl=url).one()
def getByName(self, pillar, name):
"""See ISpecificationSet."""
clauses = [Specification.name == name]
if IDistribution.providedBy(pillar):
- clauses.append(Specification.distributionID == pillar.id)
+ clauses.append(Specification.distribution == pillar)
elif IProduct.providedBy(pillar):
- clauses.append(Specification.productID == pillar.id)
+ clauses.append(Specification.product == pillar)
return IStore(Specification).find(Specification, *clauses).one()
@property
@@ -1349,9 +1367,9 @@ class SpecificationSet(HasSpecificationsMixin):
summary=summary,
definition_status=definition_status,
owner=owner,
- _approver=approver,
- _assignee=assignee,
- _drafter=drafter,
+ approver=approver,
+ assignee=assignee,
+ drafter=drafter,
whiteboard=whiteboard,
)
spec.setTarget(target)
@@ -1386,14 +1404,14 @@ class SpecificationSet(HasSpecificationsMixin):
for spec_id, dep_id in results:
if spec_id not in dependencies:
dependencies[spec_id] = []
- dependency = Specification.get(dep_id)
+ dependency = IStore(Specification).get(Specification, dep_id)
dependencies[spec_id].append(dependency)
return dependencies
def get(self, spec_id):
"""See lp.blueprints.interfaces.specification.ISpecificationSet."""
- return Specification.get(spec_id)
+ return IStore(Specification).get(Specification, spec_id)
def empty_list(self):
"""See `ISpecificationSet`."""
diff --git a/lib/lp/blueprints/model/specificationdependency.py b/lib/lp/blueprints/model/specificationdependency.py
index 365b4be..0a90436 100644
--- a/lib/lp/blueprints/model/specificationdependency.py
+++ b/lib/lp/blueprints/model/specificationdependency.py
@@ -3,23 +3,30 @@
__all__ = ["SpecificationDependency"]
+from storm.locals import Int, Reference
from zope.interface import implementer
from lp.blueprints.interfaces.specificationdependency import (
ISpecificationDependency,
)
-from lp.services.database.sqlbase import SQLBase
-from lp.services.database.sqlobject import ForeignKey
+from lp.services.database.stormbase import StormBase
@implementer(ISpecificationDependency)
-class SpecificationDependency(SQLBase):
+class SpecificationDependency(StormBase):
"""A link between a spec and a bug."""
- _table = "SpecificationDependency"
- specification = ForeignKey(
- dbName="specification", foreignKey="Specification", notNull=True
- )
- dependency = ForeignKey(
- dbName="dependency", foreignKey="Specification", notNull=True
- )
+ __storm_table__ = "SpecificationDependency"
+
+ id = Int(primary=True)
+
+ specification_id = Int(name="specification", allow_none=False)
+ specification = Reference(specification_id, "Specification.id")
+
+ dependency_id = Int(name="dependency", allow_none=False)
+ dependency = Reference(dependency_id, "Specification.id")
+
+ def __init__(self, specification, dependency):
+ super().__init__()
+ self.specification = specification
+ self.dependency = dependency
diff --git a/lib/lp/blueprints/model/specificationmessage.py b/lib/lp/blueprints/model/specificationmessage.py
index 6edb0e7..3603a9e 100644
--- a/lib/lp/blueprints/model/specificationmessage.py
+++ b/lib/lp/blueprints/model/specificationmessage.py
@@ -5,28 +5,38 @@ __all__ = ["SpecificationMessage", "SpecificationMessageSet"]
from email.utils import make_msgid
+from storm.locals import Bool, Int, Reference
from zope.interface import implementer
from lp.blueprints.interfaces.specificationmessage import (
ISpecificationMessage,
ISpecificationMessageSet,
)
-from lp.services.database.sqlbase import SQLBase
-from lp.services.database.sqlobject import BoolCol, ForeignKey
+from lp.services.database.interfaces import IStore
+from lp.services.database.stormbase import StormBase
from lp.services.messages.model.message import Message, MessageChunk
@implementer(ISpecificationMessage)
-class SpecificationMessage(SQLBase):
+class SpecificationMessage(StormBase):
"""A table linking specifications and messages."""
- _table = "SpecificationMessage"
+ __storm_table__ = "SpecificationMessage"
- specification = ForeignKey(
- dbName="specification", foreignKey="Specification", notNull=True
- )
- message = ForeignKey(dbName="message", foreignKey="Message", notNull=True)
- visible = BoolCol(notNull=True, default=True)
+ id = Int(primary=True)
+
+ specification_id = Int(name="specification", allow_none=False)
+ specification = Reference(specification_id, "Specification.id")
+
+ message_id = Int(name="message", allow_none=False)
+ message = Reference(message_id, "Message.id")
+
+ visible = Bool(allow_none=False, default=True)
+
+ def __init__(self, specification, message):
+ super().__init__()
+ self.specification = specification
+ self.message = message
@implementer(ISpecificationMessageSet)
@@ -39,8 +49,12 @@ class SpecificationMessageSet:
owner=owner, rfc822msgid=make_msgid("blueprint"), subject=subject
)
MessageChunk(message=msg, content=content, sequence=1)
- return SpecificationMessage(specification=spec, message=msg)
+ specmessage = SpecificationMessage(specification=spec, message=msg)
+ IStore(SpecificationMessage).flush()
+ return specmessage
def get(self, specmessageid):
"""See ISpecificationMessageSet."""
- return SpecificationMessage.get(specmessageid)
+ return IStore(SpecificationMessage).get(
+ SpecificationMessage, specmessageid
+ )
diff --git a/lib/lp/blueprints/model/specificationsearch.py b/lib/lp/blueprints/model/specificationsearch.py
index 2f00a54..f505628 100644
--- a/lib/lp/blueprints/model/specificationsearch.py
+++ b/lib/lp/blueprints/model/specificationsearch.py
@@ -153,9 +153,9 @@ def search_specifications(
for spec in rows:
if need_people:
person_ids |= {
- spec._assigneeID,
- spec._approverID,
- spec._drafterID,
+ spec._assignee_id,
+ spec._approver_id,
+ spec._drafter_id,
}
if need_branches:
get_property_cache(spec).linked_branches = []
@@ -215,7 +215,7 @@ def get_specification_active_product_filter(context):
return [], []
from lp.registry.model.product import Product
- tables = [LeftJoin(Product, Specification.productID == Product.id)]
+ tables = [LeftJoin(Product, Specification.product_id == Product.id)]
active_products = Or(Specification.product == None, Product.active == True)
return tables, [active_products]
diff --git a/lib/lp/blueprints/tests/test_specification.py b/lib/lp/blueprints/tests/test_specification.py
index fa7404e..62fbea3 100644
--- a/lib/lp/blueprints/tests/test_specification.py
+++ b/lib/lp/blueprints/tests/test_specification.py
@@ -586,7 +586,7 @@ class SpecificationTests(TestCaseWithFactory):
def _fetch_specs_visible_for_user(self, user):
return Store.of(self.product).find(
Specification,
- Specification.productID == self.product.id,
+ Specification.product == self.product,
*get_specification_privacy_filter(user),
)
diff --git a/lib/lp/blueprints/tests/test_specification_access_policy_triggers.py b/lib/lp/blueprints/tests/test_specification_access_policy_triggers.py
index e192cb7..2ba7153 100644
--- a/lib/lp/blueprints/tests/test_specification_access_policy_triggers.py
+++ b/lib/lp/blueprints/tests/test_specification_access_policy_triggers.py
@@ -17,15 +17,14 @@ class TestSpecificationAccessPolicyTriggers(TestCaseWithFactory):
def fetchPolicies(self, specification):
# We may be dealing with private specs, so just ignore security.
- return (
- IStore(Specification)
- .execute(
- "SELECT access_policy, access_grants FROM specification WHERE "
- "id = ?",
- (removeSecurityProxy(specification).id,),
- )
- .get_one()
- )
+ store = IStore(Specification)
+ # Ensure that the specification's ID is available.
+ store.flush()
+ return store.execute(
+ "SELECT access_policy, access_grants FROM specification WHERE "
+ "id = ?",
+ (removeSecurityProxy(specification).id,),
+ ).get_one()
def assertAccess(self, specification, expected_policy, expected_grants):
policy, grants = self.fetchPolicies(specification)
diff --git a/lib/lp/blueprints/vocabularies/specification.py b/lib/lp/blueprints/vocabularies/specification.py
index 55526cc..68a6a51 100644
--- a/lib/lp/blueprints/vocabularies/specification.py
+++ b/lib/lp/blueprints/vocabularies/specification.py
@@ -14,16 +14,16 @@ from zope.schema.vocabulary import SimpleTerm
from lp.blueprints.model.specification import Specification
from lp.services.webapp.interfaces import ILaunchBag
-from lp.services.webapp.vocabulary import SQLObjectVocabularyBase
+from lp.services.webapp.vocabulary import StormVocabularyBase
-class SpecificationVocabulary(SQLObjectVocabularyBase):
+class SpecificationVocabulary(StormVocabularyBase):
"""List specifications for the current product or distribution in
ILaunchBag, EXCEPT for the current spec in LaunchBag if one exists.
"""
_table = Specification
- _orderBy = "title"
+ _order_by = "title"
def __iter__(self):
launchbag = getUtility(ILaunchBag)
diff --git a/lib/lp/blueprints/vocabularies/specificationdependency.py b/lib/lp/blueprints/vocabularies/specificationdependency.py
index de5b89c..d458c05 100644
--- a/lib/lp/blueprints/vocabularies/specificationdependency.py
+++ b/lib/lp/blueprints/vocabularies/specificationdependency.py
@@ -8,7 +8,7 @@ __all__ = [
"SpecificationDependenciesVocabulary",
]
-from storm.locals import SQL, And, Store
+from storm.locals import SQL, Store
from zope.component import getUtility
from zope.interface import implementer
from zope.schema.vocabulary import SimpleTerm
@@ -28,12 +28,12 @@ from lp.services.webapp.interfaces import ILaunchBag
from lp.services.webapp.vocabulary import (
CountableIterator,
IHugeVocabulary,
- SQLObjectVocabularyBase,
+ StormVocabularyBase,
)
@implementer(IHugeVocabulary)
-class SpecificationDepCandidatesVocabulary(SQLObjectVocabularyBase):
+class SpecificationDepCandidatesVocabulary(StormVocabularyBase):
"""Specifications that could be dependencies of this spec.
This includes only those specs that are not blocked by this spec (directly
@@ -54,7 +54,7 @@ class SpecificationDepCandidatesVocabulary(SQLObjectVocabularyBase):
"""
_table = Specification
- _orderBy = "name"
+ _order_by = "name"
displayname = "Select a blueprint"
step_title = "Search"
@@ -74,7 +74,7 @@ class SpecificationDepCandidatesVocabulary(SQLObjectVocabularyBase):
user = getattr(getUtility(ILaunchBag), "user", None)
return spec not in set(self.context.all_blocked(user=user))
- def _order_by(self):
+ def _order_search_by(self):
"""Look at the context to provide grouping.
If the blueprint is for a project, then matching results for that
@@ -187,7 +187,7 @@ class SpecificationDepCandidatesVocabulary(SQLObjectVocabularyBase):
fti_search(Specification, query),
self._exclude_blocked_query(),
)
- .order_by(self._order_by())
+ .order_by(self._order_search_by())
)
def __iter__(self):
@@ -198,17 +198,17 @@ class SpecificationDepCandidatesVocabulary(SQLObjectVocabularyBase):
return self._is_valid_candidate(obj)
-class SpecificationDependenciesVocabulary(SQLObjectVocabularyBase):
+class SpecificationDependenciesVocabulary(StormVocabularyBase):
"""List specifications on which the current specification depends."""
_table = Specification
- _orderBy = "title"
+ _order_by = "title"
@property
- def _filter(self):
+ def _clauses(self):
user = getattr(getUtility(ILaunchBag), "user", None)
- return And(
- SpecificationDependency.specificationID == self.context.id,
- SpecificationDependency.dependencyID == Specification.id,
+ return [
+ SpecificationDependency.specification == self.context,
+ SpecificationDependency.dependency_id == Specification.id,
*get_specification_privacy_filter(user),
- )
+ ]
diff --git a/lib/lp/registry/browser/__init__.py b/lib/lp/registry/browser/__init__.py
index 403345b..5cad743 100644
--- a/lib/lp/registry/browser/__init__.py
+++ b/lib/lp/registry/browser/__init__.py
@@ -244,7 +244,9 @@ class RegistryDeleteViewMixin:
Store.of(bugtask).remove(nb.conjoined_primary)
else:
nb.milestone = None
- removeSecurityProxy(milestone.all_specifications).set(milestoneID=None)
+ removeSecurityProxy(milestone.all_specifications).set(
+ milestone_id=None
+ )
getUtility(ISpecificationWorkItemSet).unlinkMilestone(milestone)
self._deleteRelease(milestone.product_release)
milestone.destroySelf()
diff --git a/lib/lp/registry/doc/person-account.rst b/lib/lp/registry/doc/person-account.rst
index 0ed70c6..f5e2424 100644
--- a/lib/lp/registry/doc/person-account.rst
+++ b/lib/lp/registry/doc/person-account.rst
@@ -76,7 +76,10 @@ will cause this spec to be reassigned.
>>> from lp.blueprints.model.specification import Specification
>>> from lp.registry.model.person import Person
- >>> spec = Specification.selectFirst("assignee IS NULL", orderBy='id')
+ >>> from lp.services.database.interfaces import IStore
+ >>> spec = IStore(Specification).find(
+ ... Specification, _assignee=None
+ ... ).order_by("id").first()
>>> spec.assignee = foobar
>>> for membership in foobar.team_memberships:
diff --git a/lib/lp/registry/doc/productseries.rst b/lib/lp/registry/doc/productseries.rst
index 18d0c9c..44d6516 100644
--- a/lib/lp/registry/doc/productseries.rst
+++ b/lib/lp/registry/doc/productseries.rst
@@ -201,25 +201,27 @@ is informational.
We will create two specs for onezero and use them to demonstrate the
filtering.
- >>> from lp.services.database.constants import UTC_NOW
+ >>> from lp.blueprints.enums import SpecificationDefinitionStatus
+ >>> from lp.blueprints.interfaces.specification import ISpecificationSet
>>> carlos = getUtility(IPersonSet).getByName('carlos')
- >>> from lp.blueprints.model.specification import Specification
- >>> a = Specification(name='a', title='A', summary='AA', owner=carlos,
- ... product=firefox, productseries=onezero,
- ... specurl='http://wbc.com/two', goal_proposer=carlos,
- ... date_goal_proposed=UTC_NOW)
- >>> b = Specification(name='b', title='b', summary='bb', owner=carlos,
- ... product=firefox, productseries=onezero,
- ... specurl='http://fds.com/adsf', goal_proposer=carlos,
- ... date_goal_proposed=UTC_NOW)
+ >>> _ = login_person(carlos)
+ >>> a = getUtility(ISpecificationSet).new(
+ ... name="a", title="A", specurl="http://wbc.com/two", summary="AA",
+ ... definition_status=SpecificationDefinitionStatus.NEW, owner=carlos,
+ ... target=firefox,
+ ... )
+ >>> a.proposeGoal(onezero, carlos)
+ >>> b = getUtility(ISpecificationSet).new(
+ ... name="b", title="b", specurl="http://fds.com/adsf", summary="bb",
+ ... definition_status=SpecificationDefinitionStatus.NEW, owner=carlos,
+ ... target=firefox,
+ ... )
+ >>> b.proposeGoal(onezero, carlos)
Now, we will make one of them accepted, the other declined, and both of
them informational.
- >>> from lp.blueprints.enums import (
- ... SpecificationDefinitionStatus,
- ... SpecificationImplementationStatus,
- ... )
+ >>> from lp.blueprints.enums import SpecificationImplementationStatus
>>> a.definition_status = b.definition_status = (
... SpecificationDefinitionStatus.APPROVED)
>>> a.implementation_status = (
diff --git a/lib/lp/registry/model/distribution.py b/lib/lp/registry/model/distribution.py
index 3dd493e..d8ed65c 100644
--- a/lib/lp/registry/model/distribution.py
+++ b/lib/lp/registry/model/distribution.py
@@ -1404,7 +1404,7 @@ class Distribution(
- informationalness: we will show ANY if nothing is said
"""
- base_clauses = [Specification.distributionID == self.id]
+ base_clauses = [Specification.distribution == self]
return search_specifications(
self,
base_clauses,
@@ -1419,7 +1419,11 @@ class Distribution(
def getSpecification(self, name):
"""See `ISpecificationTarget`."""
- return Specification.selectOneBy(distribution=self, name=name)
+ return (
+ IStore(Specification)
+ .find(Specification, distribution=self, name=name)
+ .one()
+ )
def getAllowedSpecificationInformationTypes(self):
"""See `ISpecificationTarget`."""
diff --git a/lib/lp/registry/model/distroseries.py b/lib/lp/registry/model/distroseries.py
index c3f912d..41f4e9a 100644
--- a/lib/lp/registry/model/distroseries.py
+++ b/lib/lp/registry/model/distroseries.py
@@ -974,7 +974,7 @@ class DistroSeries(
- informationalness: if nothing is said, ANY
"""
- base_clauses = [Specification.distroseriesID == self.id]
+ base_clauses = [Specification.distroseries == self]
return search_specifications(
self,
base_clauses,
diff --git a/lib/lp/registry/model/milestone.py b/lib/lp/registry/model/milestone.py
index 0171a61..f7b1abb 100644
--- a/lib/lp/registry/model/milestone.py
+++ b/lib/lp/registry/model/milestone.py
@@ -147,7 +147,7 @@ class MilestoneData:
from lp.blueprints.model.specification import Specification
return Store.of(self).find(
- Specification, Specification.milestoneID == self.id
+ Specification, Specification.milestone == self
)
def getSpecifications(self, user):
@@ -166,7 +166,9 @@ class MilestoneData:
product_origin, clauses = get_specification_active_product_filter(self)
origin.extend(product_origin)
clauses.extend(get_specification_privacy_filter(user))
- origin.append(LeftJoin(Person, Specification._assigneeID == Person.id))
+ origin.append(
+ LeftJoin(Person, Specification._assignee_id == Person.id)
+ )
milestones = self._milestone_ids_expr(user)
results = (
@@ -180,7 +182,7 @@ class MilestoneData:
Specification.id,
tables=[Specification],
where=(
- Specification.milestoneID.is_in(milestones)
+ Specification.milestone_id.is_in(milestones)
),
),
Select(
diff --git a/lib/lp/registry/model/person.py b/lib/lp/registry/model/person.py
index db8a777..9239372 100644
--- a/lib/lp/registry/model/person.py
+++ b/lib/lp/registry/model/person.py
@@ -1630,7 +1630,7 @@ class Person(
SpecificationWorkItem.specification_id.is_in(
Select(
Specification.id,
- where=Specification._assigneeID.is_in(
+ where=Specification._assignee_id.is_in(
self.participant_ids
),
)
@@ -1670,7 +1670,7 @@ class Person(
Milestone,
Coalesce(
SpecificationWorkItem.milestone_id,
- Specification.milestoneID,
+ Specification.milestone_id,
)
== Milestone.id,
),
@@ -1697,17 +1697,17 @@ class Person(
specs = bulk.load_related(
Specification, workitems, ["specification_id"]
)
- bulk.load_related(Product, specs, ["productID"])
- bulk.load_related(Distribution, specs, ["distributionID"])
+ bulk.load_related(Product, specs, ["product_id"])
+ bulk.load_related(Distribution, specs, ["distribution_id"])
assignee_ids = set(
[workitem.assignee_id for workitem in workitems]
- + [spec._assigneeID for spec in specs]
+ + [spec._assignee_id for spec in specs]
)
assignee_ids.discard(None)
bulk.load(Person, assignee_ids, store)
milestone_ids = set(
[workitem.milestone_id for workitem in workitems]
- + [spec.milestoneID for spec in specs]
+ + [spec.milestone_id for spec in specs]
)
milestone_ids.discard(None)
bulk.load(Milestone, milestone_ids, store)
diff --git a/lib/lp/registry/model/product.py b/lib/lp/registry/model/product.py
index 357e5c4..3cae421 100644
--- a/lib/lp/registry/model/product.py
+++ b/lib/lp/registry/model/product.py
@@ -1418,7 +1418,7 @@ class Product(
need_workitems=False,
):
"""See `IHasSpecifications`."""
- base_clauses = [Specification.productID == self.id]
+ base_clauses = [Specification.product == self]
return search_specifications(
self,
base_clauses,
@@ -1433,7 +1433,11 @@ class Product(
def getSpecification(self, name):
"""See `ISpecificationTarget`."""
- return Specification.selectOneBy(product=self, name=name)
+ return (
+ IStore(Specification)
+ .find(Specification, product=self, name=name)
+ .one()
+ )
def getSeries(self, name):
"""See `IProduct`."""
diff --git a/lib/lp/registry/model/productseries.py b/lib/lp/registry/model/productseries.py
index 16cbb10..0edd632 100644
--- a/lib/lp/registry/model/productseries.py
+++ b/lib/lp/registry/model/productseries.py
@@ -348,7 +348,7 @@ class ProductSeries(
- informational, which defaults to showing BOTH if nothing is said
"""
- base_clauses = [Specification.productseriesID == self.id]
+ base_clauses = [Specification.productseries == self]
return search_specifications(
self,
base_clauses,
@@ -365,7 +365,7 @@ class ProductSeries(
@property
def all_specifications(self):
return Store.of(self).find(
- Specification, Specification.productseriesID == self.id
+ Specification, Specification.productseries == self
)
def _customizeSearchParams(self, search_params):
diff --git a/lib/lp/registry/model/projectgroup.py b/lib/lp/registry/model/projectgroup.py
index b50b11c..0dda2e0 100644
--- a/lib/lp/registry/model/projectgroup.py
+++ b/lib/lp/registry/model/projectgroup.py
@@ -287,7 +287,7 @@ class ProjectGroup(
):
"""See `IHasSpecifications`."""
base_clauses = [
- Specification.productID == Product.id,
+ Specification.product_id == Product.id,
Product.projectgroupID == self.id,
]
tables = [Specification]
@@ -296,7 +296,7 @@ class ProjectGroup(
tables.append(
Join(
ProductSeries,
- Specification.productseriesID == ProductSeries.id,
+ Specification.productseries_id == ProductSeries.id,
)
)
return search_specifications(
diff --git a/lib/lp/registry/services/sharingservice.py b/lib/lp/registry/services/sharingservice.py
index 5548b4a..b628808 100644
--- a/lib/lp/registry/services/sharingservice.py
+++ b/lib/lp/registry/services/sharingservice.py
@@ -435,9 +435,9 @@ class SharingService:
AccessPolicy,
And(
Or(
- Specification.distributionID
+ Specification.distribution_id
== AccessPolicy.distribution_id,
- Specification.productID == AccessPolicy.product_id,
+ Specification.product_id == AccessPolicy.product_id,
),
AccessPolicy.type == Specification.information_type,
),
diff --git a/lib/lp/registry/tests/test_person.py b/lib/lp/registry/tests/test_person.py
index 7fa6c28..2fa9c5c 100644
--- a/lib/lp/registry/tests/test_person.py
+++ b/lib/lp/registry/tests/test_person.py
@@ -1027,7 +1027,7 @@ class TestPersonStates(TestCaseWithFactory):
)
def test_Specification_person_validator(self):
- specification = Specification.select(limit=1)[0]
+ specification = IStore(Specification).find(Specification).first()
for attr_name in [
"assignee",
"drafter",
diff --git a/lib/lp/registry/tests/test_sharingjob.py b/lib/lp/registry/tests/test_sharingjob.py
index 6c7c65a..e017e72 100644
--- a/lib/lp/registry/tests/test_sharingjob.py
+++ b/lib/lp/registry/tests/test_sharingjob.py
@@ -159,6 +159,7 @@ class SharingJobDerivedTestCase(TestCaseWithFactory):
def test_repr_specifications(self):
requestor = self.factory.makePerson()
specification = self.factory.makeSpecification()
+ IStore(specification).flush()
job = getUtility(IRemoveArtifactSubscriptionsJobSource).create(
requestor, artifacts=[specification]
)
diff --git a/lib/lp/scripts/harness.py b/lib/lp/scripts/harness.py
index c33077c..4bf8084 100644
--- a/lib/lp/scripts/harness.py
+++ b/lib/lp/scripts/harness.py
@@ -81,8 +81,8 @@ def _get_locals():
proj = ProjectGroup.get(1)
b2 = Bug.get(2)
b1 = Bug.get(1)
- s = Specification.get(1)
- q = Question.get(1)
+ s = store.get(Specification, 1)
+ q = store.get(Question, 1)
# Silence unused name warnings
d, p, ds, prod, proj, b2, b1, s, q