or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

bundle-management.mdcaching-versioning.mdcommand-line.mdconfiguration-loading.mdenvironment-configuration.mdfilter-system.mdframework-integration.mdindex.mdmerge-system.mdupdater-system.mdutilities.md

utilities.mddocs/

0

# Utilities

1

2

Essential utility functions and classes that provide core infrastructure for webassets, including path operations, context managers, object resolution, registry management, and security features.

3

4

## Capabilities

5

6

### Hash Functions

7

8

```python { .api }

9

def hash_func(data):

10

"""

11

Create hash from arbitrary data using webassets cache system.

12

13

Args:

14

data: Data to hash (can be complex objects)

15

16

Returns:

17

str: MD5 hash string

18

"""

19

20

md5_constructor = hashlib.md5

21

```

22

23

### Path Operations

24

25

```python { .api }

26

def common_path_prefix(paths, sep=os.path.sep):

27

"""

28

Find common directory path prefix from multiple paths.

29

30

Improved version of os.path.commonpath() that handles both

31

forward and backward slashes correctly.

32

33

Args:

34

paths: List of file paths

35

sep: Path separator to use in result (default: os.path.sep)

36

37

Returns:

38

str: Common directory prefix

39

"""

40

41

def is_url(s):

42

"""

43

Check if string is a valid URL.

44

45

Args:

46

s: String to check

47

48

Returns:

49

bool: True if string has valid URL scheme and netloc

50

"""

51

```

52

53

### Context Managers

54

55

```python { .api }

56

@contextlib.contextmanager

57

def working_directory(directory=None, filename=None):

58

"""

59

Context manager to temporarily change working directory.

60

61

Useful for filters that need to run in specific directories.

62

Restores original directory on exit.

63

64

Args:

65

directory: Directory to change to

66

filename: File path (directory will be extracted)

67

68

Note: Exactly one of directory or filename must be provided.

69

70

Usage:

71

with working_directory('/path/to/dir'):

72

# commands run in /path/to/dir

73

# back to original directory

74

"""

75

```

76

77

### Object Resolution System

78

79

```python { .api }

80

def make_option_resolver(clazz=None, attribute=None, classes=None,

81

allow_none=True, desc=None):

82

"""

83

Create a function that resolves options to objects.

84

85

The resolver can handle:

86

- Object instances (returned as-is)

87

- Class objects (instantiated)

88

- String identifiers (resolved from registry)

89

- Arguments in format "key:argument"

90

91

Args:

92

clazz: Base class for type checking

93

attribute: Duck-typing attribute to check for

94

classes: Registry dictionary for string resolution

95

allow_none: Whether None values are allowed

96

desc: Description for error messages

97

98

Returns:

99

function: Resolver function that takes (option, env=None)

100

"""

101

```

102

103

### Registry Metaclass

104

105

```python { .api }

106

def RegistryMetaclass(clazz=None, attribute=None, allow_none=True, desc=None):

107

"""

108

Create a metaclass that maintains a registry of subclasses.

109

110

Classes using this metaclass are automatically registered by their

111

'id' attribute and can be resolved from strings.

112

113

Args:

114

clazz: Base class for type checking

115

attribute: Duck-typing attribute

116

allow_none: Whether None resolution is allowed

117

desc: Description for error messages

118

119

Returns:

120

type: Metaclass with REGISTRY dict and resolve() method

121

122

Features:

123

- Automatic subclass registration by 'id' attribute

124

- String-to-class resolution via resolve() method

125

- Automatic __eq__, __str__, __unicode__ methods

126

- Integration with make_option_resolver

127

"""

128

```

129

130

### Debug Level Management

131

132

```python { .api }

133

def cmp_debug_levels(level1, level2):

134

"""

135

Compare debug levels for filter execution.

136

137

Debug levels: False < 'merge' < True

138

139

Args:

140

level1: First debug level

141

level2: Second debug level

142

143

Returns:

144

int: -1 if level1 < level2, 0 if equal, 1 if level1 > level2

145

146

Raises:

147

BundleError: If invalid debug level provided

148

"""

149

```

150

151

### Security Functions

152

153

