or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdbulk-operations.mdclient-connection.mdcommand-line-interface.mdexceptions.mdindex.mdlow-level-operations.mdutilities.md

utilities.mddocs/

0

# Utility Functions

1

2

Helper functions for temporary URLs, header processing, data formatting, and various Swift-specific operations.

3

4

## Capabilities

5

6

### Temporary URL Generation

7

8

Create time-limited URLs for direct object access without authentication.

9

10

```python { .api }

11

def generate_temp_url(

12

path,

13

seconds,

14

key,

15

method,

16

absolute=False,

17

prefix_based=False,

18

iso8601=False,

19

ip_range=None,

20

digest=None,

21

auth_url=None

22

):

23

"""

24

Generate a temporary URL for Swift object access.

25

26

Parameters:

27

- path: str, object path (e.g., '/v1/AUTH_account/container/object')

28

- seconds: int, URL validity duration in seconds from now

29

- key: str, secret temporary URL key configured on account/container

30

- method: str, HTTP method ('GET', 'PUT', 'POST', 'DELETE')

31

- absolute: bool, return absolute timestamp instead of duration (default False)

32

- prefix_based: bool, allow access to all objects with path prefix (default False)

33

- iso8601: bool, use ISO 8601 timestamp format (default False)

34

- ip_range: str, restrict access to IP range (e.g., '192.168.1.0/24')

35

- digest: str, digest algorithm ('sha1', 'sha256', 'sha512', default 'sha1')

36

- auth_url: str, complete auth URL for absolute URLs

37

38

Returns:

39

str: temporary URL with signature and expiration

40

41

Usage:

42

Set temp URL key on account: X-Account-Meta-Temp-Url-Key: secret_key

43

Or on container: X-Container-Meta-Temp-Url-Key: secret_key

44

"""

45

```

46

47

### Response Processing

48

49

Parse and process Swift API responses.

50

51

```python { .api }

52

def parse_api_response(headers, body):

53

"""

54

Parse Swift API JSON response.

55

56

Parameters:

57

- headers: dict, response headers

58

- body: bytes, response body

59

60

Returns:

61

list or dict: parsed JSON response, or empty list if no body

62

"""

63

64

def get_body(headers, body):

65

"""

66

Get response body content with proper encoding handling.

67

68

Parameters:

69

- headers: dict, response headers

70

- body: bytes, raw response body

71

72

Returns:

73

str: decoded response body

74

"""

75

```

76

77

### Configuration and Formatting

78

79

Utilities for configuration parsing and data formatting.

80

81

```python { .api }

82

def config_true_value(value):

83

"""

84

Check if value represents boolean true.

85

86

Parameters:

87

- value: any, value to check

88

89

Returns:

90

bool: True if value is True or string in ('true', '1', 'yes', 'on', 't', 'y')

91

"""

92

93

def prt_bytes(num_bytes, human_flag):

94

"""

95

Format bytes for human-readable display.

96

97

Parameters:

98

- num_bytes: int, number of bytes

99

- human_flag: bool, use human-readable format (K, M, G suffixes)

100

101

Returns:

102

str: formatted byte string (4 chars for human, 12 chars right-justified otherwise)

103

"""

104

105

def parse_timeout(value):

106

"""

107

Parse timeout strings with suffixes.

108

109

Parameters:

110

- value: str, timeout value with optional suffix ('30s', '5m', '2h', '1d')

111

112

Returns:

113

float: timeout in seconds

114

115

Supported suffixes:

116

- s: seconds

117

- m, min: minutes

118

- h, hr: hours

119

- d: days

120

"""

121

122

def parse_timestamp(seconds, absolute=False):

123

"""

124

Parse timestamp values from various formats.

125

126

Parameters:

127

- seconds: str or float, timestamp value or ISO 8601 string

128

- absolute: bool, return absolute timestamp (default False for relative)

129

130

Returns:

131

float: Unix timestamp

132

133

Raises:

134

ValueError: Invalid timestamp format

135

"""

136

```

137

138

### Header Processing

139

140

Utilities for handling HTTP headers and metadata.

141

142

