or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdexceptions.mdhttp-operations.mdindex.mdnavigation.mdtemplated-links.mdutilities.md

utilities.mddocs/

0

# Utilities

1

2

Enhanced collections and helper functions for HAL processing. RestNavigator provides specialized data structures and utilities that support CURIE prefixes, link disambiguation, and HAL-specific data manipulation.

3

4

## Capabilities

5

6

### Enhanced Collections

7

8

Specialized collection classes that provide HAL-specific functionality for managing links and data.

9

10

```python { .api }

11

class CurieDict(dict):

12

def __init__(self, default_curie: str, d: dict):

13

"""

14

Dictionary supporting CURIE prefixes for link relations.

15

16

Parameters:

17

- default_curie: Default CURIE prefix for unprefixed relations

18

- d: Initial dictionary data

19

"""

20

21

@property

22

def default_curie(self) -> str:

23

"""Default CURIE prefix"""

24

```

25

26

```python { .api }

27

class LinkList(list):

28

def get_by(self, prop: str, val, raise_exc: bool = False):

29

"""

30

Get single item by property value.

31

32

Parameters:

33

- prop: Property name to search by

34

- val: Property value to match

35

- raise_exc: Whether to raise exception if not found

36

37

Returns:

38

First matching item or None

39

"""

40

41

def getall_by(self, prop: str, val) -> list:

42

"""

43

Get all items matching property value.

44

45

Parameters:

46

- prop: Property name to search by

47

- val: Property value to match

48

49

Returns:

50

List of all matching items

51

"""

52

53

def named(self, name: str):

54

"""

55

Get item by name property (HAL standard).

56

57

Parameters:

58

- name: Name value to search for

59

60

Returns:

61

Item with matching name property

62

"""

63

64

def append_with(self, obj, **properties) -> None:

65

"""

66

Add item with metadata properties.

67

68

Parameters:

69

- obj: Object to add to list

70

- **properties: Metadata properties to associate

71

"""

72

```

73

74

### Core Utility Functions

75

76

Essential helper functions for URL processing, data manipulation, and HAL-specific operations.

77

78

```python { .api }

79

def fix_scheme(url: str) -> str:

80

"""

81

Add http:// scheme if missing, validate scheme.

82

83

Parameters:

84

- url: URL to fix

85

86

Returns:

87

URL with proper scheme

88

89

Raises:

90

WileECoyoteException: For invalid schemes

91

ZachMorrisException: For multiple schemes

92

"""

93

94

def normalize_getitem_args(args) -> list:

95

"""

96

Normalize __getitem__ arguments to list.

97

98

Parameters:

99

- args: Arguments from __getitem__ call

100

101

Returns:

102

Normalized list of arguments

103

"""

104

105

def namify(root_uri: str) -> str:

106

"""

107

Convert URI to readable API name.

108

109

Parameters:

110

- root_uri: API root URI

111

112

Returns:

113

Human-readable API name

114

"""

115

116

def objectify_uri(relative_uri: str) -> str:

117

"""

118

Convert URI to object notation string.

119

120

Parameters:

121

- relative_uri: Relative URI path

122

123

Returns:

124

Object notation representation

125

"""

126

127

def parse_media_type(media_type: str) -> tuple:

128

"""

129

Parse HTTP media type header.

130

131

Parameters:

132

- media_type: Media type string from HTTP header

133

134

Returns:

135

Tuple of (type, subtype, parameters)

136

"""

137

138

def getpath(d: dict, json_path: str, default=None, sep: str = '.') -> any:

139

"""

140

Get nested dictionary value by path.

141

142

Parameters:

143

- d: Dictionary to search

144

- json_path: Dot-separated path to value

145

- default: Default value if path not found

146

- sep: Path separator character

147

148

Returns:

149

Value at path or default

150

"""

151

152

def getstate(d: dict) -> dict:

153

"""

154

Deep copy dict removing HAL keys (_links, _embedded).

155

156

Parameters:

157

- d: Dictionary to clean

158

159

Returns:

160

Clean copy without HAL metadata

161

"""

162

```

163

164

## Usage Examples

165

166

### Working with CurieDict

167

168

```python

169

# CurieDict automatically handles CURIE prefixes

170

links = CurieDict('ex', {})

171

172

# These are equivalent when default_curie is 'ex'

173

links['ex:users'] = user_navigator

174

links['users'] = user_navigator # Automatically becomes 'ex:users'

175

176

# Access with or without CURIE

177

user_nav = links['users'] # Works

178

user_nav = links['ex:users'] # Also works

179

180

# IANA standard relations have precedence

181

links['next'] = next_page # Standard 'next' relation

182

links['ex:next'] = custom_next # Custom next relation

183

print(links['next']) # Returns standard 'next', not 'ex:next'

184

```

185

186

### Working with LinkList

187

188

