Skip to content

Commit

Permalink
dep: add initial Cpn object support for pkgcraft changes
Browse files Browse the repository at this point in the history
  • Loading branch information
radhermit committed Jan 30, 2024
1 parent 1256b7d commit e85cdf4
Show file tree
Hide file tree
Showing 13 changed files with 351 additions and 86 deletions.
88 changes: 74 additions & 14 deletions src/pkgcraft/C.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ cdef extern from "pkgcraft.h":
cdef struct Config:
pass

# Opaque wrapper for pkgcraft::dep::cpv::Cpn<String> objects.
cdef struct Cpn:
pass

# Opaque wrapper for pkgcraft::dep::cpv::Cpv<String> objects.
cdef struct Cpv:
pass
Expand Down Expand Up @@ -333,6 +337,71 @@ cdef extern from "pkgcraft.h":
# The config argument must be a non-null Config pointer.
RepoSet *pkgcraft_config_repos_set(Config *c, const RepoFormat *format)

# Get the category of a Cpn object.
#
# # Safety
# The argument must be a non-null Cpn pointer.
char *pkgcraft_cpn_category(Cpn *c)

# Compare two Cpns returning -1, 0, or 1 if the first is less than, equal to, or
# greater than the second, respectively.
#
# # Safety
# The arguments must be non-null Cpn pointers.
int pkgcraft_cpn_cmp(Cpn *c1, Cpn *c2)

# Free a Cpn.
#
# # Safety
# The argument must be a Cpn pointer or NULL.
void pkgcraft_cpn_free(Cpn *c)

# Return the hash value for a Cpn object.
#
# # Safety
# The argument must be a non-null Cpn pointer.
uint64_t pkgcraft_cpn_hash(Cpn *c)

# Parse a string into a Cpn object.
#
# Returns NULL on error.
#
# # Safety
# The argument should be a UTF-8 string.
Cpn *pkgcraft_cpn_new(const char *s)

# Get the package name of a Cpn object.
#
# # Safety
# The argument must be a non-null Cpn pointer.
char *pkgcraft_cpn_package(Cpn *c)

# Determine if a string is a valid package Cpn.
#
# Returns NULL on error.
#
# # Safety
# The argument should point to a UTF-8 string.
const char *pkgcraft_cpn_parse(const char *s)

# Return the restriction for a Cpn object.
#
# # Safety
# The argument must be a non-null Cpn pointer.
Restrict *pkgcraft_cpn_restrict(Cpn *c)

# Determine if a restriction matches a Cpn object.
#
# # Safety
# The arguments must be valid Restrict and Cpn pointers.
bool pkgcraft_cpn_restrict_matches(Cpn *c, Restrict *r)

# Return the string for a Cpn object.
#
# # Safety
# The argument must be a non-null Cpn pointer.
char *pkgcraft_cpn_str(Cpn *c)

# Get the category of a Cpv object.
#
# # Safety
Expand All @@ -346,11 +415,11 @@ cdef extern from "pkgcraft.h":
# The arguments must be non-null Cpv pointers.
int pkgcraft_cpv_cmp(Cpv *c1, Cpv *c2)

# Get the category and package of a Cpv object.
# Get the Cpn of a Cpv object.
#
# # Safety
# The argument must be a non-null Cpv pointer.
char *pkgcraft_cpv_cpn(Cpv *c)
Cpn *pkgcraft_cpv_cpn(Cpv *c)

# Free a Cpv.
#
Expand All @@ -376,7 +445,7 @@ cdef extern from "pkgcraft.h":
# The arguments must be non-null Cpv and Dep pointers.
bool pkgcraft_cpv_intersects_dep(Cpv *c, Dep *d)

# Parse a CPV string into a Cpv object.
# Parse a string into a Cpv object.
#
# Returns NULL on error.
#
Expand Down Expand Up @@ -496,12 +565,11 @@ cdef extern from "pkgcraft.h":
# The arguments must be non-null Dep pointers.
int pkgcraft_dep_cmp(Dep *d1, Dep *d2)

# Get the category and package of a package dependency.
# For example, the package dependency "=cat/pkg-1-r2" returns "cat/pkg".
# Get the Cpn of a package dependency.
#
# # Safety
# The argument must be a non-null Dep pointer.
char *pkgcraft_dep_cpn(Dep *d)
Cpn *pkgcraft_dep_cpn(Dep *d)

# Get the category, package, and version of a package dependency.
# For example, the package dependency "=cat/pkg-1-r2" returns "cat/pkg-1-r2".
Expand Down Expand Up @@ -554,14 +622,6 @@ cdef extern from "pkgcraft.h":
Dep *pkgcraft_dep_new(const char *s,
const Eapi *eapi)

# Parse a string into an unversioned package dependency.
#
# Returns NULL on error.
#
# # Safety
# The argument must be a UTF-8 string.
Dep *pkgcraft_dep_new_cpn(const char *s)

# Get the package and revision of a package dependency.
# For example, the package dependency "=cat/pkg-1-r2" returns "pkg-1".
#
Expand Down
1 change: 1 addition & 0 deletions src/pkgcraft/dep/__init__.pxd
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .base cimport *
from .cpn cimport *
from .cpv cimport *
from .pkg cimport *
from .use cimport *
Expand Down
1 change: 1 addition & 0 deletions src/pkgcraft/dep/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .base import *
from .cpn import *
from .cpv import *
from .pkg import *
from .use import *
Expand Down
12 changes: 12 additions & 0 deletions src/pkgcraft/dep/cpn.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from .. cimport C


cdef class Cpn:
cdef C.Cpn *ptr
# cached fields
cdef str _category
cdef str _package
cdef int _hash

