or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

build-integration.mdbuiltin-methods.mdcore-operations.mddata-models.mdexceptions.mdindex.mdmethod-system.mdversioningit-class.md

method-system.mddocs/

0

# Method System

1

2

Framework for loading and executing custom methods via entry points, module imports, or direct callables. This system enables complete customization of versioningit's pipeline steps while maintaining a consistent interface.

3

4

## Capabilities

5

6

### Method Specifications

7

8

Abstract specifications for different ways to reference methods in configuration.

9

10

```python { .api }

11

class MethodSpec(ABC):

12

"""

13

Abstract base class for method specifications parsed from versioningit

14

configurations.

15

"""

16

17

@abstractmethod

18

def load(self, project_dir: str | Path) -> Callable:

19

"""

20

Load & return the callable specified by the MethodSpec.

21

22

Parameters:

23

- project_dir: Project directory for loading project-local methods

24

25

Returns:

26

Callable: The loaded method function

27

"""

28

```

29

30

### Entry Point Methods

31

32

Load methods from Python packaging entry points registered by packages.

33

34

```python { .api }

35

@dataclass

36

class EntryPointSpec(MethodSpec):

37

"""

38

A parsed method specification identifying a Python packaging entry point.

39

"""

40

41

group: str

42

"""The name of the group in which to look up the entry point."""

43

44

name: str

45

"""The name of the entry point."""

46

47

def load(self, _project_dir: str | Path) -> Callable:

48

"""

49

Loads & returns the entry point.

50

51

Returns:

52

Callable: The loaded entry point function

53

54

Raises:

55

- ConfigError: if no such entry point exists

56

- MethodError: if the loaded entry point is not callable

57

"""

58

```

59

60

### Custom Module Methods

61

62

Load methods from local Python modules within the project.

63

64

```python { .api }

65

@dataclass

66

class CustomMethodSpec(MethodSpec):

67

"""

68

A parsed method specification identifying a callable in a local Python

69

module.

70

"""

71

72

module: str

73

"""The dotted name of the module containing the callable."""

74

75

value: str

76

"""The name of the callable object within the module."""

77

78

module_dir: Optional[str]

79

"""The directory in which the module is located; defaults to project_dir."""

80

81

def load(self, project_dir: str | Path) -> Callable:

82

"""

83

Loads the module and returns the callable.

84

85

Parameters:

86

- project_dir: Project directory added to sys.path for import

87

88

Returns:

89

Callable: The loaded method function

90

91

Raises:

92

- MethodError: if the object is not actually callable

93

"""

94

```

95

96

### Direct Callable Methods

97

98

Wrap already-loaded callable objects.

99

100

```python { .api }

101

@dataclass

102

class CallableSpec(MethodSpec):

103

"""

104

A parsed method specification identifying a callable by the callable itself.

105

"""

106

107

func: Callable

108

"""The callable."""

109

110

def load(self, _project_dir: str | Path) -> Callable:

111

"""Return the callable."""

112

```

113

114

### Method Execution

115

116

Loaded method with user-supplied parameters for pipeline execution.

117

118

```python { .api }

119

@dataclass

120

class VersioningitMethod:

121

"""

122

A loaded versioningit method and the user-supplied parameters to pass to it.

123

"""

124

125

method: Callable

126

"""The loaded method."""

127

128

params: dict[str, Any]

129

"""User-supplied parameters obtained from the original configuration."""

130

131

def __call__(self, **kwargs: Any) -> Any:

132

"""

133

Invokes the method with the given keyword arguments and the

134

user-supplied parameters.

135

136

Parameters:

137

- **kwargs: Step-specific arguments (e.g., tag, version, description)

138

139

Returns:

140

Any: Return value from the method (type depends on pipeline step)

141

"""

142

```

143

144

## Entry Point Groups

145

146

Versioningit uses these entry point groups for built-in method discovery:

147

148

```python { .api }

149

# Entry point groups used by versioningit

150

ENTRY_POINT_GROUPS = {

151

"versioningit.vcs": "VCS querying methods",

152

"versioningit.tag2version": "Tag to version conversion methods",

153

"versioningit.next_version": "Next version calculation methods",

154

"versioningit.format": "Version formatting methods",

155

"versioningit.template_fields": "Template field generation methods",

156

"versioningit.write": "File writing methods",

157

"versioningit.onbuild": "Build-time file modification methods"

158

}

159

```

160

161

## Usage Examples

162

163

### Entry Point Method Configuration

164

165

```python

166

# Using built-in entry points

167

config = {

168

"vcs": {"method": "git"},

169

"tag2version": {"method": "basic"},

170

"next-version": {"method": "minor"},

171

"format": {"method": "basic"},

172

"template-fields": {"method": "basic"},

173

"write": {"method": "basic"},

174

"onbuild": {"method": "replace-version"}

175

}

176

```

177

178

### Custom Module Method Configuration

179

180

```python

181

# Custom method in project module

182

config = {

183

"tag2version": {

184

"method": "mypackage.versioning:custom_tag2version",

185

"strip-prefix": "release-"

186

}

187

}

188

189

# Method in separate directory

190

config = {

191

"format": {

192

"method": "scripts.version_tools:custom_format",

193

"module-dir": "build_scripts"

194

}

195

}

196

```

197

198

### Implementing Custom Methods

199

200

