diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..364f189 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,16 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-case-conflict + - id: check-yaml + - id: requirements-txt-fixer + - id: mixed-line-ending + - id: no-commit-to-branch + args: [--branch, master] +- repo: https://github.com/psf/black + rev: 22.6.0 + hooks: + - id: black diff --git a/.travis.yml b/.travis.yml index 80c19ba..9e0dfef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,27 @@ dist: xenial language: python -python: -- '3.4' -- '3.5' -- '3.6' -- '3.7' -- '3.8' -- '3.9' +jobs: + include: + - python: '3.4' + env: RUN_PRE_COMMIT=0 + - python: '3.5' + env: RUN_PRE_COMMIT=0 + - python: '3.6' + env: RUN_PRE_COMMIT=0 + - python: '3.7' + env: RUN_PRE_COMMIT=0 + - python: '3.8' + env: RUN_PRE_COMMIT=0 + - python: '3.9' + env: RUN_PRE_COMMIT=1 install: - pip install -U setuptools pip -r build-requirements.txt script: +- if [[ $RUN_PRE_COMMIT = 1 ]]; then + pip install -U pre-commit==2.12.1 && + pre-commit install && + pre-commit run --all; + fi - pytest --doctest-modules --cov=anybadge --cov-report html:htmlcov anybadge tests before_deploy: - sed -i "s/^version = .*/version = __version__ = \"$TRAVIS_TAG\"/" anybadge.py @@ -23,4 +35,4 @@ deploy: on: tags: true all_branches: true - python: '3.9' \ No newline at end of file + python: '3.9' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5e7db17..db27d97 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ I love your input! I want to make contributing to this project as easy and trans - Becoming a maintainer ## I use [Github Flow](https://docs.github.com/en/get-started/quickstart/github-flow), so all code changes happen through pull requests -Pull requests are the best way to propose changes to the codebase (I use +Pull requests are the best way to propose changes to the codebase (I use [Github Flow](https://docs.github.com/en/get-started/quickstart/github-flow)). I actively welcome your pull requests: 1. Fork the repo and create your branch from `master` @@ -20,7 +20,7 @@ Pull requests are the best way to propose changes to the codebase (I use ## Any contributions you make will be under the MIT Software License When you submit code changes, your submissions are understood to be under the same -[MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers +[MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern. ## Report bugs using Github's [issues](https://github.com/jongracecox/anybadge/issues) @@ -48,6 +48,42 @@ By contributing, you agree that your contributions will be licensed under its MI # Technical stuff +## Pre-commit +This projects makes use of [pre-commit](https://pre-commit.com) to add some safety checks and create consistency +in the project code. When committing changes to this project, please first [install pre-commit](https://pre-commit.com/#install), +then activate it for this project: + +```bash +pip install pre-commit +pre-commit install +``` + +After installing pre-commit to your project (with `pre-commit install`), committing to the project will trigger a series +of checks, and fixers. This process may reject your commit or make changes to your code to bring it into line with the +project standards. For example, [Python black](https://github.com/psf/black) will be used to reformat any code. When +changes are made by these pre-commit hooks you will need to re-add and commit those changes in order for pre-commit to +pass. + +Here is some example output from pre-commit: + +``` +trim trailing whitespace.................................................Failed +- hook id: trailing-whitespace +- exit code: 1 +- files were modified by this hook + +Fixing tests/test_anybadge.py + +fix end of files.........................................................Failed +- hook id: end-of-file-fixer +- exit code: 1 +- files were modified by this hook + +Fixing examples/color_teal.svg +``` + +This shows that two files were updated by hooks, and need to be re-added (with `git add`) before trying to commit again. + ## Documentation The `README.md` file contains a table showing example badges for the different built-in colors. If you modify the appearance of badges, or the available colors please update the table using the following code: diff --git a/TODO.md b/TODO.md index de09365..a08bed5 100644 --- a/TODO.md +++ b/TODO.md @@ -7,4 +7,4 @@ * [ ] Add CI test for Docker image * [ ] Add CI to push server to Docker hub * [ ] Support common badge server URL structure -* [ ] Documentation for all docker bits \ No newline at end of file +* [ ] Documentation for all docker bits diff --git a/anybadge/__init__.py b/anybadge/__init__.py index 5156de9..b705766 100644 --- a/anybadge/__init__.py +++ b/anybadge/__init__.py @@ -5,7 +5,7 @@ from .styles import Style # Package information version = __version__ = "0.0.0" -__version_info__ = tuple(re.split('[.-]', __version__)) +__version_info__ = tuple(re.split("[.-]", __version__)) __title__ = "anybadge" __summary__ = "A simple, flexible badge generator." __uri__ = "https://github.com/jongracecox/anybadge" diff --git a/anybadge/badge.py b/anybadge/badge.py index 3255ba4..a0c4c34 100644 --- a/anybadge/badge.py +++ b/anybadge/badge.py @@ -98,12 +98,26 @@ class Badge: '#4c1' """ - def __init__(self, label, value, font_name=None, font_size=None, - num_padding_chars=None, num_label_padding_chars=None, - num_value_padding_chars=None, template=None, style=None, - value_prefix='', value_suffix='', thresholds=None, default_color=None, - use_max_when_value_exceeds=True, value_format=None, text_color=None, - semver=False): + def __init__( + self, + label, + value, + font_name=None, + font_size=None, + num_padding_chars=None, + num_label_padding_chars=None, + num_value_padding_chars=None, + template=None, + style=None, + value_prefix="", + value_suffix="", + thresholds=None, + default_color=None, + use_max_when_value_exceeds=True, + value_format=None, + text_color=None, + semver=False, + ): """Constructor for Badge class.""" # Set defaults if values were not passed if not font_name: @@ -121,8 +135,8 @@ class Badge: else: num_value_padding_chars = num_padding_chars if not template: - template = get_template('default') - if style not in ['gitlab-scoped']: + template = get_template("default") + if style not in ["gitlab-scoped"]: style = "default" if not default_color: default_color = config.DEFAULT_COLOR @@ -146,7 +160,8 @@ class Badge: if font_name not in config.FONT_WIDTHS: raise ValueError( 'Font name "%s" not found. ' - 'Available fonts: %s' % (font_name, ', '.join(config.FONT_WIDTHS.keys())) + "Available fonts: %s" + % (font_name, ", ".join(config.FONT_WIDTHS.keys())) ) self.font_name = font_name self.font_size = font_size @@ -159,7 +174,7 @@ class Badge: # text_color can be passed as a single value or a pair of comma delimited values self.text_color = text_color - text_colors = text_color.split(',') + text_colors = text_color.split(",") self.label_text_color = text_colors[0] self.value_text_color = text_colors[0] if len(text_colors) > 1: @@ -209,26 +224,34 @@ class Badge: optional_args += ", font_size=%s" % repr(self.font_size) if self.num_label_padding_chars == self.num_value_padding_chars: if self.num_label_padding_chars != config.NUM_PADDING_CHARS: - optional_args += ", num_padding_chars=%s" % repr(self.num_label_padding_chars) + optional_args += ", num_padding_chars=%s" % repr( + self.num_label_padding_chars + ) else: if self.num_label_padding_chars != config.NUM_PADDING_CHARS: - optional_args += ", num_label_padding_chars=%s" % repr(self.num_label_padding_chars) + optional_args += ", num_label_padding_chars=%s" % repr( + self.num_label_padding_chars + ) if self.num_value_padding_chars != config.NUM_PADDING_CHARS: - optional_args += ", num_value_padding_chars=%s" % repr(self.num_value_padding_chars) - if self.template != get_template('default'): + optional_args += ", num_value_padding_chars=%s" % repr( + self.num_value_padding_chars + ) + if self.template != get_template("default"): optional_args += ", template=%s" % repr(self.template) - if self.style != 'default': + if self.style != "default": optional_args += ", style=%s" % repr(self.style) - if self.value_prefix != '': + if self.value_prefix != "": optional_args += ", value_prefix=%s" % repr(self.value_prefix) - if self.value_suffix != '': + if self.value_suffix != "": optional_args += ", value_suffix=%s" % repr(self.value_suffix) if self.thresholds: optional_args += ", thresholds=%s" % repr(self.thresholds) if self.default_color != config.DEFAULT_COLOR: optional_args += ", default_color=%s" % repr(self.default_color) if not self.use_max_when_value_exceeds: - optional_args += ", use_max_when_value_exceeds=%s" % repr(self.use_max_when_value_exceeds) + optional_args += ", use_max_when_value_exceeds=%s" % repr( + self.use_max_when_value_exceeds + ) if self.value_format: optional_args += ", value_format=%s" % repr(self.value_format) if self.text_color != config.DEFAULT_TEXT_COLOR: @@ -238,7 +261,7 @@ class Badge: self.__class__.__name__, repr(self.label), repr(self.value), - optional_args + optional_args, ) def _repr_svg_(self): @@ -255,7 +278,7 @@ class Badge: Returns: str """ - if not hasattr(cls, 'mask_id'): + if not hasattr(cls, "mask_id"): cls.mask_id = 0 cls.mask_id += 1 @@ -269,17 +292,17 @@ class Badge: Returns: str """ if self.style == "gitlab-scoped": - return get_template('gitlab_scoped') + return get_template("gitlab_scoped") # Identify whether template is a file or the actual template text - if len(self.template.split('\n')) == 1: + if len(self.template.split("\n")) == 1: try: return get_template(self.template) except UnknownBadgeTemplate: pass - with open(self.template, mode='r') as file_handle: + with open(self.template, mode="r") as file_handle: return file_handle.read() else: return self.template @@ -360,7 +383,10 @@ class Badge: Returns: int """ - return int(self.get_text_width(str(self.label)) + (2.0 * self.num_label_padding_chars * self.font_width)) + return int( + self.get_text_width(str(self.label)) + + (2.0 * self.num_label_padding_chars * self.font_width) + ) @property def value_width(self): @@ -368,7 +394,10 @@ class Badge: Returns: int """ - return int(self.get_text_width(str(self.value_text)) + (2.0 * self.num_value_padding_chars * self.font_width)) + return int( + self.get_text_width(str(self.value_text)) + + (2.0 * self.num_value_padding_chars * self.font_width) + ) @property def value_box_width(self): @@ -414,7 +443,9 @@ class Badge: Returns: float """ - return self.color_split_position + ((self.badge_width - self.color_split_position) / 2) + return self.color_split_position + ( + (self.badge_width - self.color_split_position) / 2 + ) @property def label_anchor_shadow(self): @@ -469,23 +500,25 @@ class Badge: badge_text = self._get_svg_template() - return badge_text.replace('{{ badge width }}', str(self.badge_width)) \ - .replace('{{ font name }}', self.font_name) \ - .replace('{{ font size }}', str(self.font_size)) \ - .replace('{{ label }}', self.label) \ - .replace('{{ value }}', self.value_text) \ - .replace('{{ label anchor }}', str(self.label_anchor)) \ - .replace('{{ label anchor shadow }}', str(self.label_anchor_shadow)) \ - .replace('{{ value anchor }}', str(self.value_anchor)) \ - .replace('{{ value anchor shadow }}', str(self.value_anchor_shadow)) \ - .replace('{{ color }}', self.badge_color_code) \ - .replace('{{ label text color }}', self.label_text_color) \ - .replace('{{ value text color }}', self.value_text_color) \ - .replace('{{ color split x }}', str(self.color_split_position)) \ - .replace('{{ value width }}', str(self.value_width)) \ - .replace('{{ mask id }}', self.mask_id) \ - .replace('{{ value box width }}', str(self.value_box_width)) \ - .replace('{{ arc start }}', str(self.arc_start)) + return ( + badge_text.replace("{{ badge width }}", str(self.badge_width)) + .replace("{{ font name }}", self.font_name) + .replace("{{ font size }}", str(self.font_size)) + .replace("{{ label }}", self.label) + .replace("{{ value }}", self.value_text) + .replace("{{ label anchor }}", str(self.label_anchor)) + .replace("{{ label anchor shadow }}", str(self.label_anchor_shadow)) + .replace("{{ value anchor }}", str(self.value_anchor)) + .replace("{{ value anchor shadow }}", str(self.value_anchor_shadow)) + .replace("{{ color }}", self.badge_color_code) + .replace("{{ label text color }}", self.label_text_color) + .replace("{{ value text color }}", self.value_text_color) + .replace("{{ color split x }}", str(self.color_split_position)) + .replace("{{ value width }}", str(self.value_width)) + .replace("{{ mask id }}", self.mask_id) + .replace("{{ value box width }}", str(self.value_box_width)) + .replace("{{ arc start }}", str(self.arc_start)) + ) def __str__(self): """Return string representation of badge. @@ -573,7 +606,7 @@ class Badge: if isinstance(color, Color): return color.value - if color[0] == '#': + if color[0] == "#": return color color = color.upper() @@ -589,32 +622,32 @@ class Badge: # contain underscores) we will try to get the same color. for prefix in prefixes: - if color.startswith(prefix) and color != prefix and '_' not in color: + if color.startswith(prefix) and color != prefix and "_" not in color: try: - return Color[color.replace(prefix, prefix + '_')].value + return Color[color.replace(prefix, prefix + "_")].value except KeyError: pass raise ValueError( - 'Invalid color code "%s". ' - 'Valid color codes are: %s', (color, ", ".join(list(Color.__members__.keys()))) + 'Invalid color code "%s". ' "Valid color codes are: %s", + (color, ", ".join(list(Color.__members__.keys()))), ) def write_badge(self, file_path, overwrite=False): """Write badge to file.""" # Validate path (part 1) - if file_path.endswith('/'): - raise ValueError('File location may not be a directory.') + if file_path.endswith("/"): + raise ValueError("File location may not be a directory.") # Get absolute filepath path = os.path.abspath(file_path) - if not path.lower().endswith('.svg'): - path += '.svg' + if not path.lower().endswith(".svg"): + path += ".svg" # Validate path (part 2) if not overwrite and os.path.exists(path): raise RuntimeError('File "{}" already exists.'.format(path)) - with open(path, mode='w') as file_handle: + with open(path, mode="w") as file_handle: file_handle.write(self.badge_svg_text) diff --git a/anybadge/cli.py b/anybadge/cli.py index 6dbb38d..d212902 100644 --- a/anybadge/cli.py +++ b/anybadge/cli.py @@ -10,8 +10,10 @@ from .badge import Badge def parse_args(args): """Parse the command line arguments.""" - parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent('''\ + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=textwrap.dedent( + """\ Command line utility to generate .svg badges. This utility can be used to generate .svg badge images, using configurable @@ -46,52 +48,121 @@ examples: anybadge.py --label=pipeline --value=passing --file=pipeline.svg \\ passing=green failing=red -''')) - parser.add_argument('-l', '--label', type=str, help='The badge label.') - parser.add_argument('-v', '--value', type=str, help='The badge value.', required=True) - parser.add_argument('-m', '--value-format', type=str, default=None, - help='Formatting string for value (e.g. "%%.2f" for 2dp floats)') - parser.add_argument('-c', '--color', type=str, help='For fixed color badges use --color ' - 'to specify the badge color.', - default=config.DEFAULT_COLOR) - parser.add_argument('-p', '--prefix', type=str, help='Optional prefix for value.', - default='') - parser.add_argument('-s', '--suffix', type=str, help='Optional suffix for value.', - default='') - parser.add_argument('-d', '--padding', type=int, help='Number of characters to pad on ' - 'either side of the badge text.', - default=config.NUM_PADDING_CHARS) - parser.add_argument('-lp', '--label-padding', type=int, help='Number of characters to pad on ' - 'either side of the badge label.', default=None) - parser.add_argument('-vp', '--value-padding', type=int, help='Number of characters to pad on ' - 'either side of the badge value.', default=None) - parser.add_argument('-n', '--font', type=str, - help='Font name. Supported fonts: ' - ','.join(['"%s"' % x for x in config.FONT_WIDTHS.keys()]), - default=config.DEFAULT_FONT) - parser.add_argument('-z', '--font-size', type=int, help='Font size.', - default=config.DEFAULT_FONT_SIZE) - parser.add_argument('-t', '--template', type=str, help='Location of alternative ' - 'template .svg file.', - default=get_template('default')) - parser.add_argument('-st', '--style', type=str, help='Alternative style of badge to create. Valid ' - 'values are "gitlab-scoped", "default". This ' - 'overrides any templates passed using --template.') - parser.add_argument('-u', '--use-max', action='store_true', - help='Use the maximum threshold color when the value exceeds the ' - 'maximum threshold.') - parser.add_argument('-f', '--file', type=str, help='Output file location.') - parser.add_argument('-o', '--overwrite', action='store_true', - help='Overwrite output file if it already exists.') - parser.add_argument('-r', '--text-color', type=str, help='Text color. Single value affects both label' - 'and value colors. A comma separated pair ' - 'affects label and value text respectively.', - default=config.DEFAULT_TEXT_COLOR) - parser.add_argument('-e', '--semver', action='store_true', default=False, - help='Treat value and thresholds as semantic versions.') - parser.add_argument('args', nargs=argparse.REMAINDER, help='Pairs of =. ' - 'For example 2=red 4=orange 6=yellow 8=good. ' - 'Read this as "Less than 2 = red, less than 4 = orange...".') +""" + ), + ) + parser.add_argument("-l", "--label", type=str, help="The badge label.") + parser.add_argument( + "-v", "--value", type=str, help="The badge value.", required=True + ) + parser.add_argument( + "-m", + "--value-format", + type=str, + default=None, + help='Formatting string for value (e.g. "%%.2f" for 2dp floats)', + ) + parser.add_argument( + "-c", + "--color", + type=str, + help="For fixed color badges use --color to specify the badge color.", + default=config.DEFAULT_COLOR, + ) + parser.add_argument( + "-p", "--prefix", type=str, help="Optional prefix for value.", default="" + ) + parser.add_argument( + "-s", "--suffix", type=str, help="Optional suffix for value.", default="" + ) + parser.add_argument( + "-d", + "--padding", + type=int, + help="Number of characters to pad on either side of the badge text.", + default=config.NUM_PADDING_CHARS, + ) + parser.add_argument( + "-lp", + "--label-padding", + type=int, + help="Number of characters to pad on either side of the badge label.", + default=None, + ) + parser.add_argument( + "-vp", + "--value-padding", + type=int, + help="Number of characters to pad on either side of the badge value.", + default=None, + ) + parser.add_argument( + "-n", + "--font", + type=str, + help="Font name. Supported fonts: " + ",".join(['"%s"' % x for x in config.FONT_WIDTHS.keys()]), + default=config.DEFAULT_FONT, + ) + parser.add_argument( + "-z", + "--font-size", + type=int, + help="Font size.", + default=config.DEFAULT_FONT_SIZE, + ) + parser.add_argument( + "-t", + "--template", + type=str, + help="Location of alternative template .svg file.", + default=get_template("default"), + ) + parser.add_argument( + "-st", + "--style", + type=str, + help="Alternative style of badge to create. Valid " + 'values are "gitlab-scoped", "default". This ' + "overrides any templates passed using --template.", + ) + parser.add_argument( + "-u", + "--use-max", + action="store_true", + help="Use the maximum threshold color when the value exceeds the " + "maximum threshold.", + ) + parser.add_argument("-f", "--file", type=str, help="Output file location.") + parser.add_argument( + "-o", + "--overwrite", + action="store_true", + help="Overwrite output file if it already exists.", + ) + parser.add_argument( + "-r", + "--text-color", + type=str, + help="Text color. Single value affects both label" + "and value colors. A comma separated pair " + "affects label and value text respectively.", + default=config.DEFAULT_TEXT_COLOR, + ) + parser.add_argument( + "-e", + "--semver", + action="store_true", + default=False, + help="Treat value and thresholds as semantic versions.", + ) + parser.add_argument( + "args", + nargs=argparse.REMAINDER, + help="Pairs of =. " + "For example 2=red 4=orange 6=yellow 8=good. " + 'Read this as "Less than 2 = red, less than 4 = orange...".', + ) return parser.parse_args(args) @@ -114,26 +185,39 @@ def main(args=None): if len(args.args) == 1 and Style.exists(args.args[0].upper()): style_name = args.args[0].upper() style = Style[style_name] - threshold_text = style.threshold.split(' ') + threshold_text = style.threshold.split(" ") if not args.label and style.label: label = style.label if not args.suffix and style.suffix: suffix = style.suffix if not label: - raise ValueError('Label has not been set. Please use --label argument.') + raise ValueError("Label has not been set. Please use --label argument.") # Create threshold list from args - threshold_list = [x.split('=') for x in threshold_text] + threshold_list = [x.split("=") for x in threshold_text] threshold_dict = {x[0]: x[1] for x in threshold_list} # Create badge object - badge = Badge(label, args.value, value_prefix=args.prefix, value_suffix=suffix, - default_color=args.color, num_padding_chars=args.padding, - num_label_padding_chars=args.label_padding, num_value_padding_chars=args.value_padding, - font_name=args.font, font_size=args.font_size, template=args.template, style=args.style, - use_max_when_value_exceeds=args.use_max, thresholds=threshold_dict, - value_format=args.value_format, text_color=args.text_color, semver=args.semver) + badge = Badge( + label, + args.value, + value_prefix=args.prefix, + value_suffix=suffix, + default_color=args.color, + num_padding_chars=args.padding, + num_label_padding_chars=args.label_padding, + num_value_padding_chars=args.value_padding, + font_name=args.font, + font_size=args.font_size, + template=args.template, + style=args.style, + use_max_when_value_exceeds=args.use_max, + thresholds=threshold_dict, + value_format=args.value_format, + text_color=args.text_color, + semver=args.semver, + ) if args.file: # Write badge SVG to file @@ -142,5 +226,5 @@ def main(args=None): print(badge.badge_svg_text) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/anybadge/colors.py b/anybadge/colors.py index 4a5417c..2b70da3 100644 --- a/anybadge/colors.py +++ b/anybadge/colors.py @@ -4,24 +4,24 @@ from enum import Enum class Color(Enum): - WHITE = '#FFFFFF' - SILVER = '#C0C0C0' - GRAY = '#808080' - BLACK = '#000000' - RED = '#e05d44' - BRIGHT_RED = '#FF0000' - MAROON = '#800000' - OLIVE = '#808000' - LIME = '#00FF00' - BRIGHT_YELLOW = '#FFFF00' - YELLOW = '#dfb317' - GREEN = '#4c1' - YELLOW_GREEN = '#a4a61d' - AQUA = '#00FFFF' - TEAL = '#008080' - BLUE = '#0000FF' - NAVY = '#000080' - FUCHSIA = '#FF00FF' - PURPLE = '#800080' - ORANGE = '#fe7d37' - LIGHT_GREY = '#9f9f9f' \ No newline at end of file + WHITE = "#FFFFFF" + SILVER = "#C0C0C0" + GRAY = "#808080" + BLACK = "#000000" + RED = "#e05d44" + BRIGHT_RED = "#FF0000" + MAROON = "#800000" + OLIVE = "#808000" + LIME = "#00FF00" + BRIGHT_YELLOW = "#FFFF00" + YELLOW = "#dfb317" + GREEN = "#4c1" + YELLOW_GREEN = "#a4a61d" + AQUA = "#00FFFF" + TEAL = "#008080" + BLUE = "#0000FF" + NAVY = "#000080" + FUCHSIA = "#FF00FF" + PURPLE = "#800080" + ORANGE = "#fe7d37" + LIGHT_GREY = "#9f9f9f" diff --git a/anybadge/config.py b/anybadge/config.py index c5d7efc..12a8db2 100644 --- a/anybadge/config.py +++ b/anybadge/config.py @@ -1,22 +1,20 @@ # Set some defaults -DEFAULT_FONT = 'DejaVu Sans,Verdana,Geneva,sans-serif' +DEFAULT_FONT = "DejaVu Sans,Verdana,Geneva,sans-serif" DEFAULT_FONT_SIZE = 11 NUM_PADDING_CHARS = 0.5 -DEFAULT_COLOR = '#4c1' -DEFAULT_TEXT_COLOR = '#fff' -MASK_ID_PREFIX = 'anybadge_' +DEFAULT_COLOR = "#4c1" +DEFAULT_TEXT_COLOR = "#fff" +MASK_ID_PREFIX = "anybadge_" # Dictionary for looking up approx pixel widths of # supported fonts and font sizes. FONT_WIDTHS = { - 'DejaVu Sans,Verdana,Geneva,sans-serif': { + "DejaVu Sans,Verdana,Geneva,sans-serif": { 10: 9, 11: 10, 12: 11, }, - 'Arial, Helvetica, sans-serif': { + "Arial, Helvetica, sans-serif": { 11: 8, }, } - - diff --git a/anybadge/exceptions.py b/anybadge/exceptions.py index 693e974..4dce079 100644 --- a/anybadge/exceptions.py +++ b/anybadge/exceptions.py @@ -1,3 +1,2 @@ - class UnknownBadgeTemplate(Exception): """The badge template is unknown.""" diff --git a/anybadge/helpers.py b/anybadge/helpers.py index 6f7d360..1dc0572 100644 --- a/anybadge/helpers.py +++ b/anybadge/helpers.py @@ -43,12 +43,12 @@ def _get_approx_string_width(text, font_width, fixed_width=False): # These percentages can be calculated using the ``_get_character_percentage_dict`` function. char_width_percentages = { "lij|' ": 40.0, - '![]fI.,:;/\\t': 50.0, + "![]fI.,:;/\\t": 50.0, '`-(){}r"': 60.0, - '*^zcsJkvxy': 70.0, - 'aebdhnopqug#$L+<>=?_~FZT0123456789': 70.0, - 'BSPEAKVXY&UwNRCHD': 70.0, - 'QGOMm%W@': 100.0 + "*^zcsJkvxy": 70.0, + "aebdhnopqug#$L+<>=?_~FZT0123456789": 70.0, + "BSPEAKVXY&UwNRCHD": 70.0, + "QGOMm%W@": 100.0, } for s in text: diff --git a/anybadge/server/cli.py b/anybadge/server/cli.py index 87e066b..3d65061 100644 --- a/anybadge/server/cli.py +++ b/anybadge/server/cli.py @@ -24,20 +24,32 @@ def run(listen_address: str = None, port: int = None): SERVER_LISTEN_ADDRESS = listen_address httpd = HTTPServer(server_address, AnyBadgeHTTPRequestHandler) - logger.info('Serving at: http://%s:%s' % server_address) + logger.info("Serving at: http://%s:%s" % server_address) httpd.serve_forever() def parse_args(): - logger.debug('Parsing command line arguments.') + logger.debug("Parsing command line arguments.") parser = argparse.ArgumentParser(description="Run an anybadge server.") - parser.add_argument('-p', '--port', type=int, default=DEFAULT_SERVER_PORT, - help="Server port number. Default is %s. This can also be set via an environment " - "variable called ``ANYBADGE_PORT``." % DEFAULT_SERVER_PORT) - parser.add_argument('-l', '--listen-address', type=str, default=DEFAULT_SERVER_LISTEN_ADDRESS, - help="Server listen address. Default is %s. This can also be set via an environment " - "variable called ``ANYBADGE_LISTEN_ADDRESS``." % DEFAULT_SERVER_LISTEN_ADDRESS) - parser.add_argument('-d', '--debug', action='store_true', help='Enable debug logging.') + parser.add_argument( + "-p", + "--port", + type=int, + default=DEFAULT_SERVER_PORT, + help="Server port number. Default is %s. This can also be set via an environment " + "variable called ``ANYBADGE_PORT``." % DEFAULT_SERVER_PORT, + ) + parser.add_argument( + "-l", + "--listen-address", + type=str, + default=DEFAULT_SERVER_LISTEN_ADDRESS, + help="Server listen address. Default is %s. This can also be set via an environment " + "variable called ``ANYBADGE_LISTEN_ADDRESS``." % DEFAULT_SERVER_LISTEN_ADDRESS, + ) + parser.add_argument( + "-d", "--debug", action="store_true", help="Enable debug logging." + ) return parser.parse_args() @@ -47,14 +59,14 @@ def main(): global DEFAULT_SERVER_PORT, DEFAULT_SERVER_LISTEN_ADDRESS, DEFAULT_LOGGING_LEVEL # Check for environment variables - if 'ANYBADGE_PORT' in environ: - DEFAULT_SERVER_PORT = environ['ANYBADGE_PORT'] + if "ANYBADGE_PORT" in environ: + DEFAULT_SERVER_PORT = environ["ANYBADGE_PORT"] - if 'ANYBADGE_LISTEN_ADDRESS' in environ: - DEFAULT_SERVER_LISTEN_ADDRESS = environ['ANYBADGE_LISTEN_ADDRESS'] + if "ANYBADGE_LISTEN_ADDRESS" in environ: + DEFAULT_SERVER_LISTEN_ADDRESS = environ["ANYBADGE_LISTEN_ADDRESS"] - if 'ANYBADGE_LOG_LEVEL' in environ: - DEFAULT_LOGGING_LEVEL = logging.getLevelName(environ['ANYBADGE_LOG_LEVEL']) + if "ANYBADGE_LOG_LEVEL" in environ: + DEFAULT_LOGGING_LEVEL = logging.getLevelName(environ["ANYBADGE_LOG_LEVEL"]) # Parse command line args args = parse_args() @@ -64,12 +76,14 @@ def main(): if args.debug: logging_level = logging.DEBUG - logging.basicConfig(format='%(asctime)-15s %(levelname)s:%(filename)s(%(lineno)d):%(funcName)s: %(message)s', - level=logging_level) - logger.info('Starting up anybadge server.') + logging.basicConfig( + format="%(asctime)-15s %(levelname)s:%(filename)s(%(lineno)d):%(funcName)s: %(message)s", + level=logging_level, + ) + logger.info("Starting up anybadge server.") run(listen_address=args.listen_address, port=args.port) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/anybadge/server/config.py b/anybadge/server/config.py index ec88b55..42297c5 100644 --- a/anybadge/server/config.py +++ b/anybadge/server/config.py @@ -2,7 +2,7 @@ import logging DEFAULT_SERVER_PORT = 8000 -DEFAULT_SERVER_LISTEN_ADDRESS = 'localhost' +DEFAULT_SERVER_LISTEN_ADDRESS = "localhost" DEFAULT_LOGGING_LEVEL = logging.INFO SERVER_PORT = DEFAULT_SERVER_PORT diff --git a/anybadge/server/request_handler.py b/anybadge/server/request_handler.py index ff73514..e514a2d 100644 --- a/anybadge/server/request_handler.py +++ b/anybadge/server/request_handler.py @@ -12,45 +12,45 @@ class AnyBadgeHTTPRequestHandler(BaseHTTPRequestHandler): """Request handler for anybadge HTTP server.""" def do_HEAD(self): - logging.debug('Sending head.') + logging.debug("Sending head.") self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() def do_GET(self): - logging.debug('Handling get request.') + logging.debug("Handling get request.") self.do_HEAD() # Ignore request for favicon - if self.path == '/favicon.ico': - logging.debug('Ignoring favicon request.') + if self.path == "/favicon.ico": + logging.debug("Ignoring favicon request.") return # Parse the URL query string parsed = urlparse.urlparse(self.path) url_query = urlparse.parse_qs(parsed.query) - label = '' - value = '' - color = 'green' + label = "" + value = "" + color = "green" # Extract the label and value portions - if 'label' in url_query: - label = url_query['label'][0] + if "label" in url_query: + label = url_query["label"][0] - if 'value' in url_query: - value = url_query['value'][0] + if "value" in url_query: + value = url_query["value"][0] - logging.debug('Label: %s Value: %s', label, value) + logging.debug("Label: %s Value: %s", label, value) if label and value and color: - logging.debug('All parameters present.') + logging.debug("All parameters present.") badge = Badge(label=label, value=value, default_color=color) for line in badge.badge_svg_text: self.wfile.write(str.encode(line)) else: - logging.debug('Not all parameters present.') + logging.debug("Not all parameters present.") self.wfile.write(b"Anybadge Web Server.") self.wfile.write(b"") @@ -67,8 +67,10 @@ class AnyBadgeHTTPRequestHandler(BaseHTTPRequestHandler): \ http://localhost:{port}/?label=Project%20Awesomeness&value=110% - """.format(port=config.SERVER_PORT) + """.format( + port=config.SERVER_PORT + ) for line in help_text.splitlines(): - self.wfile.write(str.encode('

