2222# Visit https://github.com/package-url/packageurl-python for support and
2323# download.
2424
25- from packageurl .contrib .route import Router
26-
2725"""
2826Validate each type according to the PURL spec type definitions
2927"""
@@ -41,7 +39,10 @@ def validate(cls, purl, strict=False):
4139 elif cls .namespace_requirement == "required" and not purl .namespace :
4240 yield f"Namespace is required for purl type: { cls .type !r} "
4341
44- if (
42+ if purl .type == "cpan" :
43+ if purl .namespace and purl .namespace != purl .namespace .upper ():
44+ yield f"Namespace must be uppercase for purl type: { cls .type !r} "
45+ elif (
4546 not cls .namespace_case_sensitive
4647 and purl .namespace
4748 and purl .namespace .lower () != purl .namespace
@@ -243,6 +244,14 @@ class CpanTypeValidator(TypeValidator):
243244 version_case_sensitive = True
244245 purl_pattern = "pkg:cpan/.*"
245246
247+ @classmethod
248+ def validate_type (cls , purl , strict = False ):
249+ if purl .namespace and "::" in purl .name :
250+ yield f"Name must not contain '::' when Namespace is absent for purl type: { cls .type !r} "
251+ if not purl .namespace and "-" in purl .name :
252+ yield f"Name must not contain '-' when Namespace is absent for purl type: { cls .type !r} "
253+ yield from super ().validate_type (purl , strict )
254+
246255
247256class CranTypeValidator (TypeValidator ):
248257 type = "cran"
@@ -355,6 +364,12 @@ class HackageTypeValidator(TypeValidator):
355364 version_case_sensitive = True
356365 purl_pattern = "pkg:hackage/.*"
357366
367+ @classmethod
368+ def validate_type (cls , purl , strict = False ):
369+ if "_" in purl .name :
370+ yield f"Name contains underscores but should be kebab-case for purl type: { cls .type !r} "
371+ yield from super ().validate_type (purl , strict )
372+
358373
359374class HexTypeValidator (TypeValidator ):
360375 type = "hex"
@@ -481,6 +496,14 @@ class PubTypeValidator(TypeValidator):
481496 version_case_sensitive = True
482497 purl_pattern = "pkg:pub/.*"
483498
499+ @classmethod
500+ def validate_type (cls , purl , strict = False ):
501+ if any (not (c .islower () or c .isdigit () or c == "_" ) for c in purl .name ):
502+ yield f"Name contains invalid characters but should only contain lowercase letters, digits, or underscores for purl type: { cls .type !r} "
503+ if " " in purl .name :
504+ yield f"Name contains spaces but should use underscores instead for purl type: { cls .type !r} "
505+ yield from super ().validate_type (purl , strict )
506+
484507
485508class PypiTypeValidator (TypeValidator ):
486509 type = "pypi"
@@ -495,6 +518,12 @@ class PypiTypeValidator(TypeValidator):
495518 version_case_sensitive = True
496519 purl_pattern = "pkg:pypi/.*"
497520
521+ @classmethod
522+ def validate_type (cls , purl , strict = False ):
523+ if "_" in purl .name :
524+ yield f"Name cannot contain `_` for purl type:{ cls .type !r} "
525+ yield from super ().validate_type (purl , strict )
526+
498527
499528class QpkgTypeValidator (TypeValidator ):
500529 type = "qpkg"
0 commit comments