gitchangelog.gitchangelog module

gitchangelog.gitchangelog.Caret(clbl)[source]
class gitchangelog.gitchangelog.Config[source]

Bases: dict

gitchangelog.gitchangelog.FileFirstRegexMatch(filename, pattern)[source]
gitchangelog.gitchangelog.FileInsertAtFirstRegexMatch(filename, pattern, flags=0, idx=<function <lambda>>)[source]
gitchangelog.gitchangelog.FileRegexSubst(filename, pattern, replace, flags=0)[source]
class gitchangelog.gitchangelog.GitCmd(repos)[source]

Bases: SubGitObjectMixin

class gitchangelog.gitchangelog.GitCommit(repos, identifier)[source]

Bases: SubGitObjectMixin

Represent a Git Commit and expose through its attribute many information

Let’s create a fake GitRepos:

>>> from minimock import Mock
>>> repos = Mock("gitRepos")

Initialization:

>>> repos.git = Mock("gitRepos.git")
>>> repos.git.log.mock_returns_func = \
...     lambda *a, **kwargs: "\x00".join([{
...             'sha1': "000000",
...             'sha1_short': "000",
...             'subject': SUBJECT,
...             'author_name': "John Smith",
...             'author_date': "Tue Feb 14 20:31:22 2017 +0700",
...             'author_email': "john.smith@example.com",
...             'author_date_timestamp': "0",   ## epoch
...             'committer_name': "Alice Wang",
...             'committer_date_timestamp': "0", ## epoch
...             'raw_body': "my subject\n\n%s" % BODY,
...             'body': BODY,
...         }[key] for key in GIT_FORMAT_KEYS.keys()])
>>> repos.git.rev_list.mock_returns = "123456"

Query, by attributes or items:

>>> SUBJECT = "fee fie foh"
>>> BODY = "foo foo foo"
>>> head = GitCommit(repos, "HEAD")
>>> head.subject
Called gitRepos.git.log(...'HEAD'...)
'fee fie foh'
>>> head.author_name
'John Smith'

Notice that on the second call, there’s no need to call again git log as all the values have already been computed.

Trailer

GitCommit offers a simple direct API to trailer values. These are like RFC822’s header value but are at the end of body:

>>> BODY = '''\
... Stuff in the body
... Change-id: 1234
... Value-X: Supports multi
...   line values'''
>>> head = GitCommit(repos, "HEAD")
>>> head.trailer_change_id
Called gitRepos.git.log(...'HEAD'...)
'1234'
>>> head.trailer_value_x
'Supports multi\nline values'

Notice how the multi-line value was unindented. In case of multiple values, these are concatenated in lists:

>>> BODY = '''\
... Stuff in the body
... Co-Authored-By: Bob
... Co-Authored-By: Alice
... Co-Authored-By: Jack
... '''
>>> head = GitCommit(repos, "HEAD")
>>> head.trailer_co_authored_by
Called gitRepos.git.log(...'HEAD'...)
['Bob', 'Alice', 'Jack']

Special values

Authors

>>> BODY = '''\
... Stuff in the body
... Co-Authored-By: Bob
... Co-Authored-By: Alice
... Co-Authored-By: Jack
... '''
>>> head = GitCommit(repos, "HEAD")
>>> head.author_names
Called gitRepos.git.log(...'HEAD'...)
['Alice', 'Bob', 'Jack', 'John Smith']

Notice that they are printed in alphabetical order.

property author_names
property authors
property date
property has_annotated_tag
property tagger_date
property tagger_date_timestamp
class gitchangelog.gitchangelog.GitConfig(repos)[source]

Bases: SubGitObjectMixin

Interface to config values of git

Let’s create a fake GitRepos:

>>> from minimock import Mock
>>> repos = Mock("gitRepos")

Initialization:

>>> cfg = GitConfig(repos)

Query, by attributes or items:

>>> repos.git.config.mock_returns = "bar"
>>> cfg.foo
Called gitRepos.git.config('foo')
'bar'
>>> cfg["foo"]
Called gitRepos.git.config('foo')
'bar'
>>> cfg.get("foo")
Called gitRepos.git.config('foo')
'bar'
>>> cfg["foo.wiz"]
Called gitRepos.git.config('foo.wiz')
'bar'

Notice that you can’t use attribute search in subsection as cfg.foo.wiz That’s because in git config files, you can have a value attached to an element, and this element can also be a section.

Nevertheless, you can do:

>>> getattr(cfg, "foo.wiz")
Called gitRepos.git.config('foo.wiz')
'bar'

Default values

get item, and getattr default values can be used:

