or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdarguments-parameters.mdconfiguration.mdcore-app.mdexceptions.mdindex.mdtypes-validation.md

types-validation.mddocs/

0

# Type System and Validation

1

2

Comprehensive type conversion system with pre-built validated types and custom validation support for robust CLI input handling.

3

4

## Capabilities

5

6

### Core Conversion Function

7

8

Central function for converting command-line tokens to Python types.

9

10

```python { .api }

11

def convert(type_: type, tokens: list[Token]) -> Any:

12

"""

13

Convert command-line tokens to target Python type.

14

15

Parameters

16

----------

17

type_

18

Target Python type for conversion

19

tokens

20

List of Token objects containing user input

21

22

Returns

23

-------

24

Any

25

Converted value of the target type

26

27

Raises

28

------

29

CoercionError

30

If conversion fails or tokens are invalid for the type

31

"""

32

```

33

34

### Environment Variable Utilities

35

36

Parse environment variables into appropriate types.

37

38

```python { .api }

39

def env_var_split(value: str, type_: type) -> list[str]:

40

"""

41

Split environment variable value based on target type.

42

43

Parameters

44

----------

45

value

46

Environment variable string value

47

type_

48

Target type that determines splitting behavior

49

50

Returns

51

-------

52

list[str]

53

List of string tokens for conversion

54

"""

55

```

56

57

### Pre-built Path Types

58

59

Validated Path types with existence and type checking.

60

61

```python { .api }

62

# Basic path types

63

ExistingPath = Annotated[Path, ...]

64

"""Path that must exist."""

65

66

NonExistentPath = Annotated[Path, ...]

67

"""Path that must not exist."""

68

69

ExistingFile = Annotated[Path, ...]

70

"""File path that must exist."""

71

72

NonExistentFile = Annotated[Path, ...]

73

"""File path that must not exist."""

74

75

ExistingDirectory = Annotated[Path, ...]

76

"""Directory path that must exist."""

77

78

NonExistentDirectory = Annotated[Path, ...]

79

"""Directory path that must not exist."""

80

81

File = Annotated[Path, ...]

82

"""Generic file path type."""

83

84

Directory = Annotated[Path, ...]

85

"""Generic directory path type."""

86

87

# Resolved path types

88

ResolvedPath = Annotated[Path, ...]

89

"""Path resolved to absolute form."""

90

91

ResolvedExistingPath = Annotated[Path, ...]

92

"""Existing path resolved to absolute form."""

93

94

ResolvedExistingFile = Annotated[Path, ...]

95

"""Existing file path resolved to absolute form."""

96

97

ResolvedExistingDirectory = Annotated[Path, ...]

98

"""Existing directory path resolved to absolute form."""

99

100

ResolvedDirectory = Annotated[Path, ...]

101

"""Directory path resolved to absolute form."""

102

103

ResolvedFile = Annotated[Path, ...]

104

"""File path resolved to absolute form."""

105

106

# Extension-specific path types

107

BinPath = Annotated[Path, ...]

108

"""Binary file path."""

109

110

CsvPath = Annotated[Path, ...]

111

"""CSV file path."""

112

113

ImagePath = Annotated[Path, ...]

114

"""Image file path."""

115

116

JsonPath = Annotated[Path, ...]

117

"""JSON file path."""

118

119

Mp4Path = Annotated[Path, ...]

120

"""MP4 video file path."""

121

122

TomlPath = Annotated[Path, ...]

123

"""TOML configuration file path."""

124

125

TxtPath = Annotated[Path, ...]

126

"""Text file path."""

127

128

YamlPath = Annotated[Path, ...]

129

"""YAML configuration file path."""

130

131

# Extension-specific path types with existence constraints

132

ExistingBinPath = Annotated[Path, ...]

133

"""Binary file path that must exist."""

134

135

NonExistentBinPath = Annotated[Path, ...]

136

"""Binary file path that must not exist."""

137

138

ExistingCsvPath = Annotated[Path, ...]

139

"""CSV file path that must exist."""

140

141

NonExistentCsvPath = Annotated[Path, ...]

142

"""CSV file path that must not exist."""

143

144

ExistingImagePath = Annotated[Path, ...]

145

"""Image file path that must exist."""

146

147

NonExistentImagePath = Annotated[Path, ...]

148

"""Image file path that must not exist."""

149

150

ExistingJsonPath = Annotated[Path, ...]

151

"""JSON file path that must exist."""

152

153

NonExistentJsonPath = Annotated[Path, ...]

154

"""JSON file path that must not exist."""

155

156

ExistingMp4Path = Annotated[Path, ...]

157

"""MP4 file path that must exist."""

158

159

NonExistentMp4Path = Annotated[Path, ...]

160

"""MP4 file path that must not exist."""

161

162

ExistingTomlPath = Annotated[Path, ...]

163

"""TOML file path that must exist."""

164

165

NonExistentTomlPath = Annotated[Path, ...]

166

"""TOML file path that must not exist."""

167

168

ExistingTxtPath = Annotated[Path, ...]

169

"""Text file path that must exist."""

170

171

NonExistentTxtPath = Annotated[Path, ...]

172

"""Text file path that must not exist."""

173

174

ExistingYamlPath = Annotated[Path, ...]

175

"""YAML file path that must exist."""

176

177

NonExistentYamlPath = Annotated[Path, ...]

178

"""YAML file path that must not exist."""

179

```

