|  | 
|  | 1 | +#!/usr/bin/env python | 
|  | 2 | +"""A simple script to update the version embedded in the source.""" | 
|  | 3 | + | 
|  | 4 | +import argparse | 
|  | 5 | +import pathlib | 
|  | 6 | +import re | 
|  | 7 | + | 
|  | 8 | +from packaging.version import Version | 
|  | 9 | + | 
|  | 10 | + | 
|  | 11 | +def main() -> None: | 
|  | 12 | +    parser = argparse.ArgumentParser(description=__doc__) | 
|  | 13 | +    parser.add_argument('version', type=Version, help='version number to embed in source files') | 
|  | 14 | +    args = parser.parse_args() | 
|  | 15 | +    version: Version = args.version | 
|  | 16 | + | 
|  | 17 | +    major_minor_version = f'{version.major}.{version.minor}' | 
|  | 18 | +    version_info = ', '.join(map(repr, get_version_info(version))) | 
|  | 19 | + | 
|  | 20 | +    updates: list[tuple[str, re.Pattern, str | Version]] = [ | 
|  | 21 | +        ('doc/source/conf.py', re.compile(r"^(version = ')[^']*(')$", flags=re.MULTILINE), major_minor_version), | 
|  | 22 | +        ('doc/source/conf.py', re.compile(r"^(release = ')[^']*(')$", flags=re.MULTILINE), version), | 
|  | 23 | +        ('setup.py', re.compile(r"^( +version=')[^']*(',)$", flags=re.MULTILINE), version), | 
|  | 24 | +        ('src/c/_cffi_backend.c', re.compile(r'^(#define CFFI_VERSION +")[^"]*(")$', flags=re.MULTILINE), version), | 
|  | 25 | +        ('src/c/test_c.py', re.compile(r'^(assert __version__ == ")[^"]*(", .*)$', flags=re.MULTILINE), version), | 
|  | 26 | +        ('src/cffi/__init__.py', re.compile(r'^(__version__ = ")[^"]*(")$', flags=re.MULTILINE), version), | 
|  | 27 | +        ('src/cffi/__init__.py', re.compile(r'^(__version_info__ = \()[^)]*(\))$', flags=re.MULTILINE), version_info), | 
|  | 28 | +        ('src/cffi/_embedding.h', re.compile(r'^( +"\\ncompiled with cffi version: )[^"]*(")$', flags=re.MULTILINE), version), | 
|  | 29 | +    ] | 
|  | 30 | + | 
|  | 31 | +    repo_root = pathlib.Path(__file__).parent.parent | 
|  | 32 | + | 
|  | 33 | +    for relative_path, pattern, replacement in updates: | 
|  | 34 | +        path = repo_root / relative_path | 
|  | 35 | +        original_content = path.read_text() | 
|  | 36 | + | 
|  | 37 | +        if not pattern.search(original_content): | 
|  | 38 | +            raise RuntimeError(f'{relative_path}: no match found for pattern: {pattern.pattern}') | 
|  | 39 | + | 
|  | 40 | +        updated_content = pattern.sub(rf'\g<1>{replacement}\g<2>', original_content) | 
|  | 41 | + | 
|  | 42 | +        if updated_content == original_content: | 
|  | 43 | +            print(f'{relative_path}: unchanged') | 
|  | 44 | +        else: | 
|  | 45 | +            path.write_text(updated_content) | 
|  | 46 | +            print(f'{relative_path}: updated') | 
|  | 47 | + | 
|  | 48 | + | 
|  | 49 | +def get_version_info(version: Version) -> tuple: | 
|  | 50 | +    """Return a tuple representing the given version.""" | 
|  | 51 | +    version_info = list(version.release) | 
|  | 52 | + | 
|  | 53 | +    if version.pre is not None: | 
|  | 54 | +        version_info.append(''.join(map(str, version.pre))) | 
|  | 55 | + | 
|  | 56 | +    if version.post is not None: | 
|  | 57 | +        version_info.append(f'post{version.post}') | 
|  | 58 | + | 
|  | 59 | +    if version.dev is not None: | 
|  | 60 | +        version_info.append(f'dev{version.dev}') | 
|  | 61 | + | 
|  | 62 | +    if version.local is not None: | 
|  | 63 | +        version_info.append(f'+{version.local}') | 
|  | 64 | + | 
|  | 65 | +    return tuple(version_info) | 
|  | 66 | + | 
|  | 67 | + | 
|  | 68 | +if __name__ == '__main__': | 
|  | 69 | +    main() | 
0 commit comments