>>> del repos.git.config.mock_returns
>>> repos.git.config.mock_raises = ShellError('Key not found',
...                                           errlvl=1, out="", err="")
>>> getattr(cfg, "foo", "default")
Called gitRepos.git.config('foo')
'default'
>>> cfg["foo"]  #
Traceback (most recent call last):
...
KeyError: 'foo'
>>> getattr(cfg, "foo")  #
Traceback (most recent call last):
...
AttributeError...
>>> cfg.get("foo", "default")
Called gitRepos.git.config('foo')
'default'
>>> print("%r" % cfg.get("foo"))
Called gitRepos.git.config('foo')
None
get(label, default=None)[source]
class gitchangelog.gitchangelog.GitRepos(path)[source]

Bases: object

commit(identifier)[source]
property config
classmethod create(directory, *args, **kwargs)[source]
property git
classmethod init(directory, user=None, email=None)[source]
log(includes=None, excludes=None, include_merge=True, encoding='utf-8')[source]

Reverse chronological list of git repository’s commits

Note: rev lists can be GitCommit instance list or identifier list.

tags(contains=None)[source]

String list of repository’s tag names

Current tag order is committer date timestamp of tagged commit. No firm reason for that, and it could change in future version.

gitchangelog.gitchangelog.Indent(*a, **kw)
class gitchangelog.gitchangelog.Phile(filename, buffersize=4096, encoding='utf-8')[source]

Bases: object

File like API to read fields separated by any delimiters

It’ll take care of file decoding to unicode.

This is an adaptor on a file object.

>>> if PY3:
...     from io import BytesIO
...     def File(s):
...         _obj = BytesIO()
...         _obj.write(s.encode(_preferred_encoding))
...         _obj.seek(0)
...         return _obj
... else:
...     from cStringIO import StringIO as File
>>> f = Phile(File("a-b-c-d"))

Read provides an iterator:

>>> def show(l):
...     print(", ".join(l))
>>> show(f.read(delimiter="-"))
a, b, c, d

You can change the buffersize loaded into memory before outputing your changes. It should not change the iterator output:

>>> f = Phile(File("é-à-ü-d"), buffersize=3)
>>> len(list(f.read(delimiter="-")))
4
>>> f = Phile(File("foo-bang-yummy"), buffersize=3)
>>> show(f.read(delimiter="-"))
foo, bang, yummy
>>> f = Phile(File("foo-bang-yummy"), buffersize=1)
>>> show(f.read(delimiter="-"))
foo, bang, yummy
close()[source]
read(delimiter='\n')[source]
write(buf)[source]
class gitchangelog.gitchangelog.Proc(command, env=None, encoding='utf-8')[source]

Bases: Popen

gitchangelog.gitchangelog.ReSub(p, r, **k)
gitchangelog.gitchangelog.SetIfEmpty(*a, **kw)
exception gitchangelog.gitchangelog.ShellError(msg, errlvl=None, command=None, out=None, err=None)[source]

Bases: Exception

class gitchangelog.gitchangelog.SubGitObjectMixin(repos)[source]

Bases: object

property git

Simple delegation to repos original method.

class gitchangelog.gitchangelog.TextProc(fun)[source]

Bases: object

gitchangelog.gitchangelog.Wrap(*a, **kw)
gitchangelog.gitchangelog.available_in_config(f)[source]
gitchangelog.gitchangelog.changelog(output_engine=<function rest_py>, unreleased_version_label='unreleased', warn=<function warn>, **kwargs)[source]

Returns a string containing the changelog of given repository

This function returns a string corresponding to the template rendered with the changelog data tree.

(see gitchangelog.rc.sample file for more info)

For an exact list of arguments, see the arguments of versions_data_iter(..).

Parameters:
  • unreleased_version_label – version label for untagged commits

  • output_engine – callable to render the changelog data

  • warn – callable to output warnings, mocked by tests

Returns:

content of changelog

gitchangelog.gitchangelog.cmd(command, env=None, shell=True)[source]
gitchangelog.gitchangelog.curryfy(f)[source]
gitchangelog.gitchangelog.die(msg=None, errlvl=1)[source]
gitchangelog.gitchangelog.ensure_template_file_exists(label, template_name)[source]

Return template file path given a label hint and the template name

Template name can be either a filename with full path, if this is the case, the label is of no use.

If template_name does not refer to an existing file, then label is used to find a template file in the the bundled ones.

gitchangelog.gitchangelog.err(msg)[source]
gitchangelog.gitchangelog.eval_if_callable(v)[source]

Rewritten lambda expression

gitchangelog.gitchangelog.file_get_contents(filename)[source]
gitchangelog.gitchangelog.file_put_contents(filename, string)[source]

Write string to filename.

gitchangelog.gitchangelog.first_matching(section_regexps, string)[source]
gitchangelog.gitchangelog.format_last_exception(prefix='  | ')[source]

Format the last exception for display it in tests.