```python { .api }

143

def split_request_headers(options, prefix=''):

144

"""

145

Split header options into dictionary format.

146

147

Parameters:

148

- options: list, header strings in 'name:value' format

149

- prefix: str, prefix to add to header names

150

151

Returns:

152

dict: headers dictionary with properly formatted names and values

153

"""

154

155

def report_traceback():

156

"""

157

Report exception traceback for debugging.

158

159

Returns:

160

str: formatted traceback string

161

"""

162

```

163

164

### Data Streaming and Processing

165

166

Helper classes and functions for data streaming and processing.

167

168

```python { .api }

169

class ReadableToIterable:

170

def __init__(self, content, chunk_size=65536, md5=False):

171

"""

172

Convert file-like readable object to iterable.

173

174

Parameters:

175

- content: file-like object with read() method

176

- chunk_size: int, size of chunks to read (default 65536)

177

- md5: bool, calculate MD5 hash while reading (default False)

178

"""

179

180

def __iter__(self):

181

"""Iterate over chunks of data."""

182

183

def get_md5sum(self):

184

"""Get MD5 hash if md5=True was specified."""

185

186

class LengthWrapper:

187

def __init__(self, readable, length, md5=False):

188

"""

189

Wrap readable object with length limiting.

190

191

Parameters:

192

- readable: file-like object with read() method

193

- length: int, maximum bytes to read

194

- md5: bool, calculate MD5 hash while reading (default False)

195

"""

196

197

def __iter__(self):

198

"""Iterate over chunks up to specified length."""

199

200

def read(self, amt=None):

201

"""Read up to amt bytes or remaining length."""

202

203

def get_md5sum(self):

204

"""Get MD5 hash if md5=True was specified."""

205

206

class NoopMD5:

207

"""No-operation MD5 hasher for when MD5 is disabled."""

208

209

def update(self, data):

210

"""No-op update method."""

211

212

def hexdigest(self):

213

"""Return empty MD5 hash."""

214

return ""

215

216

class JSONableIterable(list):

217

"""JSON-serializable iterable that extends list."""

218

219

def __init__(self, iterable=None):

220

"""Initialize with optional iterable."""

221

222

def iter_wrapper(iterable):

223

"""

224

Wrap iterable for streaming uploads.

225

226

Parameters:

227

- iterable: any iterable object

228

229

Returns:

230

generator: wrapped iterable suitable for streaming

231

"""

232

233

def n_at_a_time(seq, n):

234

"""

235

Split sequence into chunks of size n.

236

237

Parameters:

238

- seq: sequence to split

239

- n: int, chunk size

240

241

Yields:

242

list: chunks of the sequence

243

"""

244

245

def n_groups(seq, n):

246

"""

247

Split sequence into n groups of roughly equal size.

248

249

Parameters:

250

- seq: sequence to split

251

- n: int, number of groups

252

253

Returns:

254

list: list of groups

255

"""

256

257

def normalize_manifest_path(path):

258

"""

259

Normalize manifest paths for consistency.

260

261

Parameters:

262

- path: str, manifest path to normalize

263

264

Returns:

265

str: normalized path

266

"""

267

```

268

269

### Constants

270

271

```python { .api }

272

TRUE_VALUES = {'true', '1', 'yes', 'on', 't', 'y'}

273

EMPTY_ETAG = 'd41d8cd98f00b204e9800998ecf8427e'

274

EXPIRES_ISO8601_FORMAT = '%Y-%m-%dT%H:%M:%SZ'

275

SHORT_EXPIRES_ISO8601_FORMAT = '%Y-%m-%d'

276

TIME_ERRMSG = 'time must either be a whole number or in specific ISO 8601 format.'

277

```

278

279

## Usage Examples

280

281

### Temporary URL Generation

282

283

