or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-operations.mdecosystem-utilities.mdframework-integrations.mdindex.mdurl-conversion.md

ecosystem-utilities.mddocs/

0

# Ecosystem-Specific Utilities

1

2

Helper functions for specific package ecosystems and specialized use cases, including Go module handling and custom routing functionality. These utilities provide ecosystem-specific logic for parsing and constructing PackageURLs according to different package manager conventions.

3

4

## Capabilities

5

6

### Go Module Utilities

7

8

Specialized utilities for working with Go modules and packages, supporting both go.mod format and import path parsing.

9

10

```python { .api }

11

from packageurl.utils import get_golang_purl

12

13

def get_golang_purl(go_package: str):

14

"""

15

Create a PackageURL object from a Go package import path or go.mod entry.

16

17

Supports both import paths and versioned go.mod entries:

18

- Import path: "github.com/gorilla/mux"

19

- go.mod entry: "github.com/gorilla/mux v1.8.1"

20

21

Args:

22

go_package (str): Go package import path or "name version" string from go.mod

23

24

Returns:

25

PackageURL | None: PackageURL with type='golang', or None if invalid input

26

27

Raises:

28

Exception: If go_package contains '@' character (invalid for Go modules)

29

"""

30

```

31

32

### Advanced Routing System

33

34

Extended routing capabilities for custom URL pattern matching and processing workflows.

35

36

```python { .api }

37

from packageurl.contrib.route import Router, NoRouteAvailable, Rule, RouteAlreadyDefined, MultipleRoutesDefined

38

39

class Rule:

40

"""

41

A mapping between a URL pattern and a callable endpoint.

42

43

The pattern is a regex string that must match entirely for the rule

44

to be considered and the endpoint to be invoked.

45

"""

46

47

def __init__(self, pattern, endpoint):

48

"""

49

Initialize rule with pattern and endpoint.

50

51

Args:

52

pattern (str): Regular expression pattern to match URLs

53

endpoint (callable): Function or class to handle matched URLs

54

"""

55

56

def match(self, string):

57

"""

58

Match a string with the rule pattern.

59

60

Args:

61

string (str): String to match against pattern

62

63

Returns:

64

Match object or None if no match

65

"""

66

67

class RouteAlreadyDefined(TypeError):

68

"""Raised when a route Rule already exists in the route map."""

69

70

class MultipleRoutesDefined(TypeError):

71

"""Raised when multiple routes match the same string."""

72

73

class Router:

74

"""

75

Advanced URL routing system supporting regex patterns and custom handlers.

76

77

Enables pattern-based URL matching for extensible PURL inference and

78

custom URL processing workflows.

79

"""

80

81

def __init__(self, route_map=None):

82

"""

83

Initialize router with optional route map.

84

85

Args:

86

route_map (dict, optional): Ordered mapping of pattern -> Rule

87

"""

88

89

def append(self, pattern, endpoint):

90

"""

91

Add a URL route with pattern and endpoint function.

92

93

Args:

94

pattern (str): Regular expression pattern to match URLs

95

endpoint (callable): Function to process matched URLs

96

"""

97

98

def process(self, string, *args, **kwargs):

99

"""

100

Process a URL through registered patterns to find matching handler.

101

102

Args:

103

string (str): URL to route and process

104

*args, **kwargs: Additional arguments passed to endpoint

105

106

Returns:

107

Result of the matched endpoint function

108

109

Raises:

110

NoRouteAvailable: If no registered pattern matches the URL

111

"""

112

113

def route(self, *patterns):

114

"""

115

Decorator to make a callable routed to one or more patterns.

116

117

Args:

118

*patterns (str): URL patterns to match

119

120

Returns:

121

Decorator function for registering endpoints

122

"""

123

124

def resolve(self, string):

125

"""

126

Resolve a string to an endpoint function.

127

128

Args:

129

string (str): URL to resolve

130

131

Returns:

132

callable: Endpoint function for the URL

133

134

Raises:

135

NoRouteAvailable: If no pattern matches the URL

136

MultipleRoutesDefined: If multiple patterns match the URL

137

"""

138

139

def is_routable(self, string):

140

"""

141

Check if a string is routable by this router.

142

143

Args:

144

string (str): URL to check

145

146

Returns:

147

bool: True if URL matches any route pattern

148

"""

149

150

def keys(self):

151

"""

152

Return route pattern keys.

153

154

Returns:

155

dict_keys: Pattern keys from the route map

156

"""

157

158

def __iter__(self):

159

"""

160

Iterate over route map items.

161

162

Returns:

163

iterator: Iterator over (pattern, rule) pairs

164

"""

165

166

class NoRouteAvailable(Exception):

167

"""

168

Exception raised when URL routing fails to find a matching pattern.

169

170

Attributes:

171

url (str): The URL that failed to match any route

172

message (str): Error description

173

"""

174

175

def __init__(self, url, message="No route available"):

176

self.url = url

177

self.message = message

178

super().__init__(f"{message}: {url}")

179

```