This allows to raise custom exception, without loosing the context of what caused the problem in the first place:

>>> def f():
...     raise Exception("Something terrible happened")
>>> try:  #
...     f()
... except Exception:
...     formated_exception = format_last_exception()
...     raise ValueError('Oops, an error occured:\n%s'
...         % formated_exception)
Traceback (most recent call last):
...
ValueError: Oops, an error occured:
  | Traceback (most recent call last):
...
  | Exception: Something terrible happened
gitchangelog.gitchangelog.get_log_encoding(repository, config)[source]
gitchangelog.gitchangelog.get_revision(repository, config, opts)[source]
gitchangelog.gitchangelog.indent(text, chars='  ', first=None)[source]

Return text string indented with the given chars

>>> string = 'This is first line.\nThis is second line\n'
>>> print(indent(string, chars="| "))  
| This is first line.
| This is second line
|
>>> print(indent(string, first="- "))  
- This is first line.
  This is second line
>>> string = 'This is first line.\n\nThis is second line'
>>> print(indent(string, first="- "))  
- This is first line.

  This is second line
gitchangelog.gitchangelog.load_config_file(filename, default_filename=None, fail_if_not_present=True)[source]

Loads data from a config file.

gitchangelog.gitchangelog.main()[source]
gitchangelog.gitchangelog.makotemplate(template_name)[source]

Return a callable that will render a changelog data structure

returned callable must take 2 arguments data and opts.

gitchangelog.gitchangelog.manage_obsolete_options(config)[source]
gitchangelog.gitchangelog.mustache(template_name)[source]

Return a callable that will render a changelog data structure

returned callable must take 2 arguments data and opts.

gitchangelog.gitchangelog.normpath(path, cwd=None)[source]

path can be absolute or relative, if relative it uses the cwd given as param.

gitchangelog.gitchangelog.obsolete_option_manager(fun)[source]
gitchangelog.gitchangelog.paragraph_wrap(text, regexp='\n\n')[source]

Wrap text by making sure that paragraph are separated correctly

>>> string = 'This is first paragraph which is quite long don\'t you \
... think ? Well, I think so.\n\nThis is second paragraph\n'
>>> print(paragraph_wrap(string)) 
This is first paragraph which is quite long don't you think ? Well, I
think so.
This is second paragraph

Notice that that each paragraph has been wrapped separately.

gitchangelog.gitchangelog.parse_cmd_line(usage, description, epilog, exname)[source]
gitchangelog.gitchangelog.rest_py(data, opts=None)[source]

Returns ReStructured Text changelog content from data

gitchangelog.gitchangelog.safe_print(content)[source]
gitchangelog.gitchangelog.set_cwd(directory)[source]
gitchangelog.gitchangelog.set_if_empty(text, msg='No commit message.')[source]
gitchangelog.gitchangelog.stderr(msg)[source]
gitchangelog.gitchangelog.stdout(content)[source]
gitchangelog.gitchangelog.swrap(command, **kwargs)[source]

Same as wrap(...) but strips the output.

gitchangelog.gitchangelog.versions_data_iter(repository, revlist=None, ignore_regexps=None, section_regexps=None, tag_filter_regexp='\\d+\\.\\d+(\\.\\d+)?', include_merge=True, body_process=<function <lambda>>, subject_process=<function <lambda>>, log_encoding='utf-8', warn=<function warn>)[source]

Returns an iterator through versions data structures

(see gitchangelog.rc.reference file for more info)

Parameters:
  • repository – target GitRepos object

  • revlist – list of strings that git log understands as revlist

  • ignore_regexps – list of regexp identifying ignored commit messages

  • section_regexps – regexps identifying sections

  • tag_filter_regexp – regexp to match tags used as version

  • include_merge – whether to include merge commits in the log or not

  • body_process – text processing object to apply to body

  • subject_process – text processing object to apply to subject

  • log_encoding – the encoding used in git logs

  • warn – callable to output warnings, mocked by tests

Returns:

iterator of versions data_structures

gitchangelog.gitchangelog.warn(msg)[source]
gitchangelog.gitchangelog.wrap(command, ignore_errlvls=None, env=None, shell=True)[source]

Wraps a shell command and casts an exception on unexpected errlvl

>>> wrap('/tmp/lsdjflkjf') 
Traceback (most recent call last):
...
ShellError: Wrapped command '/tmp/lsdjflkjf' exited with errorlevel 127.
  stderr:
  | /bin/sh: .../tmp/lsdjflkjf: not found
>>> print(wrap('echo hello'),  end='')
hello
>>> print(wrap('echo hello && false'),
...       end='')  
Traceback (most recent call last):
...
ShellError: Wrapped command 'echo hello && false' exited with errorlevel 1.
  stdout:
  | hello