```python

201

# Custom VCS method

202

def custom_vcs_method(*, project_dir: Path, params: dict[str, Any]) -> VCSDescription:

203

"""Custom VCS implementation."""

204

# Your custom logic here

205

return VCSDescription(

206

tag="v1.0.0",

207

state="exact",

208

branch="main",

209

fields={"custom_field": "value"}

210

)

211

212

# Custom tag2version method

213

def custom_tag2version(*, tag: str, params: dict[str, Any]) -> str:

214

"""Custom tag to version conversion."""

215

prefix = params.get("strip-prefix", "")

216

if tag.startswith(prefix):

217

tag = tag[len(prefix):]

218

return tag.lstrip("v")

219

220

# Custom format method

221

def custom_format(

222

*,

223

description: VCSDescription,

224

base_version: str,

225

next_version: str,

226

params: dict[str, Any]

227

) -> str:

228

"""Custom version formatting."""

229

if description.state == "exact":

230

return base_version

231

elif description.state == "distance":

232

distance = description.fields.get("distance", 0)

233

return f"{next_version}.dev{distance}"

234

else:

235

return f"{next_version}.dev0+dirty"

236

```

237

238

### Creating Entry Point Methods

239

240

```python

241

# setup.py or pyproject.toml

242

[project.entry-points."versioningit.tag2version"]

243

mymethod = "mypackage.versioning:my_tag2version"

244

245

[project.entry-points."versioningit.format"]

246

myformat = "mypackage.versioning:my_format"

247

```

248

249

### Direct Callable Configuration

250

251

```python

252

from versioningit import Versioningit

253

from versioningit.methods import CallableSpec, VersioningitMethod

254

255

def my_custom_method(*, tag: str, params: dict[str, Any]) -> str:

256

return tag.replace("release-", "")

257

258

# Create method spec

259

callable_spec = CallableSpec(func=my_custom_method)

260

method = VersioningitMethod(

261

method=callable_spec.load("."),

262

params={}

263

)

264

265

# Use in configuration dict format

266

config = {

267

"vcs": {"method": "git"},

268

"tag2version": my_custom_method, # Direct callable

269

"format": {"method": "basic"}

270

}

271

```

272

273

### Method Loading and Execution

274

275

```python

276

from versioningit.methods import EntryPointSpec, CustomMethodSpec

277

278

# Load entry point method

279

entry_spec = EntryPointSpec(group="versioningit.tag2version", name="basic")

280

basic_tag2version = entry_spec.load(".")

281

282

# Load custom method

283

custom_spec = CustomMethodSpec(

284

module="mypackage.versioning",

285

value="custom_tag2version",

286

module_dir=None

287

)

288

custom_tag2version = custom_spec.load("/path/to/project")

289

290

# Execute methods

291

result1 = basic_tag2version(tag="v1.2.3", params={"rmprefix": "v"})

292

result2 = custom_tag2version(tag="release-1.2.3", params={"strip-prefix": "release-"})

293

```

294

295

### Error Handling in Method System

296

297

```python

298

from versioningit.methods import EntryPointSpec

299

from versioningit.errors import ConfigError, MethodError

300

301

try:

302

spec = EntryPointSpec(group="versioningit.tag2version", name="nonexistent")

303

method = spec.load(".")

304

except ConfigError as e:

305

print(f"Entry point not found: {e}")

306

307

try:

308

spec = CustomMethodSpec(

309

module="nonexistent_module",

310

value="some_function",

311

module_dir=None

312

)

313

method = spec.load(".")

314

except (ImportError, AttributeError) as e:

315

print(f"Failed to load custom method: {e}")

316

except MethodError as e:

317

print(f"Loaded object is not callable: {e}")

318

```

319

320

### Advanced Method System Usage

321

322

```python

323

from versioningit.core import Versioningit

324

from versioningit.config import Config

325

from versioningit.methods import CallableSpec, VersioningitMethod

326

327

# Custom pipeline with mix of methods

328

def custom_vcs(*, project_dir: Path, params: dict) -> VCSDescription:

329

# Custom VCS logic

330

pass

331

332

def custom_formatter(*, description: VCSDescription, base_version: str,

333

next_version: str, params: dict) -> str:

334

# Custom formatting logic

335

pass

336

337

# Build configuration programmatically

338

config_dict = {

339

"vcs": custom_vcs, # Direct callable

340

"tag2version": {"method": "basic"}, # Entry point

341

"next-version": {"method": "mypackage.custom:next_version"}, # Custom module

342

"format": custom_formatter, # Direct callable

343

"template-fields": {"method": "basic"},

344

}

345

346

# Create Versioningit instance

347

vgit = Versioningit.from_project_dir(config=config_dict)

348

version = vgit.get_version()

349

```

350

351

### Method Parameter Validation

352

353

```python

354

def validated_method(*, tag: str, params: dict[str, Any]) -> str:

355

"""Example method with parameter validation."""

356

357

# Validate required parameters

358

if "required_param" not in params:

359

raise ConfigError("required_param is mandatory")

360

361

# Validate parameter types

362

if not isinstance(params.get("numeric_param", 0), int):

363

raise ConfigError("numeric_param must be an integer")

364

365

# Validate parameter values

366

valid_options = ["option1", "option2", "option3"]

367

if params.get("choice_param") not in valid_options:

368

raise ConfigError(f"choice_param must be one of {valid_options}")

369

370

# Method implementation

371

return tag.lstrip("v")

372

```