Add pre-commit config and apply changes (#54)

- Add .pre-commit-config.yaml with initial config.
- Add python black pre-commit config
- Apply pre-commit code changes
- Add information to CONTRIBUTING.md
- Add pre-commit to build requirements
- Run pre-commit during travis CI build
- Use a travis build matrix to control when pre-commit runs
  in the CI process. Use explicit include to only run
  pre-commit on Python 3.9.
This commit is contained in:
Jon Grace-Cox
2022-08-08 19:02:02 -04:00
committed by GitHub
parent 217555a155
commit f16867114a
52 changed files with 655 additions and 385 deletions

16
.pre-commit-config.yaml Normal file
View File

@@ -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

View File

@@ -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'
python: '3.9'

View File

@@ -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:

View File

@@ -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
* [ ] Documentation for all docker bits

View File

@@ -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"

View File

@@ -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)

View File

@@ -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 <upper>=<color>. '
'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 <upper>=<color>. "
"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()

View File

@@ -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'
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"

View File

@@ -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,
},
}

View File

@@ -1,3 +1,2 @@
class UnknownBadgeTemplate(Exception):
"""The badge template is unknown."""

View File

@@ -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:

View File

@@ -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()

View File

@@ -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

View File

@@ -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"<html><head><title>Anybadge Web Server.</title></head>")
self.wfile.write(b"<body>")
@@ -67,8 +67,10 @@ class AnyBadgeHTTPRequestHandler(BaseHTTPRequestHandler):
<a href="http://localhost:{port}/?label=Project%20Awesomeness&value=110%">\
http://localhost:{port}/?label=Project%20Awesomeness&value=110%</a>
""".format(port=config.SERVER_PORT)
""".format(
port=config.SERVER_PORT
)
for line in help_text.splitlines():
self.wfile.write(str.encode('<p>%s</p>' % line))
self.wfile.write(b"</body></html>")
self.wfile.write(str.encode("<p>%s</p>" % line))
self.wfile.write(b"</body></html>")

View File

@@ -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

View File

@@ -20,4 +20,4 @@
<text x="{{ value anchor shadow }}" y="15" fill="#010101" fill-opacity=".3">{{ value }}</text>
<text x="{{ value anchor }}" y="14">{{ value }}</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -19,4 +19,4 @@
<g fill="{{ value text color }}" text-anchor="middle" font-family="{{ font name }}" font-size="{{ font size }}">
<text x="{{ value anchor }}" y="14">{{ value }}</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,3 +1,3 @@
pygments
pytest
pytest-cov
pytest-cov

View File

@@ -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
)
)

View File

@@ -20,4 +20,4 @@
<text x="111" y="15" fill="#010101" fill-opacity=".3">110%</text>
<text x="110" y="14">110%</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="80.5" y="15" fill="#010101" fill-opacity=".3">Color.AQUA</text>
<text x="79.5" y="14">Color.AQUA</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="82.5" y="15" fill="#010101" fill-opacity=".3">Color.BLACK</text>
<text x="81.5" y="14">Color.BLACK</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="79.0" y="15" fill="#010101" fill-opacity=".3">Color.BLUE</text>
<text x="78.0" y="14">Color.BLUE</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="100.5" y="15" fill="#010101" fill-opacity=".3">Color.BRIGHT_RED</text>
<text x="99.5" y="14">Color.BRIGHT_RED</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="114.0" y="15" fill="#010101" fill-opacity=".3">Color.BRIGHT_YELLOW</text>
<text x="113.0" y="14">Color.BRIGHT_YELLOW</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -20,4 +20,4 @@
<text x="75.0" y="15" fill="#010101" fill-opacity=".3">brightred</text>
<text x="74.0" y="14">brightred</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="83.0" y="15" fill="#010101" fill-opacity=".3">brightyellow</text>
<text x="82.0" y="14">brightyellow</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="88.5" y="15" fill="#010101" fill-opacity=".3">Color.FUCHSIA</text>
<text x="87.5" y="14">Color.FUCHSIA</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="80.5" y="15" fill="#010101" fill-opacity=".3">Color.GRAY</text>
<text x="79.5" y="14">Color.GRAY</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="84.0" y="15" fill="#010101" fill-opacity=".3">Color.GREEN</text>
<text x="83.0" y="14">Color.GREEN</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="102.0" y="15" fill="#010101" fill-opacity=".3">Color.LIGHT_GREY</text>
<text x="101.0" y="14">Color.LIGHT_GREY</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="74.0" y="15" fill="#010101" fill-opacity=".3">lightgrey</text>
<text x="73.0" y="14">lightgrey</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="79.5" y="15" fill="#010101" fill-opacity=".3">Color.LIME</text>
<text x="78.5" y="14">Color.LIME</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="90.5" y="15" fill="#010101" fill-opacity=".3">Color.MAROON</text>
<text x="89.5" y="14">Color.MAROON</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="79.0" y="15" fill="#010101" fill-opacity=".3">Color.NAVY</text>
<text x="78.0" y="14">Color.NAVY</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="83.0" y="15" fill="#010101" fill-opacity=".3">Color.OLIVE</text>
<text x="82.0" y="14">Color.OLIVE</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="89.0" y="15" fill="#010101" fill-opacity=".3">Color.ORANGE</text>
<text x="88.0" y="14">Color.ORANGE</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="86.0" y="15" fill="#010101" fill-opacity=".3">Color.PURPLE</text>
<text x="85.0" y="14">Color.PURPLE</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="75.5" y="15" fill="#010101" fill-opacity=".3">Color.RED</text>
<text x="74.5" y="14">Color.RED</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="85.0" y="15" fill="#010101" fill-opacity=".3">Color.SILVER</text>
<text x="84.0" y="14">Color.SILVER</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="79.0" y="15" fill="#010101" fill-opacity=".3">Color.TEAL</text>
<text x="78.0" y="14">Color.TEAL</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="83.0" y="15" fill="#010101" fill-opacity=".3">Color.WHITE</text>
<text x="82.0" y="14">Color.WHITE</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="89.0" y="15" fill="#010101" fill-opacity=".3">Color.YELLOW</text>
<text x="88.0" y="14">Color.YELLOW</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="111.5" y="15" fill="#010101" fill-opacity=".3">Color.YELLOW_GREEN</text>
<text x="110.5" y="14">Color.YELLOW_GREEN</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="82.0" y="15" fill="#010101" fill-opacity=".3">yellowgreen</text>
<text x="81.0" y="14">yellowgreen</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="93" y="15" fill="#010101" fill-opacity=".3">65.0%</text>
<text x="92" y="14">65.0%</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1023 B

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -19,4 +19,4 @@
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="91.5" y="14">Archimedes</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 963 B

After

Width:  |  Height:  |  Size: 964 B

View File

@@ -20,4 +20,4 @@
<text x="100" y="15" fill="#010101" fill-opacity=".3">passing</text>
<text x="99" y="14">passing</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,4 +20,4 @@
<text x="76" y="15" fill="#010101" fill-opacity=".3">2.22</text>
<text x="75" y="14">2.22</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1014 B

After

Width:  |  Height:  |  Size: 1015 B

View File

@@ -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"],
)

View File

@@ -20,4 +20,4 @@
<text x="{{ value anchor shadow }}" y="15" fill="#010101" fill-opacity=".3">{{ value }}</text>
<text x="{{ value anchor }}" y="14">{{ value }}</text>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -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)