%s

' % line)) - self.wfile.write(b"") \ No newline at end of file + self.wfile.write(str.encode("

%s

" % line)) + self.wfile.write(b"") diff --git a/anybadge/styles.py b/anybadge/styles.py index 6a8befd..ac7f532 100644 --- a/anybadge/styles.py +++ b/anybadge/styles.py @@ -6,8 +6,8 @@ from enum import Enum class Style(Enum): """A style that can be used for common badge types.""" - PYLINT = ('default.svg', '2=red 4=orange 8=yellow 10=green', 'pylint') - COVERAGE = ('default.svg', '50=red 60=orange 80=yellow 100=green', 'coverage', '%') + PYLINT = ("default.svg", "2=red 4=orange 8=yellow 10=green", "pylint") + COVERAGE = ("default.svg", "50=red 60=orange 80=yellow 100=green", "coverage", "%") def __init__(self, template, threshold, label, suffix=None): self.template = template diff --git a/anybadge/templates/default.svg b/anybadge/templates/default.svg index e81e92a..6b6c035 100644 --- a/anybadge/templates/default.svg +++ b/anybadge/templates/default.svg @@ -20,4 +20,4 @@ {{ value }} {{ value }} - \ No newline at end of file + diff --git a/anybadge/templates/gitlab_scoped.svg b/anybadge/templates/gitlab_scoped.svg index 678292b..be5d528 100644 --- a/anybadge/templates/gitlab_scoped.svg +++ b/anybadge/templates/gitlab_scoped.svg @@ -19,4 +19,4 @@ {{ value }} - \ No newline at end of file + diff --git a/build-requirements.txt b/build-requirements.txt index f861a54..c29f7e9 100644 --- a/build-requirements.txt +++ b/build-requirements.txt @@ -1,3 +1,3 @@ pygments pytest -pytest-cov \ No newline at end of file +pytest-cov diff --git a/build_examples.py b/build_examples.py index 4ffbaab..5601298 100644 --- a/build_examples.py +++ b/build_examples.py @@ -1,15 +1,23 @@ import anybadge -if __name__ == '__main__': +if __name__ == "__main__": - print("""| Color Name | Hex Code | Example | - | ---------- | -------- | ------- |""") + print( + """| Color Name | Hex Code | Example | + | ---------- | -------- | ------- |""" + ) for color, hex in sorted(anybadge.COLORS.items()): - file = 'examples/color_' + color + '.svg' + file = "examples/color_" + color + ".svg" - url = 'https://cdn.rawgit.com/jongracecox/anybadge/master/' + file + url = "https://cdn.rawgit.com/jongracecox/anybadge/master/" + file - anybadge.Badge(label='Color', value=color, default_color=color).write_badge(file, overwrite=True) + anybadge.Badge(label="Color", value=color, default_color=color).write_badge( + file, overwrite=True + ) - print("| {color} | {hex} | ![]({url}) |".format(color=color, hex=hex.upper(), url=url)) + print( + "| {color} | {hex} | ![]({url}) |".format( + color=color, hex=hex.upper(), url=url + ) + ) diff --git a/examples/awesomeness.svg b/examples/awesomeness.svg index a2573e5..d82df65 100644 --- a/examples/awesomeness.svg +++ b/examples/awesomeness.svg @@ -20,4 +20,4 @@ 110% 110% - \ No newline at end of file + diff --git a/examples/color_aqua.svg b/examples/color_aqua.svg index 4df0408..c0edaaf 100644 --- a/examples/color_aqua.svg +++ b/examples/color_aqua.svg @@ -20,4 +20,4 @@ Color.AQUA Color.AQUA - \ No newline at end of file + diff --git a/examples/color_black.svg b/examples/color_black.svg index 5701302..cbcb787 100644 --- a/examples/color_black.svg +++ b/examples/color_black.svg @@ -20,4 +20,4 @@ Color.BLACK Color.BLACK - \ No newline at end of file + diff --git a/examples/color_blue.svg b/examples/color_blue.svg index c427c7b..5876ad5 100644 --- a/examples/color_blue.svg +++ b/examples/color_blue.svg @@ -20,4 +20,4 @@ Color.BLUE Color.BLUE - \ No newline at end of file + diff --git a/examples/color_bright_red.svg b/examples/color_bright_red.svg index e278c6d..28edaaa 100644 --- a/examples/color_bright_red.svg +++ b/examples/color_bright_red.svg @@ -20,4 +20,4 @@ Color.BRIGHT_RED Color.BRIGHT_RED - \ No newline at end of file + diff --git a/examples/color_bright_yellow.svg b/examples/color_bright_yellow.svg index 6f4f2fd..532775a 100644 --- a/examples/color_bright_yellow.svg +++ b/examples/color_bright_yellow.svg @@ -20,4 +20,4 @@ Color.BRIGHT_YELLOW Color.BRIGHT_YELLOW - \ No newline at end of file + diff --git a/examples/color_brightred.svg b/examples/color_brightred.svg index 7e5e873..8b767f7 100644 --- a/examples/color_brightred.svg +++ b/examples/color_brightred.svg @@ -20,4 +20,4 @@ brightred brightred - \ No newline at end of file + diff --git a/examples/color_brightyellow.svg b/examples/color_brightyellow.svg index d0dd1ba..c87befa 100644 --- a/examples/color_brightyellow.svg +++ b/examples/color_brightyellow.svg @@ -20,4 +20,4 @@ brightyellow brightyellow - \ No newline at end of file + diff --git a/examples/color_fuchsia.svg b/examples/color_fuchsia.svg index d1ea317..1ad2a52 100644 --- a/examples/color_fuchsia.svg +++ b/examples/color_fuchsia.svg @@ -20,4 +20,4 @@ Color.FUCHSIA Color.FUCHSIA - \ No newline at end of file + diff --git a/examples/color_gray.svg b/examples/color_gray.svg index a7f6ae7..615ffab 100644 --- a/examples/color_gray.svg +++ b/examples/color_gray.svg @@ -20,4 +20,4 @@ Color.GRAY Color.GRAY - \ No newline at end of file + diff --git a/examples/color_green.svg b/examples/color_green.svg index 21f6d9b..253ba24 100644 --- a/examples/color_green.svg +++ b/examples/color_green.svg @@ -20,4 +20,4 @@ Color.GREEN Color.GREEN - \ No newline at end of file + diff --git a/examples/color_light_grey.svg b/examples/color_light_grey.svg index eed6495..e5c6271 100644 --- a/examples/color_light_grey.svg +++ b/examples/color_light_grey.svg @@ -20,4 +20,4 @@ Color.LIGHT_GREY Color.LIGHT_GREY - \ No newline at end of file + diff --git a/examples/color_lightgrey.svg b/examples/color_lightgrey.svg index 74e1abc..d578f18 100644 --- a/examples/color_lightgrey.svg +++ b/examples/color_lightgrey.svg @@ -20,4 +20,4 @@ lightgrey lightgrey - \ No newline at end of file + diff --git a/examples/color_lime.svg b/examples/color_lime.svg index c61ee23..7f772ee 100644 --- a/examples/color_lime.svg +++ b/examples/color_lime.svg @@ -20,4 +20,4 @@ Color.LIME Color.LIME - \ No newline at end of file + diff --git a/examples/color_maroon.svg b/examples/color_maroon.svg index 6e55e64..c5e3e63 100644 --- a/examples/color_maroon.svg +++ b/examples/color_maroon.svg @@ -20,4 +20,4 @@ Color.MAROON Color.MAROON - \ No newline at end of file + diff --git a/examples/color_navy.svg b/examples/color_navy.svg index e832f43..a67c407 100644 --- a/examples/color_navy.svg +++ b/examples/color_navy.svg @@ -20,4 +20,4 @@ Color.NAVY Color.NAVY - \ No newline at end of file + diff --git a/examples/color_olive.svg b/examples/color_olive.svg index 2d33248..8bac18e 100644 --- a/examples/color_olive.svg +++ b/examples/color_olive.svg @@ -20,4 +20,4 @@ Color.OLIVE Color.OLIVE - \ No newline at end of file + diff --git a/examples/color_orange.svg b/examples/color_orange.svg index f8ad48b..d356ea2 100644 --- a/examples/color_orange.svg +++ b/examples/color_orange.svg @@ -20,4 +20,4 @@ Color.ORANGE Color.ORANGE - \ No newline at end of file + diff --git a/examples/color_purple.svg b/examples/color_purple.svg index 55b1373..c6d4027 100644 --- a/examples/color_purple.svg +++ b/examples/color_purple.svg @@ -20,4 +20,4 @@ Color.PURPLE Color.PURPLE - \ No newline at end of file + diff --git a/examples/color_red.svg b/examples/color_red.svg index 9532eb2..f51af61 100644 --- a/examples/color_red.svg +++ b/examples/color_red.svg @@ -20,4 +20,4 @@ Color.RED Color.RED - \ No newline at end of file + diff --git a/examples/color_silver.svg b/examples/color_silver.svg index a51adb4..c50cf82 100644 --- a/examples/color_silver.svg +++ b/examples/color_silver.svg @@ -20,4 +20,4 @@ Color.SILVER Color.SILVER - \ No newline at end of file + diff --git a/examples/color_teal.svg b/examples/color_teal.svg index 6a11828..005846c 100644 --- a/examples/color_teal.svg +++ b/examples/color_teal.svg @@ -20,4 +20,4 @@ Color.TEAL Color.TEAL - \ No newline at end of file + diff --git a/examples/color_white.svg b/examples/color_white.svg index caca741..1f4f66a 100644 --- a/examples/color_white.svg +++ b/examples/color_white.svg @@ -20,4 +20,4 @@ Color.WHITE Color.WHITE - \ No newline at end of file + diff --git a/examples/color_yellow.svg b/examples/color_yellow.svg index f5225e0..0e399dc 100644 --- a/examples/color_yellow.svg +++ b/examples/color_yellow.svg @@ -20,4 +20,4 @@ Color.YELLOW Color.YELLOW - \ No newline at end of file + diff --git a/examples/color_yellow_green.svg b/examples/color_yellow_green.svg index 9f83290..dbd5e58 100644 --- a/examples/color_yellow_green.svg +++ b/examples/color_yellow_green.svg @@ -20,4 +20,4 @@ Color.YELLOW_GREEN Color.YELLOW_GREEN - \ No newline at end of file + diff --git a/examples/color_yellowgreen.svg b/examples/color_yellowgreen.svg index 266f20a..767db39 100644 --- a/examples/color_yellowgreen.svg +++ b/examples/color_yellowgreen.svg @@ -20,4 +20,4 @@ yellowgreen yellowgreen - \ No newline at end of file + diff --git a/examples/coverage.svg b/examples/coverage.svg index 2e8bc35..6e40f19 100644 --- a/examples/coverage.svg +++ b/examples/coverage.svg @@ -20,4 +20,4 @@ 65.0% 65.0% - \ No newline at end of file + diff --git a/examples/gitlab_scoped.svg b/examples/gitlab_scoped.svg index b4835ec..76e9582 100644 --- a/examples/gitlab_scoped.svg +++ b/examples/gitlab_scoped.svg @@ -19,4 +19,4 @@ Archimedes - \ No newline at end of file + diff --git a/examples/pipeline.svg b/examples/pipeline.svg index 3233d0c..1856f85 100644 --- a/examples/pipeline.svg +++ b/examples/pipeline.svg @@ -20,4 +20,4 @@ passing passing - \ No newline at end of file + diff --git a/examples/pylint.svg b/examples/pylint.svg index 9c6fb00..16e1b9f 100644 --- a/examples/pylint.svg +++ b/examples/pylint.svg @@ -20,4 +20,4 @@ 2.22 2.22 - \ No newline at end of file + diff --git a/setup.py b/setup.py index 2becd57..9402300 100644 --- a/setup.py +++ b/setup.py @@ -3,39 +3,37 @@ import os import re from setuptools import setup -with open('README.md', encoding='utf-8') as f: +with open("README.md", encoding="utf-8") as f: long_description = f.read() # Attempt to get version number from TravisCI environment variable -version = os.environ.get('TRAVIS_TAG', default='0.0.0') +version = os.environ.get("TRAVIS_TAG", default="0.0.0") # Remove leading 'v' -version = re.sub('^v', '', version) +version = re.sub("^v", "", version) setup( - name='anybadge', - description='Simple, flexible badge generator for project badges.', + name="anybadge", + description="Simple, flexible badge generator for project badges.", long_description=long_description, - long_description_content_type='text/markdown', + long_description_content_type="text/markdown", version=version, - author='Jon Grace-Cox', - author_email='jongracecox@gmail.com', - packages=['anybadge'], - py_modules=['anybadge_server'], - setup_requires=['setuptools', 'wheel'], + author="Jon Grace-Cox", + author_email="jongracecox@gmail.com", + packages=["anybadge"], + py_modules=["anybadge_server"], + setup_requires=["setuptools", "wheel"], tests_require=[], - install_requires=['packaging'], - package_data={'anybadge': ['templates/*.svg']}, - options={ - 'bdist_wheel': {'universal': False} - }, - python_requires='>=3.4', - url='https://github.com/jongracecox/anybadge', + install_requires=["packaging"], + package_data={"anybadge": ["templates/*.svg"]}, + options={"bdist_wheel": {"universal": False}}, + python_requires=">=3.4", + url="https://github.com/jongracecox/anybadge", entry_points={ - 'console_scripts': ['anybadge=anybadge.cli:main', - 'anybadge-server=anybadge.server.cli:main'], + "console_scripts": [ + "anybadge=anybadge.cli:main", + "anybadge-server=anybadge.server.cli:main", + ], }, - classifiers=[ - 'License :: OSI Approved :: MIT License' - ] + classifiers=["License :: OSI Approved :: MIT License"], ) diff --git a/tests/template.svg b/tests/template.svg index e81e92a..6b6c035 100644 --- a/tests/template.svg +++ b/tests/template.svg @@ -20,4 +20,4 @@ {{ value }} {{ value }} - \ No newline at end of file + diff --git a/tests/test_anybadge.py b/tests/test_anybadge.py index fc6ed0a..4eb10b3 100644 --- a/tests/test_anybadge.py +++ b/tests/test_anybadge.py @@ -7,319 +7,389 @@ class TestAnybadge(TestCase): """Test case class for anybadge package.""" def setUp(self): - if not hasattr(self, 'assertRaisesRegex'): + if not hasattr(self, "assertRaisesRegex"): self.assertRaisesRegex = self.assertRaisesRegexp - + def test_badge_equal_label_value_width(self): """Test that label and value widths are equal when text is the same.""" - badge = Badge(label='a', value='a', num_padding_chars=0) + badge = Badge(label="a", value="a", num_padding_chars=0) self.assertEqual(badge.label_width, badge.value_width) def test_badge_equal_split(self): """Test that the color split is in the middle when label and value are equal width.""" - badge = Badge(label='a', value='a') + badge = Badge(label="a", value="a") self.assertEqual(int(badge.badge_width / 2), badge.color_split_position) def test_badge_equal_split_no_padding(self): """Test that the color split is in the middle when label and value are equal width.""" - badge = Badge(label='a', value='a', num_padding_chars=0) + badge = Badge(label="a", value="a", num_padding_chars=0) self.assertEqual(int(badge.badge_width / 2), badge.color_split_position) def test_badge_width_with_long_value_text(self): """Test the width of a badge generated with a long text value.""" - badge = Badge(label='CppCheck', - value='err: 2 | warn: 9 | info: 99 | style: 365', - default_color='red') + badge = Badge( + label="CppCheck", + value="err: 2 | warn: 9 | info: 99 | style: 365", + default_color="red", + ) - badge.write_badge('test_badge_1.svg', overwrite=True) + badge.write_badge("test_badge_1.svg", overwrite=True) self.assertLessEqual(badge.badge_width, 326) def test_badge_width_with_long_value_text_zero_padding(self): """Test the width of a badge generated with a long text value.""" - badge = Badge(label='CppCheck', - value='err: 2 | warn: 9 | info: 99 | style: 365', - default_color='red', - num_padding_chars=0) + badge = Badge( + label="CppCheck", + value="err: 2 | warn: 9 | info: 99 | style: 365", + default_color="red", + num_padding_chars=0, + ) - badge.write_badge('test_badge_2.svg', overwrite=True) + badge.write_badge("test_badge_2.svg", overwrite=True) self.assertLessEqual(badge.badge_width, 306) def test_badge_width_with_medium_value_text(self): """Test the width of a badge generated with a medium text value.""" - badge = Badge(label='medium', - value='89.67%', - default_color='green') + badge = Badge(label="medium", value="89.67%", default_color="green") - badge.write_badge('test_badge_medium.svg', overwrite=True) + badge.write_badge("test_badge_medium.svg", overwrite=True) self.assertLessEqual(badge.badge_width, 138) def test_badge_width_with_medium_value_text_zero_pad(self): """Test the width of a badge generated with a medium text value.""" - badge = Badge(label='medium no padding', - value='89.67%', - default_color='green', - num_padding_chars=0) + badge = Badge( + label="medium no padding", + value="89.67%", + default_color="green", + num_padding_chars=0, + ) - badge.write_badge('test_badge_medium_no_padding.svg', overwrite=True) + badge.write_badge("test_badge_medium_no_padding.svg", overwrite=True) self.assertLessEqual(badge.badge_width, 156) def test_badge_width_with_short_value_text(self): """Test the width of a badge generated with a short text value.""" - badge = Badge(label='short', - value='1', - default_color='green') + badge = Badge(label="short", value="1", default_color="green") - badge.write_badge('test_badge_short.svg', overwrite=True) + badge.write_badge("test_badge_short.svg", overwrite=True) self.assertLessEqual(badge.badge_width, 101) def test_badge_width_with_short_value_text_zero_pad(self): """Test the width of a badge generated with a short text value.""" - badge = Badge(label='short value no padding', - value='1', - default_color='green', - num_padding_chars=0) + badge = Badge( + label="short value no padding", + value="1", + default_color="green", + num_padding_chars=0, + ) - badge.write_badge('test_badge_short_no_padding.svg', overwrite=True) + badge.write_badge("test_badge_short_no_padding.svg", overwrite=True) self.assertLessEqual(badge.badge_width, 143) def test_badge_width_with_tiny_value_text(self): """Test the width of a badge generated with a short text value.""" - badge = Badge(label='a', - value='1', - default_color='green') + badge = Badge(label="a", value="1", default_color="green") - badge.write_badge('test_badge_tiny_text_value.svg', overwrite=True) + badge.write_badge("test_badge_tiny_text_value.svg", overwrite=True) self.assertLessEqual(badge.badge_width, 76) def test_badge_width_with_tiny_value_text_no_padding(self): """Test the width of a badge generated with a short text value.""" - badge = Badge(label='a', - value='1', - default_color='green', - num_padding_chars=0) + badge = Badge(label="a", value="1", default_color="green", num_padding_chars=0) - badge.write_badge('test_badge_tiny_text_value_no_padding.svg', overwrite=True) + badge.write_badge("test_badge_tiny_text_value_no_padding.svg", overwrite=True) self.assertLessEqual(badge.badge_width, 76) def test_badge_with_thresholds(self): """Test generating a badge using thresholds.""" - thresholds = { - 2: 'red', 4: 'orange', 6: 'green', 8: 'brightgreen' - } + thresholds = {2: "red", 4: "orange", 6: "green", 8: "brightgreen"} - badge = Badge('thresholds', '2.22', value_suffix='%', - thresholds=thresholds) + badge = Badge("thresholds", "2.22", value_suffix="%", thresholds=thresholds) - badge.write_badge('test_badge_thresholds.svg', overwrite=True) + badge.write_badge("test_badge_thresholds.svg", overwrite=True) def test_badge_with_text_color(self): """Test generating a badge with alternate text_color.""" - badge = Badge('text color', '2.22', value_suffix='%', - text_color='#010101,#101010') + badge = Badge( + "text color", "2.22", value_suffix="%", text_color="#010101,#101010" + ) - badge.write_badge('test_badge_text_color.svg', overwrite=True) + badge.write_badge("test_badge_text_color.svg", overwrite=True) def test_multiple_badges_in_one_session(self): badges = [ - Badge('multiple 1', value='100', value_suffix='%', num_padding_chars=0), - Badge('multiple 2', value='1234567890'), + Badge("multiple 1", value="100", value_suffix="%", num_padding_chars=0), + Badge("multiple 2", value="1234567890"), ] self.assertNotEqual(badges[0].badge_width, badges[1].badge_width) def test_multiple_badges_get_different_mask_id(self): badges = [ - Badge('multiple 1', value='100', value_suffix='%', num_padding_chars=0), - Badge('multiple 2', value='1234567890'), + Badge("multiple 1", value="100", value_suffix="%", num_padding_chars=0), + Badge("multiple 2", value="1234567890"), ] self.assertNotEqual(badges[0].mask_id, badges[1].mask_id) def test_integer_str_value_is_handled_as_integer(self): - badge = Badge('integer', value='1234') + badge = Badge("integer", value="1234") self.assertTrue(badge.value_is_int) self.assertFalse(badge.value_is_float) - badge.write_badge('test_badge_int_str.svg', overwrite=True) + badge.write_badge("test_badge_int_str.svg", overwrite=True) def test_integer_int_value_is_handled_as_integer(self): - badge = Badge('integer', value=1234) + badge = Badge("integer", value=1234) self.assertTrue(badge.value_is_int) self.assertFalse(badge.value_is_float) - badge.write_badge('test_badge_int.svg', overwrite=True) + badge.write_badge("test_badge_int.svg", overwrite=True) def test_float_str_value_is_handled_as_float(self): - badge = Badge('float str', value='1234.1') + badge = Badge("float str", value="1234.1") self.assertFalse(badge.value_is_int) self.assertTrue(badge.value_is_float) - badge.write_badge('test_badge_float_str.svg', overwrite=True) + badge.write_badge("test_badge_float_str.svg", overwrite=True) def test_float_value_is_handled_as_float(self): - badge = Badge('float int', value=1234.1) + badge = Badge("float int", value=1234.1) self.assertFalse(badge.value_is_int) self.assertTrue(badge.value_is_float) - badge.write_badge('test_badge_float.svg', overwrite=True) + badge.write_badge("test_badge_float.svg", overwrite=True) def test_float_value_with_zero_decimal(self): - badge = Badge('float with zeros', value='10.00') + badge = Badge("float with zeros", value="10.00") self.assertFalse(badge.value_is_int) self.assertTrue(badge.value_is_float) - badge.write_badge('test_badge_float_zeros.svg', overwrite=True) + badge.write_badge("test_badge_float_zeros.svg", overwrite=True) def test_float_value_with_non_zero_decimal(self): - badge = Badge('float str no decimal', value='10.01') + badge = Badge("float str no decimal", value="10.01") self.assertFalse(badge.value_is_int) self.assertTrue(badge.value_is_float) - badge.write_badge('test_badge_float-str-no-decimal.svg', overwrite=True) + badge.write_badge("test_badge_float-str-no-decimal.svg", overwrite=True) def test_padding_label(self): - badge = Badge('label padding', value='10.01', num_label_padding_chars=2) + badge = Badge("label padding", value="10.01", num_label_padding_chars=2) - badge.write_badge('test_badge_padding_label.svg', overwrite=True) + badge.write_badge("test_badge_padding_label.svg", overwrite=True) def test_padding_value(self): - badge = Badge('value padding', value='10.01', num_value_padding_chars=2) + badge = Badge("value padding", value="10.01", num_value_padding_chars=2) - badge.write_badge('test_badge_padding_value.svg', overwrite=True) + badge.write_badge("test_badge_padding_value.svg", overwrite=True) def test_value_formatting(self): - badge = Badge('value formatting', value="10", value_format="%s hits/sec") + badge = Badge("value formatting", value="10", value_format="%s hits/sec") self.assertEqual("10 hits/sec", badge.value_text) def test_font_name(self): - font = 'Arial, Helvetica, sans-serif' - badge = Badge('font', value=font, font_name=font) - badge.write_badge('test_badge_font.svg', overwrite=True) + font = "Arial, Helvetica, sans-serif" + badge = Badge("font", value=font, font_name=font) + badge.write_badge("test_badge_font.svg", overwrite=True) badge_repr = repr(badge) self.assertTrue("font_name='Arial, Helvetica, sans-serif'" in badge_repr) def test_invalid_font_name(self): - font = 'Invalid font' + font = "Invalid font" with self.assertRaises(ValueError): - _ = Badge('font', value=font, font_name=font) + _ = Badge("font", value=font, font_name=font) def test_font_size(self): for size in [10, 11, 12]: - badge = Badge('font size', value=size, font_size=size) - badge.write_badge('test_badge_font_size_%s.svg' % size, overwrite=True) + badge = Badge("font size", value=size, font_size=size) + badge.write_badge("test_badge_font_size_%s.svg" % size, overwrite=True) def test_font_size_repr(self): - badge = Badge('font size', value=10, font_size=10) + badge = Badge("font size", value=10, font_size=10) badge_repr = repr(badge) self.assertTrue("font_size=10" in badge_repr) def test_template_from_file(self): file = "tests/template.svg" - badge = Badge('template from file', value=file, template=file) + badge = Badge("template from file", value=file, template=file) _ = badge.badge_svg_text def test_repr_svg(self): - badge = Badge('label', 'value') + badge = Badge("label", "value") self.assertEqual(badge.badge_svg_text, badge._repr_svg_()) def test_str_value_with_threshold_and_default(self): - badge = Badge('label', value='fred', thresholds={'pass': 'green', 'fail': 'red'}, default_color='orange') - self.assertEqual('orange', badge.badge_color) + badge = Badge( + "label", + value="fred", + thresholds={"pass": "green", "fail": "red"}, + default_color="orange", + ) + self.assertEqual("orange", badge.badge_color) def test_invalid_color(self): with self.assertRaises(ValueError): - badge = Badge('label', value='fred', default_color='floberry') + badge = Badge("label", value="fred", default_color="floberry") _ = badge.badge_color_code def test_invalid_write_path(self): - badge = Badge('label', 'value') - with self.assertRaisesRegex(ValueError, r'File location may not be a directory\.'): - badge.write_badge('tests/') + badge = Badge("label", "value") + with self.assertRaisesRegex( + ValueError, r"File location may not be a directory\." + ): + badge.write_badge("tests/") - with self.assertRaisesRegex(RuntimeError, r'File ".*tests\/exists\.svg" already exists\.'): - badge.write_badge('tests/exists') - badge.write_badge('tests/exists') + with self.assertRaisesRegex( + RuntimeError, r'File ".*tests\/exists\.svg" already exists\.' + ): + badge.write_badge("tests/exists") + badge.write_badge("tests/exists") def test_arg_parsing(self): - args = parse_args(['-l', 'label', '-v', 'value']) - self.assertEqual('label', args.label) - self.assertEqual('value', args.value) + args = parse_args(["-l", "label", "-v", "value"]) + self.assertEqual("label", args.label) + self.assertEqual("value", args.value) def test_main_print(self): - main(['--label', 'label', '--value', 'value']) + main(["--label", "label", "--value", "value"]) def test_main_write_to_file(self): - main(['--label', 'label', '--value', 'value', '--file', 'test_badge_main.svg', '--overwrite']) + main( + [ + "--label", + "label", + "--value", + "value", + "--file", + "test_badge_main.svg", + "--overwrite", + ] + ) def test_main_thresholds(self): - main([ - '--label', 'label', - '--value', 'value', - '--file', 'test_badge_main_threshold.svg', - '--overwrite', - '2=red', '4=orange']) + main( + [ + "--label", + "label", + "--value", + "value", + "--file", + "test_badge_main_threshold.svg", + "--overwrite", + "2=red", + "4=orange", + ] + ) def test_named_threshold(self): - main([ - '--value', 'value', - '--file', 'test_badge_main_named_threshold.svg', - '--overwrite', - 'coverage']) + main( + [ + "--value", + "value", + "--file", + "test_badge_main_named_threshold.svg", + "--overwrite", + "coverage", + ] + ) def test_main_missing_value(self): - with self.assertRaisesRegex(ValueError, r'Label has not been set\. Please use --label argument\.'): - main(['--value', '123', '--file', 'test_badge_main.svg', '--overwrite']) + with self.assertRaisesRegex( + ValueError, r"Label has not been set\. Please use --label argument\." + ): + main(["--value", "123", "--file", "test_badge_main.svg", "--overwrite"]) def test_version_comparison(self): # Define thresholds: <3.0.0=red, <3.2.0=orange <999.0.0=green - badge = Badge('Version', value='1.0.0', thresholds={'3.0.0': 'red', '3.2.0': 'orange', '999.0.0': 'green'}, semver=True) - self.assertEqual('red', badge.badge_color) + badge = Badge( + "Version", + value="1.0.0", + thresholds={"3.0.0": "red", "3.2.0": "orange", "999.0.0": "green"}, + semver=True, + ) + self.assertEqual("red", badge.badge_color) # Define thresholds: <3.0.0=red, <3.2.0=orange <999.0.0=green - badge = Badge('Version', value='3.0.0', thresholds={'3.0.0': 'red', '3.2.0': 'orange', '999.0.0': 'green'}, semver=True) - self.assertEqual('orange', badge.badge_color) + badge = Badge( + "Version", + value="3.0.0", + thresholds={"3.0.0": "red", "3.2.0": "orange", "999.0.0": "green"}, + semver=True, + ) + self.assertEqual("orange", badge.badge_color) # Define thresholds: <3.0.0=red, <3.2.0=orange <999.0.0=green - badge = Badge('Version', value='3.1.0', thresholds={'3.0.0': 'red', '3.2.0': 'orange', '999.0.0': 'green'}, semver=True) - self.assertEqual('orange', badge.badge_color) + badge = Badge( + "Version", + value="3.1.0", + thresholds={"3.0.0": "red", "3.2.0": "orange", "999.0.0": "green"}, + semver=True, + ) + self.assertEqual("orange", badge.badge_color) # Define thresholds: <3.0.0=red, <3.2.0=orange <999.0.0=green - badge = Badge('Version', value='3.2.0', thresholds={'3.0.0': 'red', '3.2.0': 'orange', '999.0.0': 'green'}, semver=True) - self.assertEqual('green', badge.badge_color) + badge = Badge( + "Version", + value="3.2.0", + thresholds={"3.0.0": "red", "3.2.0": "orange", "999.0.0": "green"}, + semver=True, + ) + self.assertEqual("green", badge.badge_color) - badge = Badge('Version', value='3.2.1', thresholds={'3.0.0': 'red', '3.2.0': 'orange', '999.0.0': 'green'}, semver=True) - self.assertEqual('green', badge.badge_color) + badge = Badge( + "Version", + value="3.2.1", + thresholds={"3.0.0": "red", "3.2.0": "orange", "999.0.0": "green"}, + semver=True, + ) + self.assertEqual("green", badge.badge_color) # Define thresholds: <3.0.0=red, <3.2.0=orange <999.0.0=green - badge = Badge('Version', value='10.20.30', thresholds={'3.0.0': 'red', '3.2.0': 'orange', '999.0.0': 'green'}, semver=True) - self.assertEqual('green', badge.badge_color) + badge = Badge( + "Version", + value="10.20.30", + thresholds={"3.0.0": "red", "3.2.0": "orange", "999.0.0": "green"}, + semver=True, + ) + self.assertEqual("green", badge.badge_color) # Define thresholds: <3.0.0=red, <13.2.0=orange <999.0.0=green - badge = Badge('Version', value='14.0.0', thresholds={'3.0.0': 'red', '13.2.0': 'orange', '999.0.0': 'green'}, semver=True) - self.assertEqual('green', badge.badge_color) + badge = Badge( + "Version", + value="14.0.0", + thresholds={"3.0.0": "red", "13.2.0": "orange", "999.0.0": "green"}, + semver=True, + ) + self.assertEqual("green", badge.badge_color) - badge = Badge('Version', value='12.0.0', thresholds={'3.0.0': 'red', '13.2.0': 'orange', '999.0.0': 'green'}, semver=True) - self.assertEqual('orange', badge.badge_color) + badge = Badge( + "Version", + value="12.0.0", + thresholds={"3.0.0": "red", "13.2.0": "orange", "999.0.0": "green"}, + semver=True, + ) + self.assertEqual("orange", badge.badge_color)