```python

284

from swiftclient.utils import generate_temp_url

285

import time

286

287

# Set temporary URL key on account (do this once via swift CLI or API)

288

# swift post -m "Temp-URL-Key:my-secret-key"

289

290

# Generate temporary GET URL valid for 1 hour

291

path = '/v1/AUTH_account/documents/confidential.pdf'

292

temp_url = generate_temp_url(

293

path=path,

294

seconds=3600, # 1 hour

295

key='my-secret-key',

296

method='GET'

297

)

298

299

print(f"Temporary URL: https://swift.example.com{temp_url}")

300

301

# Generate temporary PUT URL for uploads

302

upload_url = generate_temp_url(

303

path='/v1/AUTH_account/uploads/new-file.txt',

304

seconds=1800, # 30 minutes

305

key='my-secret-key',

306

method='PUT'

307

)

308

309

# Generate prefix-based URL for directory access

310

prefix_url = generate_temp_url(

311

path='/v1/AUTH_account/documents/',

312

seconds=7200, # 2 hours

313

key='my-secret-key',

314

method='GET',

315

prefix_based=True # Access all objects under documents/

316

)

317

318

# Generate URL with IP restrictions

319

restricted_url = generate_temp_url(

320

path='/v1/AUTH_account/private/data.csv',

321

seconds=3600,

322

key='my-secret-key',

323

method='GET',

324

ip_range='192.168.1.0/24' # Only allow from this subnet

325

)

326

327

# Use absolute timestamp instead of duration

328

absolute_timestamp = int(time.time()) + 3600 # 1 hour from now

329

absolute_url = generate_temp_url(

330

path=path,

331

seconds=absolute_timestamp,

332

key='my-secret-key',

333

method='GET',

334

absolute=True

335

)

336

```

337

338

### Data Formatting and Configuration

339

340

```python

341

from swiftclient.utils import prt_bytes, config_true_value, parse_timeout

342

343

# Format bytes for display

344

print(prt_bytes(1024, False)) # ' 1024'

345

print(prt_bytes(1024, True)) # '1.0K'

346

print(prt_bytes(1536000, True)) # '1.5M'

347

print(prt_bytes(5368709120, True)) # '5.0G'

348

349

# Check boolean configuration values

350

config_values = ['true', '1', 'yes', 'on', 'false', '0', 'no', 'off']

351

for value in config_values:

352

print(f"'{value}' is {config_true_value(value)}")

353

354

# Parse timeout strings

355

timeouts = ['30', '30s', '5m', '2h', '1d']

356

for timeout in timeouts:

357

seconds = parse_timeout(timeout)

358

print(f"'{timeout}' = {seconds} seconds")

359

```

360

361

### Header Processing

362

363

```python

364

from swiftclient.utils import split_request_headers

365

366

# Parse header options from command line format

367

header_options = [

368

'Content-Type:application/json',

369

'X-Object-Meta-Author:John Doe',

370

'X-Object-Meta-Version:2.0',

371

'Cache-Control:max-age=3600'

372

]

373

374

headers = split_request_headers(header_options)

375

print(headers)

376

# {

377

# 'content-type': 'application/json',

378

# 'x-object-meta-author': 'John Doe',

379

# 'x-object-meta-version': '2.0',

380

# 'cache-control': 'max-age=3600'

381

# }

382

383

# Parse with prefix

384

container_headers = split_request_headers([

385

'Meta-Owner:TeamA',

386

'Read:.r:*',

387

'Write:.r:*'

388

], prefix='X-Container-')

389

390

print(container_headers)

391

# {

392

# 'x-container-meta-owner': 'TeamA',

393

# 'x-container-read': '.r:*',

394

# 'x-container-write': '.r:*'

395

# }

396

```

397

398

### Data Streaming

399

400

```python

401

from swiftclient.utils import ReadableToIterable, LengthWrapper

402

import io

403

404

# Convert file to iterable for streaming upload

405

with open('large_file.dat', 'rb') as f:

406

iterable = ReadableToIterable(f, chunk_size=65536, md5=True)

407

408

# Upload using the iterable (example with low-level function)

409

etag = put_object(storage_url, token, 'container', 'object', iterable)

410

411

# Get MD5 hash

412

md5_hash = iterable.get_md5sum()

413

print(f"Uploaded with ETag: {etag}, MD5: {md5_hash}")

414

415

# Limit reading to specific length

416

data = io.BytesIO(b'x' * 10000) # 10KB of data

417

limited = LengthWrapper(data, 5000, md5=True) # Only read first 5KB

418

419

chunk_count = 0

420

for chunk in limited:

421

chunk_count += 1

422

print(f"Chunk {chunk_count}: {len(chunk)} bytes")

423

424

print(f"MD5 of first 5KB: {limited.get_md5sum()}")

425

```

426

427

### Sequence Processing

428

429