180

181

### Route Decorators

182

183

Decorator utilities for registering route handlers with routers.

184

185

```python { .api }

186

def route(pattern, router=None):

187

"""

188

Decorator for registering a function as a route handler.

189

190

Args:

191

pattern (str): URL pattern to match

192

router (Router, optional): Router instance to register with

193

194

Returns:

195

Decorated function registered as route handler

196

"""

197

```

198

199

## Usage Examples

200

201

### Go Module Processing

202

203

```python

204

from packageurl.utils import get_golang_purl

205

206

# Parse Go import paths

207

purl1 = get_golang_purl("github.com/gorilla/mux")

208

print(purl1)

209

# PackageURL(type='golang', namespace='github.com/gorilla', name='mux', version=None, qualifiers={}, subpath=None)

210

211

# Parse go.mod entries with versions

212

purl2 = get_golang_purl("github.com/gorilla/mux v1.8.1")

213

print(purl2)

214

# PackageURL(type='golang', namespace='github.com/gorilla', name='mux', version='v1.8.1', qualifiers={}, subpath=None)

215

216

# Handle multi-level namespaces

217

purl3 = get_golang_purl("go.uber.org/zap/zapcore")

218

print(purl3)

219

# PackageURL(type='golang', namespace='go.uber.org/zap', name='zapcore', version=None, qualifiers={}, subpath=None)

220

221

# Handle standard library (empty namespace)

222

purl4 = get_golang_purl("fmt")

223

print(purl4)

224

# PackageURL(type='golang', namespace='', name='fmt', version=None, qualifiers={}, subpath=None)

225

```

226

227

### Advanced Routing

228

229

```python

230

from packageurl.contrib.route import Router, NoRouteAvailable, route

231

from packageurl import PackageURL

232

import re

233

234

# Create and configure router

235

router = Router()

236

237

# Manual route registration

238

def handle_custom_npm(url):

239

"""Handle custom npm registry URLs."""

240

match = re.search(r'/package/(@[^/]+/[^/]+|[^/]+)', url)

241

if match:

242

name = match.group(1)

243

return PackageURL(type="npm", name=name)

244

return None

245

246

router.append(r'https://custom-npm\.example\.com/package/', handle_custom_npm)

247

248

# Decorator-based registration

249

@route(r'https://internal-pypi\.company\.com/simple/([^/]+)/?', router)

250

def handle_internal_pypi(url, match):

251

"""Handle internal PyPI URLs."""

252

package_name = match.group(1)

253

return PackageURL(type="pypi", name=package_name)

254

255

# Route URLs

256

try:

257

npm_purl = router.process("https://custom-npm.example.com/package/@angular/core")

258

pypi_purl = router.process("https://internal-pypi.company.com/simple/requests/")

259

print(f"NPM: {npm_purl}")

260

print(f"PyPI: {pypi_purl}")

261

except NoRouteAvailable as e:

262

print(f"Routing failed: {e}")

263

264

# Check route matches without execution

265

match_result = router.match("https://custom-npm.example.com/package/lodash")

266

if match_result:

267

pattern, handler, groups = match_result

268

print(f"Matched pattern: {pattern}")

269

```

270

271

### Complex Routing Scenarios

272

273