@staticmethod
cdef Cpn from_ptr(C.Cpn *)
161 changes: 161 additions & 0 deletions src/pkgcraft/dep/cpn.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
cimport cython

from .. cimport C
from .._misc cimport cstring_to_str
from ..restrict cimport Restrict

from ..error import InvalidCpn


@cython.final
cdef class Cpn:
"""Unversioned package."""

def __init__(self, s: str):
"""Create a new Cpn object.
Valid:
>>> from pkgcraft.dep import Cpn
>>> cpn = Cpn('cat/pkg')
>>> cpn.category
'cat'
>>> cpn.package
'pkg'
Invalid:
>>> Cpn('>cat/pkg-1')
Traceback (most recent call last):
...
pkgcraft.error.InvalidCpn: parsing failure: invalid cpn: >cat/pkg-1
...
"""
self.ptr = C.pkgcraft_cpn_new(s.encode())
if self.ptr is NULL:
raise InvalidCpn

@staticmethod
def parse(s: str, raised=False):
"""Determine if a string is a valid package Cpn.
This avoids any allocations, only returning the validity status.
Args:
s: the string to parse
raised: if True, raise an exception when invalid
Returns:
bool: True if the given string represents a valid Cpn, otherwise False.
Raises:
InvalidCpn: on failure if the raised parameter is set to True
>>> from pkgcraft.dep import Cpn
>>> Cpn.parse('cat/pkg')
True
"""
valid = C.pkgcraft_cpn_parse(s.encode()) is not NULL
if not valid and raised:
raise InvalidCpn
return valid

@staticmethod
cdef Cpn from_ptr(C.Cpn *ptr):
"""Create a Cpn from a pointer."""
inst = <Cpn>Cpn.__new__(Cpn)
inst.ptr = <C.Cpn *>ptr
return inst

@property
def category(self):
"""Get the category of a Cpn.
Returns:
str: the category name
>>> from pkgcraft.dep import Cpn
>>> cpn = Cpn('cat/pkg')
>>> cpn.category
'cat'
"""
if self._category is None:
self._category = cstring_to_str(C.pkgcraft_cpn_category(self.ptr))
return self._category

@property
def package(self):
"""Get the package name of a Cpn.
Returns:
str: the package name
>>> from pkgcraft.dep import Cpn
>>> cpn = Cpn('cat/pkg')
>>> cpn.package
'pkg'
"""
if self._package is None:
self._package = cstring_to_str(C.pkgcraft_cpn_package(self.ptr))
return self._package

def matches(self, r: Restrict):
"""Determine if a restriction matches a Cpn.
Args:
r: restriction object to match against
Returns:
bool: True if matching, otherwise False.
"""
return C.pkgcraft_cpn_restrict_matches(self.ptr, r.ptr)

def __lt__(self, other):
if isinstance(other, Cpn):
return C.pkgcraft_cpn_cmp(self.ptr, (<Cpn>other).ptr) == -1
return NotImplemented

def __le__(self, other):
if isinstance(other, Cpn):
return C.pkgcraft_cpn_cmp(self.ptr, (<Cpn>other).ptr) <= 0
return NotImplemented

def __eq__(self, other):
if isinstance(other, Cpn):
return C.pkgcraft_cpn_cmp(self.ptr, (<Cpn>other).ptr) == 0
return NotImplemented

def __ne__(self, other):
if isinstance(other, Cpn):
return C.pkgcraft_cpn_cmp(self.ptr, (<Cpn>other).ptr) != 0
return NotImplemented

def __ge__(self, other):
if isinstance(other, Cpn):
return C.pkgcraft_cpn_cmp(self.ptr, (<Cpn>other).ptr) >= 0
return NotImplemented

def __gt__(self, other):
if isinstance(other, Cpn):
return C.pkgcraft_cpn_cmp(self.ptr, (<Cpn>other).ptr) == 1
return NotImplemented

def __str__(self):
return cstring_to_str(C.pkgcraft_cpn_str(self.ptr))

def __repr__(self):
addr = <size_t>&self.ptr
name = self.__class__.__name__
return f"<{name} '{self}' at 0x{addr:0x}>"

def __hash__(self):
if not self._hash:
self._hash = C.pkgcraft_cpn_hash(self.ptr)
return self._hash

def __reduce__(self):
"""Support pickling Cpn objects."""
return self.__class__, (str(self),)

def __dealloc__(self):
C.pkgcraft_cpn_free(self.ptr)
9 changes: 5 additions & 4 deletions src/pkgcraft/dep/cpv.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ cimport cython
from .. cimport C
from .._misc cimport cstring_to_str
from ..restrict cimport Restrict
from . cimport Dep
from .cpn cimport Cpn
from .pkg cimport Dep
from .version cimport Version

from ..error import InvalidCpv, PkgcraftError
Expand Down Expand Up @@ -263,14 +264,14 @@ cdef class Cpv:

@property
def cpn(self):
"""Get the category and package of a Cpv.
"""Get the Cpn of a Cpv.
>>> from pkgcraft.dep import Cpv
>>> cpv = Cpv('cat/pkg-1-r2')
>>> cpv.cpn
>>> str(cpv.cpn)
'cat/pkg'
"""
return cstring_to_str(C.pkgcraft_cpv_cpn(self.ptr))
return Cpn.from_ptr(C.pkgcraft_cpv_cpn(self.ptr))

def matches(self, r: Restrict):
"""Determine if a restriction matches a Cpv.
Expand Down
4 changes: 0 additions & 4 deletions src/pkgcraft/dep/pkg.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,3 @@ cdef class Dep:

@staticmethod
cdef Dep from_ptr(C.Dep *)


cdef class Cpn(Dep):
pass
Loading

0 comments on commit e85cdf4

Please sign in to comment.