```python

430

from swiftclient.utils import n_at_a_time, n_groups

431

432

# Process items in batches

433

items = list(range(23))

434

435

# Split into chunks of 5

436

for i, chunk in enumerate(n_at_a_time(items, 5)):

437

print(f"Batch {i}: {chunk}")

438

# Batch 0: [0, 1, 2, 3, 4]

439

# Batch 1: [5, 6, 7, 8, 9]

440

# ...

441

442

# Split into 4 roughly equal groups

443

groups = n_groups(items, 4)

444

for i, group in enumerate(groups):

445

print(f"Group {i}: {group} ({len(group)} items)")

446

```

447

448

### Response Processing

449

450

```python

451

from swiftclient.utils import parse_api_response, get_body

452

import json

453

454

# Parse Swift API JSON response

455

headers = {'content-type': 'application/json; charset=utf-8'}

456

body = json.dumps([

457

{'name': 'container1', 'count': 42, 'bytes': 1024000},

458

{'name': 'container2', 'count': 17, 'bytes': 512000}

459

]).encode('utf-8')

460

461

containers = parse_api_response(headers, body)

462

for container in containers:

463

print(f"Container: {container['name']}, Objects: {container['count']}")

464

465

# Get response body with encoding

466

text_body = get_body(headers, body)

467

print(f"Response body: {text_body}")

468

```

469

470

### Advanced Utility Classes

471

472

Specialized utility classes for stream processing, content handling, and data conversion.

473

474

```python { .api }

475

class ReadableToIterable:

476

def __init__(self, content, checksum=None, md5=None, chunk_size=65536):

477

"""

478

Convert file-like objects to iterables suitable for Swift uploads.

479

480

Parameters:

481

- content: file-like object or iterable to convert

482

- checksum: bool, whether to calculate content checksum

483

- md5: hashlib MD5 object, existing MD5 hasher to update

484

- chunk_size: int, chunk size for reading (default 65536)

485

486

Yields:

487

bytes: content chunks for streaming upload

488

"""

489

490

def __iter__(self):

491

"""Return iterator for content chunks."""

492

493

def get_md5sum(self):

494

"""Get MD5 checksum of processed content."""

495

496

class LengthWrapper:

497

def __init__(self, readable, length):

498

"""

499

Wrap readable object with known content length.

500

501

Parameters:

502

- readable: file-like object or iterable

503

- length: int, total content length in bytes

504

505

Used for streaming uploads where content length must be known upfront.

506

"""

507

508

def __iter__(self):

509

"""Return iterator for content."""

510

511

def __len__(self):

512

"""Return content length."""

513

514

def read(self, size=-1):

515

"""Read up to size bytes from content."""

516

517

class JSONableIterable(list):

518

def __init__(self, iterable):

519

"""

520

JSON-serializable iterable that preserves iteration behavior.

521

522

Parameters:

523

- iterable: any iterable to wrap

524

525

Allows iterables to be JSON serialized while maintaining

526

their iteration properties for Swift operations.

527

"""

528

529

class NoopMD5:

530

"""

531

No-operation MD5 hasher for environments where hashlib MD5 is unavailable.

532

533

Provides the same interface as hashlib.md5() but performs no actual hashing.

534

Used as fallback when FIPS mode or other restrictions disable MD5.

535

"""

536

537

def update(self, data):

538

"""Accept data but perform no hashing."""

539

540

def digest(self):

541

"""Return empty digest."""

542

543

def hexdigest(self):

544

"""Return empty hex digest."""

545

```

546

547

### Advanced Utility Usage

548

549

```python

550

from swiftclient.utils import ReadableToIterable, LengthWrapper

551

552

# Convert file to iterable for streaming upload

553

with open('largefile.dat', 'rb') as f:

554

iterable = ReadableToIterable(f, checksum=True)

555

556

# Use in Swift upload

557

conn.put_object('container', 'object', iterable)

558

559

# Get checksum after upload

560

md5_hash = iterable.get_md5sum()

561

562

# Wrap content with known length

563

content = b'Hello, Swift!'

564

wrapped = LengthWrapper(iter([content]), len(content))

565

566

# Use for precise content length uploads

567

conn.put_object(

568

'container',

569

'object',

570

wrapped,

571

content_length=len(wrapped)

572

)

573

```