```python

189

# LinkList for managing HAL link arrays

190

links = LinkList()

191

192

# Add links with properties

193

links.append_with(widget1_nav, name='widget1', profile='widget')

194

links.append_with(widget2_nav, name='widget2', profile='widget')

195

links.append_with(gadget_nav, name='gadget1', profile='gadget')

196

197

# Find by property

198

widget1 = links.get_by('name', 'widget1')

199

gadget = links.get_by('profile', 'gadget')

200

201

# Get all matching items

202

all_widgets = links.getall_by('profile', 'widget')

203

204

# Use standard HAL name property

205

specific_item = links.named('gadget1') # Same as get_by('name', 'gadget1')

206

207

# Handle missing items

208

missing = links.get_by('name', 'nonexistent', raise_exc=False) # Returns None

209

```

210

211

### URL and Scheme Utilities

212

213

```python

214

from restnavigator.utils import fix_scheme, namify, objectify_uri

215

216

# Fix URL schemes

217

clean_url = fix_scheme('api.example.com') # Returns 'http://api.example.com'

218

clean_url = fix_scheme('https://api.example.com') # Returns unchanged

219

220

# Generate API names

221

api_name = namify('https://api.github.com/v3') # Returns 'Github'

222

api_name = namify('http://haltalk.herokuapp.com') # Returns 'Haltalk'

223

224

# Convert URIs to object notation

225

obj_notation = objectify_uri('/users/123/posts') # Returns 'users.123.posts'

226

obj_notation = objectify_uri('/api/v1/repos') # Returns 'api.v1.repos'

227

```

228

229

### Data Path Utilities

230

231

```python

232

from restnavigator.utils import getpath, getstate

233

234

# Navigate nested data structures

235

data = {

236

'user': {

237

'profile': {

238

'name': 'John Doe',

239

'settings': {

240

'theme': 'dark'

241

}

242

}

243

}

244

}

245

246

# Get nested values

247

name = getpath(data, 'user.profile.name') # Returns 'John Doe'

248

theme = getpath(data, 'user.profile.settings.theme') # Returns 'dark'

249

missing = getpath(data, 'user.missing.field', 'default') # Returns 'default'

250

251

# Custom separator

252

theme = getpath(data, 'user/profile/settings/theme', sep='/') # Returns 'dark'

253

```

254

255

### HAL Data Cleaning

256

257

```python

258

from restnavigator.utils import getstate

259

260

# Remove HAL metadata from response data

261

hal_response = {

262

'id': 1,

263

'name': 'John Doe',

264

'_links': {

265

'self': {'href': '/users/1'},

266

'posts': {'href': '/users/1/posts'}

267

},

268

'_embedded': {

269

'posts': [

270

{

271

'id': 1,

272

'title': 'First Post',

273

'_links': {'self': {'href': '/posts/1'}}

274

}

275

]

276

}

277

}

278

279

# Get clean state without HAL metadata

280

clean_data = getstate(hal_response)

281

# Returns: {'id': 1, 'name': 'John Doe'}

282

```

283

284

### Media Type Parsing

285

286

```python

287

from restnavigator.utils import parse_media_type

288

289

# Parse content-type headers

290

main_type, sub_type, params = parse_media_type('application/hal+json; charset=utf-8')

291

# main_type: 'application'

292

# sub_type: 'hal+json'

293

# params: {'charset': 'utf-8'}

294

295

# Handle complex media types

296

main_type, sub_type, params = parse_media_type('text/html; charset=utf-8; boundary=something')

297

# params: {'charset': 'utf-8', 'boundary': 'something'}

298

```

299

300

### Navigation Argument Processing

301

302

```python

303

from restnavigator.utils import normalize_getitem_args

304

305

# Normalize different argument patterns

306

args1 = normalize_getitem_args('single') # Returns ['single']

307

args2 = normalize_getitem_args(('a', 'b')) # Returns ['a', 'b']

308

args3 = normalize_getitem_args(['x', 'y', 'z']) # Returns ['x', 'y', 'z']

309

310

# Used internally for bracket notation like:

311

# api['users', 'posts', 0] # Gets normalized to ['users', 'posts', 0]

312

```

313

314

### Advanced Collection Patterns

315

316

```python

317

# Building dynamic link collections

318

def build_link_collection(api_links):

319

"""Build organized link collection from API links"""

320

organized = {}

321

322

for rel, links in api_links.items():

323

if isinstance(links, list):

324

link_list = LinkList()

325

for link in links:

326

# Extract properties from link metadata

327

props = getattr(link, 'props', {})

328

link_list.append_with(link, **props)

329

organized[rel] = link_list

330

else:

331

organized[rel] = links

332

333

return organized

334

335

# Custom CURIE handling

336

def smart_curie_lookup(curie_dict, relation):

337

"""Smart lookup with fallback logic"""

338

# Try exact match first

339

if relation in curie_dict:

340

return curie_dict[relation]

341

342

# Try with default CURIE

343

if curie_dict.default_curie:

344

full_rel = f"{curie_dict.default_curie}:{relation}"

345

if full_rel in curie_dict:

346

return curie_dict[full_rel]

347

348

# Try without CURIE if relation includes one

349

if ':' in relation:

350

bare_rel = relation.split(':', 1)[1]

351

if bare_rel in curie_dict:

352

return curie_dict[bare_rel]

353

354

return None

355

```