```python { .api }

154

def calculate_sri(data):

155

"""

156

Calculate Subresource Integrity (SRI) hash for data.

157

158

Args:

159

data: Bytes data to hash

160

161

Returns:

162

str: SRI string in format 'sha384-<base64hash>'

163

"""

164

165

def calculate_sri_on_file(file_name):

166

"""

167

Calculate SRI hash for file contents.

168

169

Args:

170

file_name: Path to file

171

172

Returns:

173

str or None: SRI string, or None if file not found

174

"""

175

```

176

177

### Module Exports

178

179

```python { .api }

180

# Compatibility imports for different Python versions

181

md5_constructor = hashlib.md5

182

set = set # Built-in set for older Python versions

183

StringIO = io.StringIO # String buffer for text operations

184

pickle = pickle # Serialization module

185

```

186

187

## Key Features

188

189

### Flexible Object Resolution

190

191

The option resolver system enables flexible configuration:

192

193

```python

194

# String resolution with arguments

195

resolver = make_option_resolver(classes={'file': FileCache, 'mem': MemoryCache})

196

cache = resolver('file:/tmp/cache', env) # Creates FileCache('/tmp/cache')

197

198

# Class resolution with factory method

199

cache = resolver(FileCache, env) # Uses FileCache.make(env) if available

200

201

# Instance resolution

202

cache = resolver(existing_cache) # Returns existing_cache as-is

203

```

204

205

### Registry Pattern

206

207

The metaclass provides automatic registration:

208

209

```python

210

class BaseUpdater(metaclass=RegistryMetaclass(desc='updater')):

211

pass

212

213

class TimestampUpdater(BaseUpdater):

214

id = 'timestamp' # Automatically registered

215

216

# String resolution

217

updater = BaseUpdater.resolve('timestamp') # Returns TimestampUpdater instance

218

```

219

220

### Cross-Platform Path Handling

221

222

Path utilities work consistently across operating systems:

223

224

```python

225

# Handles mixed separators correctly

226

paths = ['/home/user/project\\assets\\css', '/home/user/project/assets/js']

227

common = common_path_prefix(paths) # '/home/user/project/assets'

228

```

229

230

### Safe Working Directory Changes

231

232

Context manager ensures directory restoration even on exceptions:

233

234

```python

235

original_dir = os.getcwd()

236

with working_directory('/tmp/build'):

237

# Process files in /tmp/build

238

run_build_command()

239

# Exception here still restores directory

240

# Back in original_dir

241

```

242

243

### Security Integration

244

245

SRI support for Content Security Policy compliance:

246

247

```python

248

# Generate SRI for inline content

249

content = "console.log('hello');"

250

sri = calculate_sri(content.encode('utf-8'))

251

# Use in HTML: <script integrity="sha384-...">

252

253

# Generate SRI for files

254

sri = calculate_sri_on_file('app.js')

255

if sri:

256

print(f'<script src="app.js" integrity="{sri}"></script>')

257

```

258

259

## Usage Examples

260

261

### Factory Pattern with Environment

262

263

```python

264

class CustomCache:

265

@classmethod

266

def make(cls, env, path):

267

# Initialize with environment-specific settings

268

return cls(path, debug=env.debug)

269

270

resolver = make_option_resolver(classes={'custom': CustomCache})

271

cache = resolver('custom:/tmp/cache', env) # Uses make() method

272

```

273

274

### Registry-Based Plugin System

275

276

```python

277

class FilterBase(metaclass=RegistryMetaclass(desc='filter')):

278

def apply(self, content):

279

raise NotImplementedError

280

281

class JSMinFilter(FilterBase):

282

id = 'jsmin'

283

def apply(self, content):

284

return minify_js(content)

285

286

# Automatic resolution

287

filter_obj = FilterBase.resolve('jsmin') # Gets JSMinFilter instance

288

```

289

290

### Debug Level Filtering

291

292

```python

293

# Filter selection based on debug level

294

available_filters = [dev_filter, prod_filter, debug_filter]

295

current_level = 'merge'

296

297

active_filters = [f for f in available_filters

298

if cmp_debug_levels(current_level, f.max_debug_level) <= 0]

299

```