class Role(db.Model): __tablename__ = "roles" __table_args__ = ( Index("roles_user_id_idx", "user_id"), Index("roles_project_id_idx", "project_id"), UniqueConstraint("user_id", "project_id", name="_roles_user_project_uc"), ) __repr__ = make_repr("role_name") role_name = Column(Text, nullable=False) user_id = Column(ForeignKey("users.id", onupdate="CASCADE", ondelete="CASCADE"), nullable=False) project_id = Column( ForeignKey("projects.id", onupdate="CASCADE", ondelete="CASCADE"), nullable=False, ) user = orm.relationship(User, lazy=False) project = orm.relationship("Project", lazy=False)
def __repr__(self): inst = inspect(self) self.__repr__ = make_repr( *[c_attr.key for c_attr in inst.mapper.column_attrs], _self=self, ) return self.__repr__()
class RoleInvitation(db.Model): __tablename__ = "role_invitations" __table_args__ = ( Index("role_invitations_user_id_idx", "user_id"), UniqueConstraint("user_id", "project_id", name="_role_invitations_user_project_uc"), ) __repr__ = make_repr("invite_status", "user", "project") invite_status = Column( Enum(RoleInvitationStatus, values_callable=lambda x: [e.value for e in x]), nullable=False, ) token = Column(Text, nullable=False) user_id = Column(ForeignKey("users.id", onupdate="CASCADE", ondelete="CASCADE"), nullable=False) project_id = Column( ForeignKey("projects.id", onupdate="CASCADE", ondelete="CASCADE"), nullable=False, ) user = orm.relationship(User, lazy=False) project = orm.relationship("Project", lazy=False)
class OrganizationInvitation(db.Model): __tablename__ = "organization_invitations" __table_args__ = ( Index("organization_invitations_user_id_idx", "user_id"), UniqueConstraint( "user_id", "organization_id", name="_organization_invitations_user_organization_uc", ), ) __repr__ = make_repr("invite_status", "user", "organization") invite_status = Column( Enum(OrganizationInvitationStatus, values_callable=lambda x: [e.value for e in x]), nullable=False, ) token = Column(Text, nullable=False) user_id = Column( ForeignKey("users.id", onupdate="CASCADE", ondelete="CASCADE"), nullable=False, index=True, ) organization_id = Column( ForeignKey("organizations.id", onupdate="CASCADE", ondelete="CASCADE"), nullable=False, index=True, ) user = orm.relationship(User, lazy=False) organization = orm.relationship("Organization", lazy=False)
class User(SitemapMixin, db.Model): __tablename__ = "users" __table_args__ = ( CheckConstraint("length(username) <= 50", name="users_valid_username_length"), CheckConstraint( "username ~* '^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$'", name="users_valid_username", ), ) __repr__ = make_repr("username") username = Column(CIText, nullable=False, unique=True) name = Column(String(length=100), nullable=False) password = Column(String(length=128), nullable=False) password_date = Column(DateTime, nullable=True, server_default=sql.func.now()) is_active = Column(Boolean, nullable=False, server_default=sql.false()) is_superuser = Column(Boolean, nullable=False, server_default=sql.false()) is_moderator = Column(Boolean, nullable=False, server_default=sql.false()) date_joined = Column(DateTime, server_default=sql.func.now()) last_login = Column(DateTime, nullable=False, server_default=sql.func.now()) disabled_for = Column( Enum(DisableReason, values_callable=lambda x: [e.value for e in x]), nullable=True, ) totp_secret = Column(Binary(length=20), nullable=True) emails = orm.relationship( "Email", backref="user", cascade="all, delete-orphan", lazy=False ) @property def primary_email(self): primaries = [x for x in self.emails if x.primary] if primaries: return primaries[0] @hybrid_property def email(self): primary_email = self.primary_email return primary_email.email if primary_email else None @email.expression def email(self): return ( select([Email.email]) .where((Email.user_id == self.id) & (Email.primary.is_(True))) .as_scalar() ) @property def has_two_factor(self): # TODO: This is where user.u2f_provisioned et al. # will also go. return self.totp_secret is not None @property def two_factor_provisioning_allowed(self): return self.primary_email is not None and self.primary_email.verified
class OrganizationRole(db.Model): __tablename__ = "organization_roles" __table_args__ = ( Index("organization_roles_user_id_idx", "user_id"), Index("organization_roles_organization_id_idx", "organization_id"), UniqueConstraint( "user_id", "organization_id", name="_organization_roles_user_organization_uc", ), ) __repr__ = make_repr("role_name") role_name = Column( Enum(OrganizationRoleType, values_callable=lambda x: [e.value for e in x]), nullable=False, ) user_id = Column(ForeignKey("users.id", onupdate="CASCADE", ondelete="CASCADE"), nullable=False) organization_id = Column( ForeignKey("organizations.id", onupdate="CASCADE", ondelete="CASCADE"), nullable=False, ) user = orm.relationship(User, lazy=False) organization = orm.relationship("Organization", lazy=False)
class OrganizationProject(db.Model): __tablename__ = "organization_project" __table_args__ = ( Index("organization_project_organization_id_idx", "organization_id"), Index("organization_project_project_id_idx", "project_id"), UniqueConstraint( "organization_id", "project_id", name="_organization_project_organization_project_uc", ), ) __repr__ = make_repr("project_id", "organization_id", "is_active") is_active = Column(Boolean, nullable=False, server_default=sql.false()) organization_id = Column( ForeignKey("organizations.id", onupdate="CASCADE", ondelete="CASCADE"), nullable=False, ) project_id = Column( ForeignKey("projects.id", onupdate="CASCADE", ondelete="CASCADE"), nullable=False, ) organization = orm.relationship("Organization", lazy=False) project = orm.relationship("Project", lazy=False)
class BlacklistedProject(db.Model): __tablename__ = "blacklist" __table_args__ = ( CheckConstraint( "name ~* '^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$'::text", name="blacklist_valid_name", ), ) __repr__ = make_repr("name") created = Column( DateTime(timezone=False), nullable=False, server_default=sql.func.now(), ) name = Column(Text, unique=True, nullable=False) _blacklisted_by = Column( "blacklisted_by", UUID(as_uuid=True), ForeignKey("accounts_user.id"), ) blacklisted_by = orm.relationship(User) comment = Column(Text, nullable=False, server_default="")
class Role(db.Model): __tablename__ = "roles" __table_args__ = ( Index("roles_pack_name_idx", "package_name"), Index("roles_user_name_idx", "user_name"), ) __repr__ = make_repr("role_name", "user_name", "package_name") role_name = Column(Text) user_name = Column( CIText, ForeignKey("accounts_user.username", onupdate="CASCADE", ondelete="CASCADE"), ) package_name = Column( Text, ForeignKey("packages.name", onupdate="CASCADE", ondelete="CASCADE") ) user = orm.relationship(User, lazy=False) project = orm.relationship("Project", lazy=False) def __gt__(self, other): """ Temporary hack to allow us to only display the 'highest' role when there are multiple for a given user TODO: This should be removed when fixing GH-2745. """ order = ["Maintainer", "Owner"] # from lowest to highest return order.index(self.role_name) > order.index(other.role_name)
class User(SitemapMixin, db.Model): __tablename__ = "accounts_user" __table_args__ = ( CheckConstraint("length(username) <= 50", name="packages_valid_name"), CheckConstraint( "username ~* '^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$'", name="accounts_user_valid_username", ), ) __repr__ = make_repr("username") username = Column(CIText, nullable=False, unique=True) name = Column(String(length=100), nullable=False) password = Column(String(length=128), nullable=False) password_date = Column( DateTime, nullable=True, server_default=sql.func.now(), ) is_active = Column(Boolean, nullable=False) is_staff = Column(Boolean, nullable=False) is_superuser = Column(Boolean, nullable=False) date_joined = Column(DateTime, server_default=sql.func.now()) last_login = Column( DateTime, nullable=False, server_default=sql.func.now(), ) emails = orm.relationship( "Email", backref="user", cascade="all, delete-orphan", lazy=False, ) @property def primary_email(self): primaries = [x for x in self.emails if x.primary] if primaries: return primaries[0] @hybrid_property def email(self): primary_email = self.primary_email return primary_email.email if primary_email else None @email.expression def email(self): return (select([Email.email ]).where((Email.user_id == self.id) & (Email.primary.is_(True))).as_scalar())
class Classifier(db.ModelBase): __tablename__ = "trove_classifiers" __tableargs__ = CheckConstraint( "classifier not ilike 'private ::%'", name="ck_disallow_private_top_level_classifier", ) __repr__ = make_repr("classifier") id = Column(Integer, primary_key=True, nullable=False) classifier = Column(Text, unique=True)
class Classifier(db.ModelBase): __tablename__ = "trove_classifiers" __repr__ = make_repr("classifier") id = Column(Integer, primary_key=True, nullable=False) classifier = Column(Text, unique=True) deprecated = Column(Boolean, nullable=False, server_default=sql.false()) l2 = Column(Integer) l3 = Column(Integer) l4 = Column(Integer) l5 = Column(Integer)
class Dependency(db.Model): __tablename__ = "release_dependencies" __table_args__ = (Index("release_dependencies_release_kind_idx", "release_id", "kind"), ) __repr__ = make_repr("name", "version", "kind", "specifier") release_id = Column( ForeignKey("releases.id", onupdate="CASCADE", ondelete="CASCADE"), nullable=False, ) kind = Column(Integer) specifier = Column(Text)
class Project(db.ModelBase): __tablename__ = "packages" __table_args__ = (CheckConstraint( "name ~* '^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$'::text", name="packages_valid_name", ), ) __repr__ = make_repr("name") name = Column(Text, primary_key=True, nullable=False) normalized_name = orm.column_property(func.normalize_pep426_name(name)) stable_version = Column(Text) autohide = Column(Boolean, server_default=sql.true()) comments = Column(Boolean, server_default=sql.true()) bugtrack_url = Column(Text) hosting_mode = Column(Text, nullable=False, server_default="pypi-explicit") created = Column( DateTime(timezone=False), nullable=False, server_default=sql.func.now(), ) releases = orm.relationship( "Release", backref="project", cascade="all, delete-orphan", lazy="dynamic", ) def __getitem__(self, version): try: return self.releases.filter(Release.version == version).one() except NoResultFound: raise KeyError from None @property def documentation_url(self): # TODO: Move this into the database and elimnate the use of the # threadlocal here. registry = get_current_registry() request = get_current_request() path = "/".join([self.name, "index.html"]) # If the path doesn't exist, then we'll just return a None here. if not registry["filesystems"]["documentation"].exists(path): return return request.route_url("legacy.docs", project=self.name)
class Classifier(db.ModelBase): __tablename__ = "trove_classifiers" __table_args__ = ( Index("trove_class_class_idx", "classifier"), Index("trove_class_id_idx", "id"), ) __repr__ = make_repr("classifier") id = Column(Integer, primary_key=True, nullable=False) classifier = Column(Text, unique=True) l2 = Column(Integer) l3 = Column(Integer) l4 = Column(Integer) l5 = Column(Integer)
class Classifier(db.ModelBase): __tablename__ = "trove_classifiers" __tableargs__ = CheckConstraint( "classifier not ilike 'private ::%'", name="ck_disallow_private_top_level_classifier", ) __repr__ = make_repr("classifier") id = Column(Integer, primary_key=True, nullable=False) classifier = Column(Text, unique=True) deprecated = Column(Boolean, nullable=False, server_default=sql.false()) l2 = Column(Integer) l3 = Column(Integer) l4 = Column(Integer) l5 = Column(Integer)
class Sponsor(db.Model): __tablename__ = "sponsors" __repr__ = make_repr("name") name = Column(String, nullable=False) service = Column(String) activity_markdown = Column(Text) link_url = Column(URLType, nullable=False) color_logo_url = Column(URLType, nullable=False) white_logo_url = Column(URLType) # control flags is_active = Column(Boolean, default=False, nullable=False) footer = Column(Boolean, default=False, nullable=False) psf_sponsor = Column(Boolean, default=False, nullable=False) infra_sponsor = Column(Boolean, default=False, nullable=False) one_time = Column(Boolean, default=False, nullable=False) sidebar = Column(Boolean, default=False, nullable=False) # pythondotorg integration origin = Column(String, default="manual") level_name = Column(String) level_order = Column(Integer, default=0) slug = Column(String) @property def color_logo_img(self): return f'<img src="{ self.color_logo_url }" alt="{ self.name }">' @property def white_logo_img(self): if not self.white_logo_url: return "" return f'<img class="sponsors__image" \ src="{ self.white_logo_url }" alt="{ self.name }">' @property def activity(self): """ Render raw activity markdown as HTML """ if not self.activity_markdown: return "" return readme.render(self.activity_markdown, "text/markdown")
class Dependency(db.Model): __tablename__ = "release_dependencies" __table_args__ = ( Index("rel_dep_name_version_kind_idx", "name", "version", "kind"), ForeignKeyConstraint( ["name", "version"], ["releases.name", "releases.version"], onupdate="CASCADE", ondelete="CASCADE", ), ) __repr__ = make_repr("name", "version", "kind", "specifier") name = Column(Text) version = Column(Text) kind = Column(Integer) specifier = Column(Text)
class OrganizationNameCatalog(db.Model): __tablename__ = "organization_name_catalog" __table_args__ = ( Index("organization_name_catalog_normalized_name_idx", "normalized_name"), Index("organization_name_catalog_organization_id_idx", "organization_id"), UniqueConstraint( "normalized_name", "organization_id", name="_organization_name_catalog_normalized_name_organization_uc", ), ) __repr__ = make_repr("normalized_name", "organization_id") normalized_name = Column(Text, nullable=False, index=True) organization_id = Column(UUID(as_uuid=True), nullable=True, index=True)
class ProhibitedProjectName(db.Model): __tablename__ = "prohibited_project_names" __table_args__ = (CheckConstraint( "name ~* '^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$'::text", name="prohibited_project_valid_name", ), ) __repr__ = make_repr("name") created = Column(DateTime(timezone=False), nullable=False, server_default=sql.func.now()) name = Column(Text, unique=True, nullable=False) _prohibited_by = Column("prohibited_by", UUID(as_uuid=True), ForeignKey("users.id"), index=True) prohibited_by = orm.relationship(User) comment = Column(Text, nullable=False, server_default="")
class Role(db.Model): __tablename__ = "roles" __table_args__ = ( Index("roles_pack_name_idx", "package_name"), Index("roles_user_name_idx", "user_name"), ) __repr__ = make_repr("role_name", "user_name", "package_name") role_name = Column(Text) user_name = Column( CIText, ForeignKey("accounts_user.username", onupdate="CASCADE"), ) package_name = Column( Text, ForeignKey("packages.name", onupdate="CASCADE"), ) user = orm.relationship(User, lazy=False) project = orm.relationship("Project", lazy=False)
class MalwareCheck(db.Model): __tablename__ = "malware_checks" __table_args__ = (UniqueConstraint("name", "version"), ) __repr__ = make_repr("name", "version") name = Column(CIText, nullable=False) version = Column(Integer, default=1, nullable=False) short_description = Column(String(length=128), nullable=False) long_description = Column(Text, nullable=False) check_type = Column( Enum(MalwareCheckType, values_callable=lambda x: [e.value for e in x]), nullable=False, ) # The object name that hooked-based checks operate on, e.g. # Project, File, Release hooked_object = Column( Enum(MalwareCheckObjectType, values_callable=lambda x: [e.value for e in x]), nullable=True, ) # The run schedule for schedule-based checks. schedule = Column(JSONB, nullable=True) state = Column( Enum(MalwareCheckState, values_callable=lambda x: [e.value for e in x]), nullable=False, server_default=("disabled"), ) created = Column(DateTime, nullable=False, server_default=sql.func.now()) @property def is_stale(self): session = orm.object_session(self) newest = (session.query(MalwareCheck).filter( MalwareCheck.name == self.name).order_by( MalwareCheck.version.desc()).first()) return self.version != newest.version
class Banner(db.Model): __tablename__ = "banners" __repr__ = make_repr("text") DEFAULT_FA_ICON = "fa-comment-alt" DEFAULT_BTN_LABEL = "See more" # internal name name = Column(String, nullable=False) # banner display configuration text = Column(Text, nullable=False) link_url = Column(URLType, nullable=False) link_label = Column(String, nullable=False, default=DEFAULT_BTN_LABEL) fa_icon = Column(String, nullable=False, default=DEFAULT_FA_ICON) # visibility control active = Column(Boolean, nullable=False, default=False) end = Column(Date, nullable=False) @property def is_live(self): # date.today is using the server timezone which is UTC return self.active and date.today() <= self.end
def __repr__(self): self.__repr__ = make_repr(*self.__table__.columns.keys(), _self=self) return self.__repr__()
class Project(SitemapMixin, db.Model): __tablename__ = "projects" __table_args__ = (CheckConstraint( "name ~* '^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$'::text", name="projects_valid_name", ), ) __repr__ = make_repr("name") name = Column(Text, nullable=False) normalized_name = orm.column_property(func.normalize_pep426_name(name)) created = Column( DateTime(timezone=False), nullable=False, server_default=sql.func.now(), index=True, ) has_docs = Column(Boolean) upload_limit = Column(Integer, nullable=True) last_serial = Column(Integer, nullable=False, server_default=sql.text("0")) zscore = Column(Float, nullable=True) total_size = Column(BigInteger, server_default=sql.text("0")) users = orm.relationship(User, secondary=Role.__table__, backref="projects") releases = orm.relationship( "Release", backref="project", cascade="all, delete-orphan", order_by=lambda: Release._pypi_ordering.desc(), passive_deletes=True, ) events = orm.relationship("ProjectEvent", backref="project", cascade="all, delete-orphan", lazy=True) def __getitem__(self, version): session = orm.object_session(self) canonical_version = packaging.utils.canonicalize_version(version) try: return (session.query(Release).filter( Release.project == self, Release.canonical_version == canonical_version, ).one()) except MultipleResultsFound: # There are multiple releases of this project which have the same # canonical version that were uploaded before we checked for # canonical version equivalence, so return the exact match instead try: return (session.query(Release).filter( Release.project == self, Release.version == version).one()) except NoResultFound: # There are multiple releases of this project which have the # same canonical version, but none that have the exact version # specified, so just 404 raise KeyError from None except NoResultFound: raise KeyError from None def __acl__(self): session = orm.object_session(self) acls = [ (Allow, "group:admins", "admin"), (Allow, "group:moderators", "moderator"), ] # Get all of the users for this project. query = session.query(Role).filter(Role.project == self) query = query.options(orm.lazyload("project")) query = query.options(orm.joinedload("user").lazyload("emails")) query = query.join(User).order_by(User.id.asc()) for role in sorted( query.all(), key=lambda x: ["Owner", "Maintainer"].index(x.role_name)): if role.role_name == "Owner": acls.append( (Allow, str(role.user.id), ["manage:project", "upload"])) else: acls.append((Allow, str(role.user.id), ["upload"])) return acls def record_event(self, *, tag, ip_address, additional=None): session = orm.object_session(self) event = ProjectEvent(project=self, tag=tag, ip_address=ip_address, additional=additional) session.add(event) session.flush() return event @property def documentation_url(self): # TODO: Move this into the database and eliminate the use of the # threadlocal here. request = get_current_request() # If the project doesn't have docs, then we'll just return a None here. if not self.has_docs: return return request.route_url("legacy.docs", project=self.name) @property def all_versions(self): return (orm.object_session(self).query( Release.version, Release.created, Release.is_prerelease, Release.yanked).filter(Release.project == self).order_by( Release._pypi_ordering.desc()).all()) @property def latest_version(self): return (orm.object_session(self).query( Release.version, Release.created, Release.is_prerelease).filter( Release.project == self, Release.yanked.is_(False)).order_by( Release.is_prerelease.nullslast(), Release._pypi_ordering.desc()).first())
class Release(db.Model): __tablename__ = "releases" @declared_attr def __table_args__(cls): # noqa return ( Index("release_created_idx", cls.created.desc()), Index("release_project_created_idx", cls.project_id, cls.created.desc()), Index("release_version_idx", cls.version), UniqueConstraint("project_id", "version"), ) __repr__ = make_repr("project", "version") __parent__ = dotted_navigator("project") __name__ = dotted_navigator("version") project_id = Column( ForeignKey("projects.id", onupdate="CASCADE", ondelete="CASCADE"), nullable=False, ) version = Column(Text, nullable=False) canonical_version = Column(Text, nullable=False) is_prerelease = orm.column_property(func.pep440_is_prerelease(version)) author = Column(Text) author_email = Column(Text) maintainer = Column(Text) maintainer_email = Column(Text) home_page = Column(Text) license = Column(Text) summary = Column(Text) keywords = Column(Text) platform = Column(Text) download_url = Column(Text) _pypi_ordering = Column(Integer) requires_python = Column(Text) created = Column(DateTime(timezone=False), nullable=False, server_default=sql.func.now()) description_id = Column( ForeignKey("release_descriptions.id", onupdate="CASCADE", ondelete="CASCADE"), nullable=False, ) description = orm.relationship( "Description", backref=orm.backref( "release", cascade="all, delete-orphan", passive_deletes=True, passive_updates=True, single_parent=True, uselist=False, ), ) yanked = Column(Boolean, nullable=False, server_default=sql.false()) yanked_reason = Column(Text, nullable=False, server_default="") _classifiers = orm.relationship( Classifier, backref="project_releases", secondary=lambda: release_classifiers, order_by=Classifier.classifier, passive_deletes=True, ) classifiers = association_proxy("_classifiers", "classifier") files = orm.relationship( "File", backref="release", cascade="all, delete-orphan", lazy="dynamic", order_by=lambda: File.filename, passive_deletes=True, ) dependencies = orm.relationship( "Dependency", backref="release", cascade="all, delete-orphan", passive_deletes=True, ) _requires = _dependency_relation(DependencyKind.requires) requires = association_proxy("_requires", "specifier") _provides = _dependency_relation(DependencyKind.provides) provides = association_proxy("_provides", "specifier") _obsoletes = _dependency_relation(DependencyKind.obsoletes) obsoletes = association_proxy("_obsoletes", "specifier") _requires_dist = _dependency_relation(DependencyKind.requires_dist) requires_dist = association_proxy("_requires_dist", "specifier") _provides_dist = _dependency_relation(DependencyKind.provides_dist) provides_dist = association_proxy("_provides_dist", "specifier") _obsoletes_dist = _dependency_relation(DependencyKind.obsoletes_dist) obsoletes_dist = association_proxy("_obsoletes_dist", "specifier") _requires_external = _dependency_relation(DependencyKind.requires_external) requires_external = association_proxy("_requires_external", "specifier") _project_urls = _dependency_relation(DependencyKind.project_url) project_urls = association_proxy("_project_urls", "specifier") uploader_id = Column( ForeignKey("users.id", onupdate="CASCADE", ondelete="SET NULL"), nullable=True, index=True, ) uploader = orm.relationship(User) uploaded_via = Column(Text) @property def urls(self): _urls = OrderedDict() if self.home_page: _urls["Homepage"] = self.home_page if self.download_url: _urls["Download"] = self.download_url for urlspec in self.project_urls: name, _, url = urlspec.partition(",") name = name.strip() url = url.strip() if name and url: _urls[name] = url return _urls @property def github_repo_info_url(self): for url in self.urls.values(): parsed = urlparse(url) segments = parsed.path.strip("/").split("/") if parsed.netloc in {"github.com", "www.github.com" } and len(segments) >= 2: user_name, repo_name = segments[:2] return f"https://api.github.com/repos/{user_name}/{repo_name}" @property def has_meta(self): return any([ self.license, self.keywords, self.author, self.author_email, self.maintainer, self.maintainer_email, self.requires_python, ])
class Project(SitemapMixin, db.ModelBase): __tablename__ = "packages" __table_args__ = (CheckConstraint( "name ~* '^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$'::text", name="packages_valid_name", ), ) __repr__ = make_repr("name") name = Column(Text, primary_key=True, nullable=False) normalized_name = orm.column_property(func.normalize_pep426_name(name)) stable_version = Column(Text) autohide = Column(Boolean, server_default=sql.true()) comments = Column(Boolean, server_default=sql.true()) bugtrack_url = Column(Text) hosting_mode = Column(Text, nullable=False, server_default="pypi-only") created = Column( DateTime(timezone=False), nullable=False, server_default=sql.func.now(), ) has_docs = Column(Boolean) upload_limit = Column(Integer, nullable=True) last_serial = Column(Integer, nullable=False, server_default=sql.text("0")) allow_legacy_files = Column( Boolean, nullable=False, server_default=sql.false(), ) users = orm.relationship( User, secondary=Role.__table__, backref="projects", ) releases = orm.relationship( "Release", backref="project", cascade="all, delete-orphan", order_by=lambda: Release._pypi_ordering.desc(), ) def __getitem__(self, version): session = orm.object_session(self) try: return (session.query(Release).filter((Release.project == self) & ( Release.version == version)).one()) except NoResultFound: raise KeyError from None def __acl__(self): session = orm.object_session(self) acls = [] # Get all of the users for this project. query = session.query(Role).filter(Role.project == self) query = query.options(orm.lazyload("project")) query = query.options(orm.joinedload("user").lazyload("emails")) for role in sorted( query.all(), key=lambda x: ["Owner", "Maintainer"].index(x.role_name)): acls.append((Allow, role.user.id, ["upload"])) return acls @property def documentation_url(self): # TODO: Move this into the database and elimnate the use of the # threadlocal here. request = get_current_request() # If the project doesn't have docs, then we'll just return a None here. if not self.has_docs: return return request.route_url("legacy.docs", project=self.name)
class Release(db.ModelBase): __tablename__ = "releases" @declared_attr def __table_args__(cls): # noqa return ( Index("release_created_idx", cls.created.desc()), Index("release_name_created_idx", cls.name, cls.created.desc()), Index("release_name_idx", cls.name), Index("release_pypi_hidden_idx", cls._pypi_hidden), Index("release_version_idx", cls.version), ) __repr__ = make_repr("name", "version") name = Column( Text, ForeignKey("packages.name", onupdate="CASCADE"), primary_key=True, ) version = Column(Text, primary_key=True) is_prerelease = orm.column_property(func.pep440_is_prerelease(version)) author = Column(Text) author_email = Column(Text) maintainer = Column(Text) maintainer_email = Column(Text) home_page = Column(Text) license = Column(Text) summary = Column(Text) description = Column(Text) keywords = Column(Text) platform = Column(Text) download_url = Column(Text) _pypi_ordering = Column(Integer) _pypi_hidden = Column(Boolean) cheesecake_installability_id = Column( Integer, ForeignKey("cheesecake_main_indices.id"), ) cheesecake_documentation_id = Column( Integer, ForeignKey("cheesecake_main_indices.id"), ) cheesecake_code_kwalitee_id = Column( Integer, ForeignKey("cheesecake_main_indices.id"), ) requires_python = Column(Text) description_from_readme = Column(Boolean) created = Column( DateTime(timezone=False), nullable=False, server_default=sql.func.now(), ) _classifiers = orm.relationship( Classifier, backref="project_releases", secondary=lambda: release_classifiers, order_by=Classifier.classifier, ) classifiers = association_proxy("_classifiers", "classifier") files = orm.relationship( "File", backref="release", cascade="all, delete-orphan", lazy="dynamic", order_by=lambda: File.filename, ) dependencies = orm.relationship("Dependency") _requires = _dependency_relation(DependencyKind.requires) requires = association_proxy("_requires", "specifier") _provides = _dependency_relation(DependencyKind.provides) provides = association_proxy("_provides", "specifier") _obsoletes = _dependency_relation(DependencyKind.obsoletes) obsoletes = association_proxy("_obsoletes", "specifier") _requires_dist = _dependency_relation(DependencyKind.requires_dist) requires_dist = association_proxy("_requires_dist", "specifier") _provides_dist = _dependency_relation(DependencyKind.provides_dist) provides_dist = association_proxy("_provides_dist", "specifier") _obsoletes_dist = _dependency_relation(DependencyKind.obsoletes_dist) obsoletes_dist = association_proxy("_obsoletes_dist", "specifier") _requires_external = _dependency_relation(DependencyKind.requires_external) requires_external = association_proxy("_requires_external", "specifier") _project_urls = _dependency_relation(DependencyKind.project_url) project_urls = association_proxy("_project_urls", "specifier") uploader = orm.relationship( "User", secondary=lambda: JournalEntry.__table__, primaryjoin=lambda: ((JournalEntry.name == orm.foreign(Release.name)) & (JournalEntry.version == orm.foreign(Release.version)) & (JournalEntry.action == "new release")), secondaryjoin=lambda: ( (User.username == orm.foreign(JournalEntry._submitted_by))), order_by=lambda: JournalEntry.submitted_date.desc(), # TODO: We have uselist=False here which raises a warning because # multiple items were returned. This should only be temporary because # we should add a nullable FK to JournalEntry so we don't need to rely # on ordering and implicitly selecting the first object to make this # happen, uselist=False, viewonly=True, ) @property def urls(self): _urls = OrderedDict() if self.home_page: _urls["Homepage"] = self.home_page for urlspec in self.project_urls: name, url = urlspec.split(",", 1) _urls[name] = url if self.download_url and "Download" not in _urls: _urls["Download"] = self.download_url return _urls @property def has_meta(self): return any([ self.keywords, self.author, self.author_email, self.maintainer, self.maintainer_email ])
def __repr__(self): self.__repr__ = make_repr("foo", _self=self) return self.__repr__()
class Fake: __repr__ = make_repr("foo") @property def foo(self): raise DetachedInstanceError
class Organization(HasEvents, db.Model): __tablename__ = "organizations" __table_args__ = (CheckConstraint( "name ~* '^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$'::text", name="organizations_valid_name", ), ) __repr__ = make_repr("name") name = Column(Text, nullable=False) normalized_name = orm.column_property(func.normalize_pep426_name(name)) display_name = Column(Text, nullable=False) orgtype = Column( Enum(OrganizationType, values_callable=lambda x: [e.value for e in x]), nullable=False, ) link_url = Column(URLType, nullable=False) description = Column(Text, nullable=False) is_active = Column(Boolean, nullable=False, server_default=sql.false()) is_approved = Column(Boolean) created = Column( DateTime(timezone=False), nullable=False, server_default=sql.func.now(), index=True, ) date_approved = Column( DateTime(timezone=False), nullable=True, onupdate=func.now(), ) users = orm.relationship( User, secondary=OrganizationRole.__table__, backref="organizations" # type: ignore # noqa ) projects = orm.relationship( "Project", secondary=OrganizationProject.__table__, backref="organizations" # type: ignore # noqa ) def record_event(self, *, tag, ip_address, additional={}): """Record organization name in events in case organization is ever deleted.""" super().record_event( tag=tag, ip_address=ip_address, additional={ "organization_name": self.name, **additional }, ) def __acl__(self): session = orm.object_session(self) acls = [ (Allow, "group:admins", "admin"), (Allow, "group:moderators", "moderator"), ] # Get all of the users for this organization. query = session.query(OrganizationRole).filter( OrganizationRole.organization == self) query = query.options(orm.lazyload("organization")) query = query.join(User).order_by(User.id.asc()) for role in sorted( query.all(), key=lambda x: [e.value for e in OrganizationRoleType].index( x.role_name), ): # Allow all people in organization read access. # Allow write access depending on role. if role.role_name == OrganizationRoleType.Owner: acls.append(( Allow, f"user:{role.user.id}", ["view:organization", "manage:organization"], )) elif role.role_name == OrganizationRoleType.BillingManager: acls.append(( Allow, f"user:{role.user.id}", ["view:organization", "manage:billing"], )) elif role.role_name == OrganizationRoleType.Manager: acls.append(( Allow, f"user:{role.user.id}", ["view:organization", "manage:team"], )) else: # No member-specific write access needed for now. acls.append( (Allow, f"user:{role.user.id}", ["view:organization"])) return acls