180

181

### Pre-built Numeric Types

182

183

Validated numeric types with range constraints.

184

185

```python { .api }

186

# Float range types

187

PositiveFloat = Annotated[float, ...]

188

"""Float greater than 0."""

189

190

NonNegativeFloat = Annotated[float, ...]

191

"""Float greater than or equal to 0."""

192

193

NegativeFloat = Annotated[float, ...]

194

"""Float less than 0."""

195

196

NonPositiveFloat = Annotated[float, ...]

197

"""Float less than or equal to 0."""

198

199

# Integer range types

200

PositiveInt = Annotated[int, ...]

201

"""Integer greater than 0."""

202

203

NonNegativeInt = Annotated[int, ...]

204

"""Integer greater than or equal to 0."""

205

206

NegativeInt = Annotated[int, ...]

207

"""Integer less than 0."""

208

209

NonPositiveInt = Annotated[int, ...]

210

"""Integer less than or equal to 0."""

211

212

# Fixed-width integer types

213

UInt8 = Annotated[int, ...]

214

"""8-bit unsigned integer (0-255)."""

215

216

Int8 = Annotated[int, ...]

217

"""8-bit signed integer (-128 to 127)."""

218

219

UInt16 = Annotated[int, ...]

220

"""16-bit unsigned integer (0-65535)."""

221

222

Int16 = Annotated[int, ...]

223

"""16-bit signed integer (-32768 to 32767)."""

224

225

UInt32 = Annotated[int, ...]

226

"""32-bit unsigned integer."""

227

228

Int32 = Annotated[int, ...]

229

"""32-bit signed integer."""

230

231

UInt64 = Annotated[int, ...]

232

"""64-bit unsigned integer."""

233

234

Int64 = Annotated[int, ...]

235

"""64-bit signed integer."""

236

237

# Hexadecimal integer types

238

HexUInt = Annotated[int, ...]

239

"""Unsigned integer from hexadecimal string."""

240

241

HexUInt8 = Annotated[int, ...]

242

"""8-bit unsigned integer from hexadecimal string."""

243

244

HexUInt16 = Annotated[int, ...]

245

"""16-bit unsigned integer from hexadecimal string."""

246

247

HexUInt32 = Annotated[int, ...]

248

"""32-bit unsigned integer from hexadecimal string."""

249

250

HexUInt64 = Annotated[int, ...]

251

"""64-bit unsigned integer from hexadecimal string."""

252

```

253

254

### Other Validated Types

255

256

Additional pre-built types for common use cases.

257

258

```python { .api }

259

Json = Annotated[Any, ...]

260

"""JSON string parsed to Python object."""

261

262

Email = Annotated[str, ...]

263

"""Email address with validation."""

264

265

Port = Annotated[int, ...]

266

"""Network port number (1-65535)."""

267

268

URL = Annotated[str, ...]

269

"""URL with validation."""

270

```

271

272

### Validator Classes

273

274

Built-in validators for custom validation logic.

275

276