```python

274

from packageurl.contrib.route import Router

275

from packageurl import PackageURL

276

import re

277

from urllib.parse import urlparse, parse_qs

278

279

# Enterprise routing setup

280

enterprise_router = Router()

281

282

def handle_nexus_repository(url):

283

"""Handle Sonatype Nexus repository URLs."""

284

parsed = urlparse(url)

285

path_parts = parsed.path.strip('/').split('/')

286

287

if 'maven' in path_parts:

288

# Maven artifact URL

289

try:

290

idx = path_parts.index('maven')

291

group_parts = path_parts[idx+1:-2]

292

artifact_id = path_parts[-2]

293

version = path_parts[-1]

294

namespace = '.'.join(group_parts)

295

return PackageURL(type="maven", namespace=namespace, name=artifact_id, version=version)

296

except (ValueError, IndexError):

297

return None

298

299

return None

300

301

def handle_artifactory(url):

302

"""Handle JFrog Artifactory URLs."""

303

# Custom Artifactory URL parsing logic

304

match = re.search(r'/artifactory/([^/]+)/(.+)', url)

305

if match:

306

repo_name, path = match.groups()

307

# Parse path based on repository type

308

if 'npm' in repo_name:

309

return PackageURL(type="npm", name=path.split('/')[-1])

310

elif 'pypi' in repo_name:

311

return PackageURL(type="pypi", name=path.split('/')[-1])

312

return None

313

314

# Register enterprise handlers

315

enterprise_router.append(r'https://nexus\.company\.com/repository/', handle_nexus_repository)

316

enterprise_router.append(r'https://artifactory\.company\.com/artifactory/', handle_artifactory)

317

318

# Multi-step routing with fallbacks

319

def route_with_fallback(url, routers):

320

"""Try multiple routers in sequence."""

321

for router in routers:

322

try:

323

return router.process(url)

324

except NoRouteAvailable:

325

continue

326

raise NoRouteAvailable(url, "No router could handle URL")

327

328

# Usage

329

routers = [enterprise_router, purl_router] # purl_router from url2purl module

330

result = route_with_fallback("https://nexus.company.com/repository/maven/org/springframework/spring-core/5.3.21/", routers)

331

print(result)

332

```

333

334

### Ecosystem Pattern Matching

335

336

```python

337

from packageurl.utils import get_golang_purl

338

from packageurl.contrib.route import Router

339

340

# Combine ecosystem utilities with routing

341

golang_router = Router()

342

343

@route(r'https://pkg\.go\.dev/([^@]+)(?:@([^?]+))?', golang_router)

344

def handle_pkg_go_dev(url, match):

345

"""Handle pkg.go.dev URLs."""

346

import_path = match.group(1)

347

version = match.group(2)

348

349

if version:

350

go_spec = f"{import_path} {version}"

351

else:

352

go_spec = import_path

353

354

return get_golang_purl(go_spec)

355

356

# Test Go package URL handling

357

go_purl = golang_router.process("https://pkg.go.dev/github.com/gin-gonic/gin@v1.8.1")

358

print(go_purl)

359

# PackageURL(type='golang', namespace='github.com/gin-gonic', name='gin', version='v1.8.1', qualifiers={}, subpath=None)

360

```

361

362

### Error Handling and Validation

363

364

```python

365

from packageurl.utils import get_golang_purl

366

from packageurl.contrib.route import NoRouteAvailable

367

368

def safe_golang_purl(go_package):

369

"""Safely create Go PURL with error handling."""

370

try:

371

if '@' in go_package:

372

raise ValueError("Go packages should not contain '@' character")

373

374

purl = get_golang_purl(go_package)

375

if purl is None:

376

raise ValueError(f"Could not parse Go package: {go_package}")

377

378

return purl

379

except Exception as e:

380

print(f"Error processing Go package '{go_package}': {e}")

381

return None

382

383

# Safe usage

384

valid_purl = safe_golang_purl("github.com/stretchr/testify v1.7.0")

385

invalid_purl = safe_golang_purl("invalid@package") # Will return None

386

387

def safe_route(router, url):

388

"""Safely route URL with comprehensive error handling."""

389

try:

390

return router.route(url)

391

except NoRouteAvailable as e:

392

print(f"No route available for {url}: {e}")

393

return None

394

except Exception as e:

395

print(f"Routing error for {url}: {e}")

396

return None

397

398

# Safe routing usage

399

result = safe_route(router, "https://unknown-registry.com/package/foo")

400

```