```python { .api }

277

class Number:

278

def __init__(

279

self,

280

min: float | None = None,

281

max: float | None = None,

282

min_inclusive: bool = True,

283

max_inclusive: bool = True

284

):

285

"""

286

Numeric range validator.

287

288

Parameters

289

----------

290

min

291

Minimum allowed value

292

max

293

Maximum allowed value

294

min_inclusive

295

Whether minimum is inclusive

296

max_inclusive

297

Whether maximum is inclusive

298

"""

299

300

def __call__(self, value: float) -> float:

301

"""Validate numeric value against range constraints."""

302

303

class Path:

304

def __init__(

305

self,

306

exists: bool | None = None,

307

file_okay: bool = True,

308

dir_okay: bool = True,

309

resolve: bool = False

310

):

311

"""

312

Path validator with existence and type checking.

313

314

Parameters

315

----------

316

exists

317

Whether path must exist (True), not exist (False), or either (None)

318

file_okay

319

Whether files are allowed

320

dir_okay

321

Whether directories are allowed

322

resolve

323

Whether to resolve path to absolute form

324

"""

325

326

def __call__(self, value: Path) -> Path:

327

"""Validate path against constraints."""

328

329

class LimitedChoice:

330

def __init__(self, *choices: Any, case_sensitive: bool = True):

331

"""

332

Limit value to specific choices.

333

334

Parameters

335

----------

336

choices

337

Valid choice values

338

case_sensitive

339

Whether string comparison is case sensitive

340

"""

341

342

def __call__(self, value: Any) -> Any:

343

"""Validate value is in allowed choices."""

344

345

class MutuallyExclusive:

346

def __init__(self, *groups: str):

347

"""

348

Ensure mutual exclusivity between parameter groups.

349

350

Parameters

351

----------

352

groups

353

Parameter group names that are mutually exclusive

354

"""

355

356

def __call__(self, namespace: dict) -> dict:

357

"""Validate mutual exclusivity constraints."""

358

```

359

360

### Validator Functions

361

362

Standalone validator functions for common patterns.

363

364

```python { .api }

365

def mutually_exclusive(*groups: str) -> Callable:

366

"""

367

Create validator for mutually exclusive parameter groups.

368

369

Parameters

370

----------

371

groups

372

Parameter group names that are mutually exclusive

373

374

Returns

375

-------

376

Callable

377

Validator function

378

"""

379

380

def all_or_none(*parameters: str) -> Callable:

381

"""

382

Create validator requiring all or none of the specified parameters.

383

384

Parameters

385

----------

386

parameters

387

Parameter names with all-or-none constraint

388

389

Returns

390

-------

391

Callable

392

Validator function

393

"""

394

```

395

396

## Usage Examples

397

398

### Custom Type with Validation

399

400

```python

401

from cyclopts import App, Parameter

402

from cyclopts.validators import Number

403

from typing import Annotated

404

405

# Custom type with range validation

406

Percentage = Annotated[float, Number(min=0.0, max=100.0)]

407

408

app = App()

409

410

@app.command

411

def set_threshold(value: Percentage):

412

"""Set threshold as a percentage."""

413

print(f"Threshold set to {value}%")

414

```

415

416

### Using Pre-built Types

417

418

```python

419

from cyclopts import App

420

from cyclopts.types import ExistingFile, PositiveInt, Email

421

422

app = App()

423

424

@app.command

425

def process_user_data(

426

input_file: ExistingFile,

427

max_records: PositiveInt,

428

notify_email: Email

429

):

430

"""Process user data file."""

431

print(f"Processing {input_file} (max {max_records} records)")

432

print(f"Will notify {notify_email}")

433

```

434

435

### Custom Converter Example

436

437

```python

438

from cyclopts import App, Parameter

439

from datetime import datetime

440

441

def parse_date(value: str) -> datetime:

442

"""Custom date converter."""

443

return datetime.strptime(value, "%Y-%m-%d")

444

445

app = App()

446

447

@app.command

448

def schedule_task(

449

name: str,

450

due_date: datetime = Parameter(converter=parse_date, help="Date in YYYY-MM-DD format")

451

):

452

"""Schedule a task with due date."""

453

print(f"Task '{name}' scheduled for {due_date.strftime('%Y-%m-%d')}")

454

```

455

456

### Multiple Validators

457

458

```python

459

from cyclopts import App, Parameter

460

from cyclopts.validators import Number, LimitedChoice

461

462

app = App()

463

464

@app.command

465

def configure_server(

466

port: int = Parameter(

467

validator=[

468

Number(min=1024, max=65535),

469

LimitedChoice(8080, 8443, 9000, 9443)

470

],

471

help="Server port (must be 1024-65535 and one of: 8080, 8443, 9000, 9443)"

472

)

473

):

474

"""Configure server with validated port."""

475

print(f"Server configured on port {port}")

476

```