Drop py27 support and refactor (#51) (#52)

Python version support
======================
- Drop Python 2.7 support
- Add testing for Python 3.4 and 3.9
- Set python version requirements in setup.py

Package changes
===============
- Split module up into a package with submodules
- Break templates out into package resources
- Incorporate server into same package
- Use Enum for colors
- Refresh example badges
- Simplify documentation - Remove cli options and python api
  documentation from the README as it is hard to maintain, and
  often goes out-of-date
- Add CONTRIBUTING.md
This commit is contained in:
Jon Grace-Cox
2022-03-10 07:26:46 -05:00
committed by GitHub
parent 6de2da8d58
commit b6ca345ebe
38 changed files with 821 additions and 1117 deletions

1
.gitignore vendored
View File

@@ -116,3 +116,4 @@ demo.svg
htmlcov/
**/*.svg
!anybadge/templates/*.svg

View File

@@ -1,14 +1,16 @@
dist: xenial
language: python
python:
- '2.7'
- '3.4'
- '3.5'
- '3.6'
- '3.7'
- '3.8'
- '3.9'
install:
- pip install -U setuptools pip -r build-requirements.txt
script:
- pytest --doctest-modules --cov=anybadge --cov-report html:htmlcov anybadge.py tests
- pytest --doctest-modules --cov=anybadge --cov-report html:htmlcov anybadge tests
before_deploy:
- sed -i "s/^version = .*/version = __version__ = \"$TRAVIS_TAG\"/" anybadge.py
deploy:
@@ -21,4 +23,4 @@ deploy:
on:
tags: true
all_branches: true
python: '3.7'
python: '3.9'

64
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,64 @@
# Contributing to anybadge
I love your input! I want to make contributing to this project as easy and transparent as possible, whether it's:
- Reporting a bug
- Discussing the current state of the code
- Submitting a fix
- Proposing new features
- 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
[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`
2. If you've added code that should be tested, add tests
3. If you've changed APIs, update the documentation
4. Ensure the test suite passes
5. Make sure your code lints (tbc)
6. Issue that pull request!
## 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
if that's a concern.
## Report bugs using Github's [issues](https://github.com/jongracecox/anybadge/issues)
I use GitHub issues to track public bugs. Report a bug by
[opening a new issue](https://github.com/jongracecox/anybadge/issues/new/choose).
## Write bug reports with detail, background, and sample code
**Great Bug Reports** tend to have:
- A quick summary and/or background
- Steps to reproduce
- Be specific!
- Give sample code if you can (ideally sample code that *anyone* with a basic setup can run to reproduce)
- What you expected would happen - (include explanation, screenshot, drawings, etc. to be exact)
- What actually happens
- Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)
People *love* thorough bug reports.
## Use a Consistent Coding Style
Please follow the existing coding style.
## License
By contributing, you agree that your contributions will be licensed under its MIT License.
# Technical stuff
## 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:
```python
import anybadge
print("""| Color Name | Hex Code | Example |
| ---------- | -------- | ------- |""")
for color in sorted([c for c in anybadge.colors.Color], key=lambda x: x.name):
file = 'examples/color_' + color.name.lower() + '.svg'
url = 'https://cdn.rawgit.com/jongracecox/anybadge/master/' + file
anybadge.Badge(label='Color', value=color, default_color=color.name.lower()).write_badge(file, overwrite=True)
print("| {color} | {hex} | ![]({url}) |".format(color=color.name.lower(), hex=color.value.upper(), url=url))
```

407
README.md
View File

@@ -11,6 +11,8 @@ Python project for generating badges for your projects
[![buymeacoffee](https://camo.githubusercontent.com/c3f856bacd5b09669157ed4774f80fb9d8622dd45ce8fdf2990d3552db99bd27/68747470733a2f2f7777772e6275796d6561636f666665652e636f6d2f6173736574732f696d672f637573746f6d5f696d616765732f6f72616e67655f696d672e706e67)](https://www.buymeacoffee.com/jongracecox)
Supports: Python 3.4-3.9 (2.7 support has been dropped)
## Overview
`anybadge` can be used to add badge generation to your Python projects,
@@ -140,12 +142,12 @@ Available named colors are:
| aqua | #00FFFF | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_aqua.svg) |
| black | #000000 | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_black.svg) |
| blue | #0000FF | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_blue.svg) |
| brightred | #FF0000 | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_brightred.svg) |
| brightyellow | #FFFF00 | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_brightyellow.svg) |
| bright_red | #FF0000 | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_bright_red.svg) |
| bright_yellow | #FFFF00 | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_bright_yellow.svg) |
| fuchsia | #FF00FF | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_fuchsia.svg) |
| gray | #808080 | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_gray.svg) |
| green | #4C1 | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_green.svg) |
| lightgrey | #9F9F9F | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_lightgrey.svg) |
| light_grey | #9F9F9F | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_light_grey.svg) |
| lime | #00FF00 | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_lime.svg) |
| maroon | #800000 | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_maroon.svg) |
| navy | #000080 | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_navy.svg) |
@@ -157,19 +159,7 @@ Available named colors are:
| teal | #008080 | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_teal.svg) |
| white | #FFFFFF | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_white.svg) |
| yellow | #DFB317 | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_yellow.svg) |
| yellowgreen | #A4A61D | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_yellowgreen.svg) |
This table was generated with the following code:
```python
print("""| Color Name | Hex Code | Example |
| ---------- | -------- | ------- |""")
for color, hex in sorted(anybadge.COLORS.items()):
file = 'examples/color_' + color + '.svg'
url = 'https://cdn.rawgit.com/jongracecox/anybadge/master/' + file
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))
```
| yellow_green | #A4A61D | ![](https://cdn.rawgit.com/jongracecox/anybadge/master/examples/color_yellow_green.svg) |
### Semantic version support
@@ -261,60 +251,9 @@ test1 = Badge(
test1.write_badge('test1.svg')
```
### Options
### Command-line options
These are the command line options:
```
positional arguments:
args 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...".
optional arguments:
-h, --help show this help message and exit
-l LABEL, --label LABEL
The badge label.
-v VALUE, --value VALUE
The badge value.
-m VALUE_FORMAT, --value-format VALUE_FORMAT
Formatting string for value (e.g. "%.2f" for 2dp
floats)
-c COLOR, --color COLOR
For fixed color badges use --colorto specify the badge
color.
-p PREFIX, --prefix PREFIX
Optional prefix for value.
-s SUFFIX, --suffix SUFFIX
Optional suffix for value.
-d PADDING, --padding PADDING
Number of characters to pad on either side of the
badge text.
-lp LABEL_PADDING, --label-padding LABEL_PADDING
Number of characters to pad on either side of the
badge label.
-vp VALUE_PADDING, --value-padding VALUE_PADDING
Number of characters to pad on either side of the
badge value.
-n FONT, --font FONT "DejaVu Sans,Verdana,Geneva,sans-serif"Font name.
Supported fonts: ,"Arial, Helvetica, sans-serif"
-z FONT_SIZE, --font-size FONT_SIZE
Font size.
-t TEMPLATE, --template TEMPLATE
Location of alternative template .svg file.
-s STYLE, --style STYLE
Alternative style of badge to create. Valid values are
"gitlab-scoped", "default". This overrides any templates
passed using --template.
-u, --use-max Use the maximum threshold color when the value exceeds
the maximum threshold.
-f FILE, --file FILE Output file location.
-o, --overwrite Overwrite output file if it already exists.
-r TEXT_COLOR, --text-color TEXT_COLOR
Text color. Single value affects both labeland value
colors. A comma separated pair affects label and value
text respectively.
```
The command line options can be viewed using `anybadge --help`.
Examples
--------
@@ -344,330 +283,10 @@ anybadge.py --label=pipeline --value=passing --file=pipeline.svg passing=green f
Python usage
============
Here is the output of ``help(anybadge)``::
```
Help on module anybadge:
NAME
anybadge - anybadge
DESCRIPTION
A Python module for generating badges for your projects, with a focus on
simplicity and flexibility.
CLASSES
builtins.object
Badge
class Badge(builtins.object)
| Badge(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)
|
| Badge class used to generate badges.
|
| Args:
| label(str): Badge label text.
| value(str): Badge value text.
| font_name(str, optional): Name of font to use.
| font_size(int, optional): Font size.
| num_padding_chars(float, optional): Number of padding characters to use to give extra
| space around text.
| num_label_padding_chars(float, optional): Number of padding characters to use to give extra
| space around label text.
| num_value_padding_chars(float, optional): Number of padding characters to use to give extra
| space around value text.
| template(str, optional): String containing the SVG template. This should be valid SVG
| file content with place holders for variables to be populated during rendering.
| style(str, optional): Style of badge to create. This will make anybadge render a badge in a
| different style. Valid values are "gitlab-scoped", "default". Default is "default".
| value_prefix(str, optional): Prefix to be placed before value.
| value_suffix(str, optional): Suffix to be placed after value.
| thresholds(dict, optional): A dictionary containing thresholds used to select badge
| color based on the badge value.
| default_color(str, optional): Badge color as a name or as an HTML color code.
| use_max_when_value_exceeds(bool, optional): Choose whether to use the maximum threshold
| value when the badge value exceeds the top threshold. Default is True.
| value_format(str, optional) String with formatting to be used to format the value text.
| text_color(str, optional): Text color as a name or as an HTML color code.
| semver(bool, optional): Used to indicate that the value is a semantic version number.
|
| Examples:
|
| Create a simple green badge:
|
| >>> badge = Badge('label', 123, default_color='green')
|
| Write a badge to file, overwriting any existing file:
|
| >>> badge = Badge('label', 123, default_color='green')
| >>> badge.write_badge('demo.svg', overwrite=True)
|
| Here are a number of examples showing thresholds, since there
| are certain situations that may not be obvious:
|
| >>> badge = Badge('pipeline', 'passing', thresholds={'passing': 'green', 'failing': 'red'})
| >>> badge.badge_color
| 'green'
|
| 2.32 is not <2
| 2.32 is < 4, so 2.32 yields orange
| >>> badge = Badge('pylint', 2.32, thresholds={2: 'red',
| ... 4: 'orange',
| ... 8: 'yellow',
| ... 10: 'green'})
| >>> badge.badge_color
| 'orange'
|
| 8 is not <8
| 8 is <4, so 8 yields orange
| >>> badge = Badge('pylint', 8, thresholds={2: 'red',
| ... 4: 'orange',
| ... 8: 'yellow',
| ... 10: 'green'})
| >>> badge.badge_color
| 'green'
|
| 10 is not <8, but use_max_when_value_exceeds defaults to
| True, so 10 yields green
| >>> badge = Badge('pylint', 11, thresholds={2: 'red',
| ... 4: 'orange',
| ... 8: 'yellow',
| ... 10: 'green'})
| >>> badge.badge_color
| 'green'
|
| 11 is not <10, and use_max_when_value_exceeds is set to
| False, so 11 yields the default color '#4c1'
| >>> badge = Badge('pylint', 11, use_max_when_value_exceeds=False,
| ... thresholds={2: 'red', 4: 'orange', 8: 'yellow',
| ... 10: 'green'})
| >>> badge.badge_color
| '#4c1'
|
| Methods defined here:
|
| __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.
|
| __repr__(self)
| Return a representation of the Badge object instance.
|
| The output of the __repr__ function could be used to recreate the current object.
|
| Examples:
|
| >>> badge = Badge('example', '123.456')
| >>> repr(badge)
| "Badge('example', '123.456')"
|
| >>> badge = Badge('example', '123.456', value_suffix='TB')
| >>> repr(badge)
| "Badge('example', '123.456', value_suffix='TB')"
|
| >>> badge = Badge('example', '123.456', text_color='#111111', value_suffix='TB')
| >>> repr(badge)
| "Badge('example', '123.456', value_suffix='TB', text_color='#111111')"
|
| >>> badge = Badge('example', '123', num_padding_chars=5)
| >>> repr(badge)
| "Badge('example', '123', num_padding_chars=5)"
|
| >>> badge = Badge('example', '123', num_label_padding_chars=5)
| >>> repr(badge)
| "Badge('example', '123', num_label_padding_chars=5)"
|
| >>> badge = Badge('example', '123', num_label_padding_chars=5, num_value_padding_chars=6,
| ... template='template.svg', value_prefix='$', thresholds={10: 'green', 30: 'red'},
| ... default_color='red', use_max_when_value_exceeds=False, value_format="%s m/s")
| >>> repr(badge)
| "Badge('example', '123', num_label_padding_chars=5, num_value_padding_chars=6, template='template.svg', value_prefix='$', thresholds={10: 'green', 30: 'red'}, default_color='red', use_max_when_value_exceeds=False, value_format='%s m/s')"
|
| __str__(self)
| Return string representation of badge.
|
| This will return the badge SVG text.
|
| Returns: str
|
| Examples:
|
| >>> print(Badge('example', '123')) # doctest: +ELLIPSIS
| <?xml version="1.0" encoding="UTF-8"?>
| ...
|
| get_text_width(self, text)
| Return the width of text.
|
| Args:
| text(str): Text to get the pixel width of.
|
| Returns:
| int: Pixel width of the given text based on the badges selected font.
|
| This implementation assumes a fixed font of:
|
| font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"
| >>> badge = Badge('x', 1, font_name='DejaVu Sans,Verdana,Geneva,sans-serif', font_size=11)
| >>> badge.get_text_width('pylint')
| 34
|
| write_badge(self, file_path, overwrite=False)
| Write badge to file.
|
| ----------------------------------------------------------------------
| Readonly properties defined here:
|
| arc_start
| The position where the arc on the gitlab-scoped should start.
|
| Returns: int
|
| Examples:
|
| >>> badge = Badge('pylint', '5')
| >>> badge.arc_start
| 58
|
| badge_color
| Badge color based on the configured thresholds.
|
| Returns: str
|
| badge_color_code
| Return the color code for the badge.
|
| Returns: str
|
| Raises: ValueError when an invalid badge color is set.
|
| badge_svg_text
| The badge SVG text.
|
| Returns: str
|
| badge_width
| The total width of badge.
|
| Returns: int
|
| Examples:
|
| >>> badge = Badge('pylint', '5')
| >>> badge.badge_width
| 61
|
| color_split_position
| The SVG x position where the color split should occur.
|
| Returns: int
|
| float_thresholds
| Thresholds as a dict using floats as keys.
|
| font_width
| Return the width multiplier for a font.
|
| Returns:
| int: Maximum pixel width of badges selected font.
|
| Example:
|
| >>> Badge(label='x', value='1').font_width
| 10
|
| label_anchor
| The SVG x position of the middle anchor for the label text.
|
| Returns: float
|
| label_anchor_shadow
| The SVG x position of the label shadow anchor.
|
| Returns: float
|
| label_width
| The SVG width of the label text.
|
| Returns: int
|
| semver_thresholds
| Thresholds as a dict using LooseVersion as keys.
|
| semver_version
| The semantic version represented by the value string.
|
| Returns: LooseVersion
|
| value_anchor
| The SVG x position of the middle anchor for the value text.
|
| Returns: float
|
| value_anchor_shadow
| The SVG x position of the value shadow anchor.
|
| Returns: float
|
| value_box_width
| The SVG width of the value text box.
|
| Returns: int
|
| value_is_float
| Identify whether the value text is a float.
|
| Returns: bool
|
| value_is_int
| Identify whether the value text is an int.
|
| Returns: bool
|
| value_type
| The Python type associated with the value.
|
| Returns: type
|
| value_width
| The SVG width of the value text.
|
| Returns: int
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
FUNCTIONS
main(args=None)
Generate a badge based on command line arguments.
parse_args(args)
Parse the command line arguments.
DATA
BADGE_TEMPLATES = {'coverage': {'label': 'coverage', 'suffix': '%', 't...
COLORS = {'aqua': '#00FFFF', 'black': '#000000', 'blue': '#0000FF', 'b...
DEFAULT_COLOR = '#4c1'
DEFAULT_FONT = 'DejaVu Sans,Verdana,Geneva,sans-serif'
DEFAULT_FONT_SIZE = 11
DEFAULT_TEXT_COLOR = '#fff'
FONT_WIDTHS = {'Arial, Helvetica, sans-serif': {11: 8}, 'DejaVu Sans,V...
MASK_ID_PREFIX = 'anybadge_'
NUM_PADDING_CHARS = 0.5
TEMPLATE_GITLAB_SCOPED_SVG = '<?xml version="1.0" encoding="UTF-8"?>\n...
TEMPLATE_SVG = '<?xml version="1.0" encoding="UTF-8"?>\n<svg xmln...ho...
VERSION_COMPARISON_SUPPORTED = True
__summary__ = 'A simple, flexible badge generator.'
__title__ = 'anybadge'
__uri__ = 'https://github.com/jongracecox/anybadge'
__version_info__ = ('0', '0', '0')
version = '0.0.0'
VERSION
0.0.0
For Python API details you can use the inbuilt documentation:
```python
from anybadge import badge
help(badge)
```

11
anybadge/__init__.py Normal file
View File

@@ -0,0 +1,11 @@
import re
from .badge import Badge
from .colors import Color
from .styles import Style
# Package information
version = __version__ = "0.0.0"
__version_info__ = tuple(re.split('[.-]', __version__))
__title__ = "anybadge"
__summary__ = "A simple, flexible badge generator."
__uri__ = "https://github.com/jongracecox/anybadge"

493
anybadge.py → anybadge/badge.py Executable file → Normal file
View File

@@ -1,147 +1,20 @@
#!/usr/bin/python
"""
anybadge
A Python module for generating badges for your projects, with a focus on
simplicity and flexibility.
"""
import sys
import os
import re
import argparse
import textwrap
from collections import OrderedDict
from . import config
from .colors import Color
from .exceptions import UnknownBadgeTemplate
from .helpers import _get_approx_string_width
# Try and obtain packaging package to support version comparison.
try:
from distutils.version import LooseVersion
VERSION_COMPARISON_SUPPORTED = True
except ImportError:
VERSION_COMPARISON_SUPPORTED = False
from .templates import get_template
from packaging.version import Version
# Package information
version = __version__ = "0.0.0"
__version_info__ = tuple(re.split('[.-]', __version__))
__title__ = "anybadge"
__summary__ = "A simple, flexible badge generator."
__uri__ = "https://github.com/jongracecox/anybadge"
# Set some defaults
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_'
# Dictionary for looking up approx pixel widths of
# supported fonts and font sizes.
FONT_WIDTHS = {
'DejaVu Sans,Verdana,Geneva,sans-serif': {
10: 9,
11: 10,
12: 11,
},
'Arial, Helvetica, sans-serif': {
11: 8,
},
}
# Create a dictionary of colors to make selections
# easier.
COLORS = {
'white': '#FFFFFF',
'silver': '#C0C0C0',
'gray': '#808080',
'black': '#000000',
'red': '#e05d44',
'brightred': '#FF0000',
'maroon': '#800000',
'olive': '#808000',
'lime': '#00FF00',
'brightyellow': '#FFFF00',
'yellow': '#dfb317',
'green': '#4c1',
'yellowgreen': '#a4a61d',
'aqua': '#00FFFF',
'teal': '#008080',
'blue': '#0000FF',
'navy': '#000080',
'fuchsia': '#FF00FF',
'purple': '#800080',
'orange': '#fe7d37',
'lightgrey': '#9f9f9f',
}
# Template SVG with placeholders for various items that
# will be added during final creation.
TEMPLATE_SVG = """<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="{{ badge width }}" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="{{ mask id }}">
<rect width="{{ badge width }}" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#{{ mask id }})">
<path fill="#555" d="M0 0h{{ color split x }}v20H0z"/>
<path fill="{{ color }}" d="M{{ color split x }} 0h{{ value width }}v20H{{ color split x }}z"/>
<path fill="url(#b)" d="M0 0h{{ badge width }}v20H0z"/>
</g>
<g fill="{{ label text color }}" text-anchor="middle" font-family="{{ font name }}" font-size="{{ font size }}">
<text x="{{ label anchor shadow }}" y="15" fill="#010101" fill-opacity=".3">{{ label }}</text>
<text x="{{ label anchor }}" y="14">{{ label }}</text>
</g>
<g fill="{{ value text color }}" text-anchor="middle" font-family="{{ font name }}" font-size="{{ font size }}">
<text x="{{ value anchor shadow }}" y="15" fill="#010101" fill-opacity=".3">{{ value }}</text>
<text x="{{ value anchor }}" y="14">{{ value }}</text>
</g>
</svg>"""
# Template SVG for GitLab Scoped label and value badges with placeholders for
# various items that will be added during final creation.
TEMPLATE_GITLAB_SCOPED_SVG = """<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="{{ badge width }}" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="{{ mask id }}">
<rect width="{{ badge width }}" height="20" rx="10" fill="#fff"/>
</mask>
<g mask="url(#{{ mask id }})">
<path fill="{{ color }}" d="M0 0h{{ badge width }}v20H0z"/>
<path fill="#262626" d="M{{ color split x }} 2h{{ value box width }}v16H{{ color split x }}z"/>
<path fill="#262626" d="M{{ arc start }},18 a1,1 0 0,0 0,-16"/>
<path fill="url(#b)" d="M0 0h{{ badge width }}v20H0z"/>
</g>
<g fill="{{ label text color }}" text-anchor="middle" font-family="{{ font name }}" font-size="{{ font size }}">
<text x="{{ label anchor }}" y="14">{{ label }}</text>
</g>
<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>"""
# Define some templates that can be used for common badge types, saving
# from having to provide thresholds and labels each time.
BADGE_TEMPLATES = {
'pylint': {
'threshold': '2=red 4=orange 8=yellow 10=green',
'label': 'pylint'
},
'coverage': {
'threshold': '50=red 60=orange 80=yellow 100=green',
'label': 'coverage',
'suffix': '%'
}
}
class Badge(object):
class Badge:
"""
Badge class used to generate badges.
@@ -234,34 +107,32 @@ class Badge(object):
"""Constructor for Badge class."""
# Set defaults if values were not passed
if not font_name:
font_name = DEFAULT_FONT
font_name = config.DEFAULT_FONT
if not font_size:
font_size = DEFAULT_FONT_SIZE
font_size = config.DEFAULT_FONT_SIZE
if num_label_padding_chars is None:
if num_padding_chars is None:
num_label_padding_chars = NUM_PADDING_CHARS
num_label_padding_chars = config.NUM_PADDING_CHARS
else:
num_label_padding_chars = num_padding_chars
if num_value_padding_chars is None:
if num_padding_chars is None:
num_value_padding_chars = NUM_PADDING_CHARS
num_value_padding_chars = config.NUM_PADDING_CHARS
else:
num_value_padding_chars = num_padding_chars
if not template:
template = TEMPLATE_SVG
template = get_template('default')
if style not in ['gitlab-scoped']:
style = "default"
if not default_color:
default_color = DEFAULT_COLOR
default_color = config.DEFAULT_COLOR
if not text_color:
text_color = DEFAULT_TEXT_COLOR
text_color = config.DEFAULT_TEXT_COLOR
self.label = label
self.value = value
self.value_is_version = semver
if self.value_is_version and not VERSION_COMPARISON_SUPPORTED:
raise RuntimeError("Version comparison is not supported.")
self.value_format = value_format
if value_format:
@@ -272,8 +143,11 @@ class Badge(object):
self.value_suffix = value_suffix
self.value_text = value_prefix + value_text + value_suffix
if font_name not in FONT_WIDTHS:
raise ValueError('Font name "%s" not found. Available fonts: %s' % (font_name, ', '.join(FONT_WIDTHS.keys())))
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()))
)
self.font_name = font_name
self.font_size = font_size
self.num_label_padding_chars = num_label_padding_chars
@@ -329,19 +203,19 @@ class Badge(object):
"""
optional_args = ""
if self.font_name != DEFAULT_FONT:
if self.font_name != config.DEFAULT_FONT:
optional_args += ", font_name=%s" % repr(self.font_name)
if self.font_size != DEFAULT_FONT_SIZE:
if self.font_size != config.DEFAULT_FONT_SIZE:
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 != NUM_PADDING_CHARS:
if self.num_label_padding_chars != config.NUM_PADDING_CHARS:
optional_args += ", num_padding_chars=%s" % repr(self.num_label_padding_chars)
else:
if self.num_label_padding_chars != NUM_PADDING_CHARS:
if self.num_label_padding_chars != config.NUM_PADDING_CHARS:
optional_args += ", num_label_padding_chars=%s" % repr(self.num_label_padding_chars)
if self.num_value_padding_chars != NUM_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 != TEMPLATE_SVG:
if self.template != get_template('default'):
optional_args += ", template=%s" % repr(self.template)
if self.style != 'default':
optional_args += ", style=%s" % repr(self.style)
@@ -351,13 +225,13 @@ class Badge(object):
optional_args += ", value_suffix=%s" % repr(self.value_suffix)
if self.thresholds:
optional_args += ", thresholds=%s" % repr(self.thresholds)
if self.default_color != DEFAULT_COLOR:
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)
if self.value_format:
optional_args += ", value_format=%s" % repr(self.value_format)
if self.text_color != DEFAULT_TEXT_COLOR:
if self.text_color != config.DEFAULT_TEXT_COLOR:
optional_args += ", text_color=%s" % repr(self.text_color)
return "%s(%s, %s%s)" % (
@@ -375,7 +249,6 @@ class Badge(object):
"""
return self.badge_svg_text
@classmethod
def _get_next_mask_id(cls):
"""Return a new mask ID from a singleton sequence maintained on the class.
@@ -387,7 +260,7 @@ class Badge(object):
cls.mask_id += 1
return MASK_ID_PREFIX + str(cls.mask_id)
return config.MASK_ID_PREFIX + str(cls.mask_id)
def _get_svg_template(self):
"""Return the correct SVG template to render, based on the style and template
@@ -396,10 +269,16 @@ class Badge(object):
Returns: str
"""
if self.style == "gitlab-scoped":
return TEMPLATE_GITLAB_SCOPED_SVG
return get_template('gitlab_scoped')
# Identify whether template is a file or the actual template text
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:
return file_handle.read()
else:
@@ -409,16 +288,16 @@ class Badge(object):
def semver_version(self):
"""The semantic version represented by the value string.
Returns: LooseVersion
Returns: Version
"""
return LooseVersion(self.value)
return Version(self.value)
@property
def semver_thresholds(self):
"""Thresholds as a dict using LooseVersion as keys."""
# LooseVersion is not a hashable type, so can't be used to create an
"""Thresholds as a dict using Version as keys."""
# Version is not a hashable type, so can't be used to create an
# ordered dict directly. First we need to create an ordered list of keys
ordered_keys = sorted(self.thresholds.keys(), key=LooseVersion)
ordered_keys = sorted(self.thresholds.keys(), key=Version)
return OrderedDict((key, self.thresholds[key]) for key in ordered_keys)
@property
@@ -441,7 +320,7 @@ class Badge(object):
try:
_ = float(self.value)
except ValueError:
except (ValueError, TypeError):
return False
else:
return True
@@ -455,7 +334,7 @@ class Badge(object):
try:
a = float(self.value)
b = int(self.value)
except ValueError:
except (ValueError, TypeError):
return False
else:
return a == b
@@ -467,7 +346,7 @@ class Badge(object):
Returns: type
"""
if self.value_is_version:
return LooseVersion
return Version
if self.value_is_float:
return float
elif self.value_is_int:
@@ -511,7 +390,7 @@ class Badge(object):
>>> Badge(label='x', value='1').font_width
10
"""
return FONT_WIDTHS[self.font_name][self.font_size]
return config.FONT_WIDTHS[self.font_name][self.font_size]
@property
def color_split_position(self):
@@ -658,7 +537,7 @@ class Badge(object):
# Set value and thresholds based on the value type. This will result in either
# value and thresholds as floats or value and thresholds as semantic versions.
if self.value_type == LooseVersion:
if self.value_type == Version:
value = self.semver_version
thresholds = self.semver_thresholds
else:
@@ -682,7 +561,7 @@ class Badge(object):
return self.default_color
@property
def badge_color_code(self):
def badge_color_code(self) -> str:
"""Return the color code for the badge.
Returns: str
@@ -690,13 +569,36 @@ class Badge(object):
Raises: ValueError when an invalid badge color is set.
"""
color = self.badge_color
if isinstance(color, Color):
return color.value
if color[0] == '#':
return color
color = color.upper()
prefixes = ["BRIGHT", "YELLOW", "LIGHT"]
try:
return COLORS[color]
return Color[color.upper()].value
except KeyError:
raise ValueError('Invalid color code "%s". Valid color codes are: %s', (color, ", ".join(COLORS.keys())))
pass
# For backward compatibility with old color names (that were lowercase and didn't
# 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:
try:
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())))
)
def write_badge(self, file_path, overwrite=False):
"""Write badge to file."""
@@ -716,248 +618,3 @@ class Badge(object):
with open(path, mode='w') as file_handle:
file_handle.write(self.badge_svg_text)
# Based on the following SO answer: https://stackoverflow.com/a/16008023/6252525
def _get_approx_string_width(text, font_width, fixed_width=False):
"""
Get the approximate width of a string using a specific average font width.
Args:
text(str): Text string to calculate width of.
font_width(int): Average width of font characters.
fixed_width(bool): Indicates that the font is fixed width.
Returns:
int: Width of string in pixels.
Examples:
Call the function with a string and the maximum character width of the font you are using:
>>> int(_get_approx_string_width('hello', 10))
29
This example shows the comparison of simplistic calculation based on a fixed width.
Given a test string and a fixed font width of 10, we can calculate the width
by multiplying the length and the font character with:
>>> test_string = 'GOOGLE|ijkl'
>>> _get_approx_string_width(test_string, 10, fixed_width=True)
110
Since some characters in the string are thinner than others we expect that the
apporximate text width will be narrower than the fixed width calculation:
>>> _get_approx_string_width(test_string, 10)
77
"""
if fixed_width:
return len(text) * font_width
size = 0.0
# A dictionary containing percentages that relate to how wide
# each character will be represented in a variable width font.
# These percentages can be calculated using the ``_get_character_percentage_dict`` function.
char_width_percentages = {
"lij|' ": 40.0,
'![]fI.,:;/\\t': 50.0,
'`-(){}r"': 60.0,
'*^zcsJkvxy': 70.0,
'aebdhnopqug#$L+<>=?_~FZT0123456789': 70.0,
'BSPEAKVXY&UwNRCHD': 70.0,
'QGOMm%W@': 100.0
}
for s in text:
percentage = 100.0
for k in char_width_percentages.keys():
if s in k:
percentage = char_width_percentages[k]
break
size += (percentage / 100.0) * float(font_width)
return int(size)
# This is a helper function that can be used to generate alternate dictionaries
# for the _get_approx_string_width function. The function is not needed for
# normal operation of this package, and since it depends on the PIL package,
# which is not included in the dependencies the function will remain commented out.
#
# def _get_character_percentage_dict(font_path, font_size):
# """Get the dictionary used to estimate variable width font text lengths.
#
# Args:
# font_path(str): Path to valid font file.
# font_size(int): Font size to use.
#
# Returns: dict
#
# This function can be used to calculate the dictionary used in the
# ``get_approx_string_width`` function.
#
# Examples:
# >>> _get_character_percentage_dict('/Library/Fonts/Verdana.ttf', 9) # doctest: +ELLIPSIS
# {"lij|' ": 40, '![]fI.,:;/\\\\t': 50, '`-(){}r"': 60, '*^zcsJkvxy': 70, ...
# """
# from PIL import ImageFont
#
# # List of groups in size order, smallest to largest
# char_width_groups = [
# "lij|' ",
# '![]fI.,:;/\\t',
# '`-(){}r"',
# '*^zcsJkvxy',
# 'aebdhnopqug#$L+<>=?_~FZT' + digits,
# 'BSPEAKVXY&UwNRCHD',
# 'QGOMm%W@',
# ]
#
# def get_largest_in_group(group):
# """Get the widest character from the group."""
# return max([ImageFont.truetype(font_path, font_size).getsize(c)[0] for c in group])
#
# largest = char_width_groups[-1]
# font_width = get_largest_in_group(largest)
# return {group: int((get_largest_in_group(group) / font_width) * 100)
# for group in char_width_groups}
def parse_args(args):
"""Parse the command line arguments."""
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
thresholds for coloring. Values can be passed as string, integer or floating
point. The type will be detected automatically.
Running the utility with a --file option will result in the .svg image being
written to file. Without the --file option the .svg file content will be
written to stdout, so can be redirected to a file.
Some thresholds have been built in to save time. To use these thresholds you
can simply specify the template name instead of threshold value/color pairs.
examples:
Here are some usage specific examples that may save time on defining
thresholds.
Pylint
anybadge.py --value=2.22 --file=pylint.svg pylint
anybadge.py --label=pylint --value=2.22 --file=pylint.svg \\
2=red 4=orange 8=yellow 10=green
Coverage
anybadge.py --value=65 --file=coverage.svg coverage
anybadge.py --label=coverage --value=65 --suffix='%%' --file=coverage.svg \\
50=red 60=orange 80=yellow 100=green
CI Pipeline
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=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=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 FONT_WIDTHS.keys()]),
default=DEFAULT_FONT)
parser.add_argument('-z', '--font-size', type=int, help='Font size.',
default=DEFAULT_FONT_SIZE)
parser.add_argument('-t', '--template', type=str, help='Location of alternative '
'template .svg file.',
default=TEMPLATE_SVG)
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=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)
def main(args=None):
"""Generate a badge based on command line arguments."""
# Args may be sent from command line of as args directly.
if not args:
args = sys.argv[1:]
# Parse command line arguments
args = parse_args(args)
label = args.label
threshold_text = args.args
suffix = args.suffix
# Check whether thresholds were sent as one word, and is in the
# list of templates. If so, swap in the template.
if len(args.args) == 1 and args.args[0] in BADGE_TEMPLATES:
template_name = args.args[0]
template_dict = BADGE_TEMPLATES[template_name]
threshold_text = template_dict['threshold'].split(' ')
if not args.label:
label = template_dict['label']
if not args.suffix and 'suffix' in template_dict:
suffix = template_dict['suffix']
if not label:
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_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)
if args.file:
# Write badge SVG to file
badge.write_badge(args.file, overwrite=args.overwrite)
else:
print(badge.badge_svg_text)
if __name__ == '__main__':
main()

146
anybadge/cli.py Normal file
View File

@@ -0,0 +1,146 @@
import argparse
import sys
import textwrap
from anybadge.styles import Style
from anybadge.templates import get_template
from . import config
from .badge import Badge
def parse_args(args):
"""Parse the command line arguments."""
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
thresholds for coloring. Values can be passed as string, integer or floating
point. The type will be detected automatically.
Running the utility with a --file option will result in the .svg image being
written to file. Without the --file option the .svg file content will be
written to stdout, so can be redirected to a file.
Some thresholds have been built in to save time. To use these thresholds you
can simply specify the template name instead of threshold value/color pairs.
examples:
Here are some usage specific examples that may save time on defining
thresholds.
Pylint
anybadge.py --value=2.22 --file=pylint.svg pylint
anybadge.py --label=pylint --value=2.22 --file=pylint.svg \\
2=red 4=orange 8=yellow 10=green
Coverage
anybadge.py --value=65 --file=coverage.svg coverage
anybadge.py --label=coverage --value=65 --suffix='%%' --file=coverage.svg \\
50=red 60=orange 80=yellow 100=green
CI Pipeline
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...".')
return parser.parse_args(args)
def main(args=None):
"""Generate a badge based on command line arguments."""
# Args may be sent from command line of as args directly.
if not args:
args = sys.argv[1:]
# Parse command line arguments
args = parse_args(args)
label = args.label
threshold_text = args.args
suffix = args.suffix
# Check whether thresholds were sent as one word, and is in the
# list of available styles. If so, swap in the style.
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(' ')
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.')
# Create threshold list from args
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)
if args.file:
# Write badge SVG to file
badge.write_badge(args.file, overwrite=args.overwrite)
else:
print(badge.badge_svg_text)
if __name__ == '__main__':
main()

27
anybadge/colors.py Normal file
View File

@@ -0,0 +1,27 @@
# Create a dictionary of colors to make selections
# easier.
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'

22
anybadge/config.py Normal file
View File

@@ -0,0 +1,22 @@
# Set some defaults
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_'
# Dictionary for looking up approx pixel widths of
# supported fonts and font sizes.
FONT_WIDTHS = {
'DejaVu Sans,Verdana,Geneva,sans-serif': {
10: 9,
11: 10,
12: 11,
},
'Arial, Helvetica, sans-serif': {
11: 8,
},
}

3
anybadge/exceptions.py Normal file
View File

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

62
anybadge/helpers.py Normal file
View File

@@ -0,0 +1,62 @@
# Based on the following SO answer: https://stackoverflow.com/a/16008023/6252525
def _get_approx_string_width(text, font_width, fixed_width=False):
"""
Get the approximate width of a string using a specific average font width.
Args:
text(str): Text string to calculate width of.
font_width(int): Average width of font characters.
fixed_width(bool): Indicates that the font is fixed width.
Returns:
int: Width of string in pixels.
Examples:
Call the function with a string and the maximum character width of the font you are using:
>>> int(_get_approx_string_width('hello', 10))
29
This example shows the comparison of simplistic calculation based on a fixed width.
Given a test string and a fixed font width of 10, we can calculate the width
by multiplying the length and the font character with:
>>> test_string = 'GOOGLE|ijkl'
>>> _get_approx_string_width(test_string, 10, fixed_width=True)
110
Since some characters in the string are thinner than others we expect that the
apporximate text width will be narrower than the fixed width calculation:
>>> _get_approx_string_width(test_string, 10)
77
"""
if fixed_width:
return len(text) * font_width
size = 0.0
# A dictionary containing percentages that relate to how wide
# each character will be represented in a variable width font.
# These percentages can be calculated using the ``_get_character_percentage_dict`` function.
char_width_percentages = {
"lij|' ": 40.0,
'![]fI.,:;/\\t': 50.0,
'`-(){}r"': 60.0,
'*^zcsJkvxy': 70.0,
'aebdhnopqug#$L+<>=?_~FZT0123456789': 70.0,
'BSPEAKVXY&UwNRCHD': 70.0,
'QGOMm%W@': 100.0
}
for s in text:
percentage = 100.0
for k in char_width_percentages.keys():
if s in k:
percentage = char_width_percentages[k]
break
size += (percentage / 100.0) * float(font_width)
return int(size)

75
anybadge/server/cli.py Normal file
View File

@@ -0,0 +1,75 @@
import argparse
import logging
from http.server import HTTPServer
from os import environ
from anybadge.server.request_handler import AnyBadgeHTTPRequestHandler
from anybadge.server import config
logger = logging.getLogger(__name__)
def run(listen_address: str = None, port: int = None):
if not listen_address:
listen_address = config.DEFAULT_SERVER_LISTEN_ADDRESS
if not port:
port = config.DEFAULT_SERVER_PORT
server_address = (listen_address, port)
global SERVER_PORT, SERVER_LISTEN_ADDRESS
SERVER_PORT = port
SERVER_LISTEN_ADDRESS = listen_address
httpd = HTTPServer(server_address, AnyBadgeHTTPRequestHandler)
logger.info('Serving at: http://%s:%s' % server_address)
httpd.serve_forever()
def parse_args():
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.')
return parser.parse_args()
def main():
"""Run server."""
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_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'])
# Parse command line args
args = parse_args()
# Set logging level
logging_level = DEFAULT_LOGGING_LEVEL
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.')
run(listen_address=args.listen_address, port=args.port)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,9 @@
import logging
DEFAULT_SERVER_PORT = 8000
DEFAULT_SERVER_LISTEN_ADDRESS = 'localhost'
DEFAULT_LOGGING_LEVEL = logging.INFO
SERVER_PORT = DEFAULT_SERVER_PORT
SERVER_LISTEN_ADDRESS = DEFAULT_SERVER_LISTEN_ADDRESS

View File

@@ -0,0 +1,74 @@
import logging
import urllib.parse as urlparse
from http.server import BaseHTTPRequestHandler
from anybadge import Badge
from anybadge.server import config
logger = logging.getLogger(__name__)
class AnyBadgeHTTPRequestHandler(BaseHTTPRequestHandler):
"""Request handler for anybadge HTTP server."""
def do_HEAD(self):
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.')
self.do_HEAD()
# Ignore request for favicon
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'
# Extract the label and value portions
if 'label' in url_query:
label = url_query['label'][0]
if 'value' in url_query:
value = url_query['value'][0]
logging.debug('Label: %s Value: %s', label, value)
if label and value and color:
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.')
self.wfile.write(b"<html><head><title>Anybadge Web Server.</title></head>")
self.wfile.write(b"<body>")
help_text = """
<h1>Welcome to the Anybadge Web Server.</h1>
You are seeing this message because you haven't passed all the query parameters
to display a badge.
You need to pass at least a <b>label</b> and <b>value</b> parameter.
Here is an example:
<a href="http://localhost:{port}/?label=Project%20Awesomeness&value=110%">\
http://localhost:{port}/?label=Project%20Awesomeness&value=110%</a>
""".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>")

25
anybadge/styles.py Normal file
View File

@@ -0,0 +1,25 @@
from enum import Enum
# Define some templates that can be used for common badge types, saving
# from having to provide thresholds and labels each time.
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', '%')
def __init__(self, template, threshold, label, suffix=None):
self.template = template
self.threshold = threshold
self.label = label
self.suffix = suffix
@classmethod
def exists(cls, name: str):
"""Test whether a style exists."""
try:
_ = cls[name]
return True
except KeyError:
return False

View File

@@ -0,0 +1,19 @@
"""Templates package."""
import pkgutil
from anybadge.exceptions import UnknownBadgeTemplate
def get_template(name: str) -> str:
"""Get a template by name.
Examples:
>>> get_template('default') # doctest: +ELLIPSIS
'<?xml version="1.0" encoding="UTF-8...
"""
try:
return pkgutil.get_data(__name__, name + ".svg").decode("utf-8")
except FileNotFoundError as e:
raise UnknownBadgeTemplate from e

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="{{ badge width }}" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="{{ mask id }}">
<rect width="{{ badge width }}" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#{{ mask id }})">
<path fill="#555" d="M0 0h{{ color split x }}v20H0z"/>
<path fill="{{ color }}" d="M{{ color split x }} 0h{{ value width }}v20H{{ color split x }}z"/>
<path fill="url(#b)" d="M0 0h{{ badge width }}v20H0z"/>
</g>
<g fill="{{ label text color }}" text-anchor="middle" font-family="{{ font name }}" font-size="{{ font size }}">
<text x="{{ label anchor shadow }}" y="15" fill="#010101" fill-opacity=".3">{{ label }}</text>
<text x="{{ label anchor }}" y="14">{{ label }}</text>
</g>
<g fill="{{ value text color }}" text-anchor="middle" font-family="{{ font name }}" font-size="{{ font size }}">
<text x="{{ value anchor shadow }}" y="15" fill="#010101" fill-opacity=".3">{{ value }}</text>
<text x="{{ value anchor }}" y="14">{{ value }}</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="{{ badge width }}" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="{{ mask id }}">
<rect width="{{ badge width }}" height="20" rx="10" fill="#fff"/>
</mask>
<g mask="url(#{{ mask id }})">
<path fill="{{ color }}" d="M0 0h{{ badge width }}v20H0z"/>
<path fill="#262626" d="M{{ color split x }} 2h{{ value box width }}v16H{{ color split x }}z"/>
<path fill="#262626" d="M{{ arc start }},18 a1,1 0 0,0 0,-16"/>
<path fill="url(#b)" d="M0 0h{{ badge width }}v20H0z"/>
</g>
<g fill="{{ label text color }}" text-anchor="middle" font-family="{{ font name }}" font-size="{{ font size }}">
<text x="{{ label anchor }}" y="14">{{ label }}</text>
</g>
<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>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,160 +0,0 @@
#!/usr/bin/python
"""
Anybadge Server
This package provides a server for anybadge.
"""
from __future__ import print_function
from os import environ
import logging
import argparse
from anybadge import Badge
# Import the correct version of HTTP server
try:
from http.server import HTTPServer, BaseHTTPRequestHandler
except ImportError:
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
# Import the correct version of urlparse, depending on which version
# of Python we are using
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
logger = logging.getLogger(__name__)
DEFAULT_SERVER_PORT = 8000
DEFAULT_SERVER_LISTEN_ADDRESS = 'localhost'
DEFAULT_LOGGING_LEVEL = logging.INFO
SERVER_PORT = DEFAULT_SERVER_PORT
SERVER_LISTEN_ADDRESS = DEFAULT_SERVER_LISTEN_ADDRESS
class AnybadgeHTTPRequestHandler(BaseHTTPRequestHandler):
"""Request handler for Anybadge HTTP server."""
def do_HEAD(self):
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.')
self.do_HEAD()
# Ignore request for favicon
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'
# Extract the label and value portions
if 'label' in url_query:
label = url_query['label'][0]
if 'value' in url_query:
value = url_query['value'][0]
logging.debug('Label: %s Value: %s', label, value)
if label and value and color:
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.')
self.wfile.write(b"<html><head><title>Anybadge Web Server.</title></head>")
self.wfile.write(b"<body>")
help_text = """
<h1>Welcome to the Anybadge Web Server.</h1>
You are seeing this message because you haven't passed all the query parameters
to display a badge.
You need to pass at least a <b>label</b> and <b>value</b> parameter.
Here is an example:
<a href="http://localhost:{port}/?label=Project%20Awesomeness&value=110%">\
http://localhost:{port}/?label=Project%20Awesomeness&value=110%</a>
""".format(port=SERVER_PORT)
for line in help_text.splitlines():
self.wfile.write(str.encode('<p>%s</p>' % line))
self.wfile.write(b"</body></html>")
def run(listen_address=DEFAULT_SERVER_LISTEN_ADDRESS, port=DEFAULT_SERVER_PORT):
server_address = (listen_address, port)
global SERVER_PORT, SERVER_LISTEN_ADDRESS
SERVER_PORT = port
SERVER_LISTEN_ADDRESS = listen_address
httpd = HTTPServer(server_address, AnybadgeHTTPRequestHandler)
logging.info('Serving at: http://%s:%s' % server_address)
httpd.serve_forever()
def parse_args():
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.')
return parser.parse_args()
def main():
"""Run server."""
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_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'])
# Parse command line args
args = parse_args()
# Set logging level
logging_level = DEFAULT_LOGGING_LEVEL
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.')
run(listen_address=args.listen_address, port=args.port)
if __name__ == '__main__':
main()

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="79" height="20">
<svg xmlns="http://www.w3.org/2000/svg" width="118" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="anybadge_1">
<rect width="79" height="20" rx="3" fill="#fff"/>
<mask id="anybadge_43">
<rect width="118" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#anybadge_1)">
<g mask="url(#anybadge_43)">
<path fill="#555" d="M0 0h41v20H0z"/>
<path fill="#00FFFF" d="M41 0h38v20H41z"/>
<path fill="url(#b)" d="M0 0h79v20H0z"/>
<path fill="#00FFFF" d="M41 0h77v20H41z"/>
<path fill="url(#b)" d="M0 0h118v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
<text x="20.5" y="14">Color</text>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="61.0" y="15" fill="#010101" fill-opacity=".3">aqua</text>
<text x="60.0" y="14">aqua</text>
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="83" height="20">
<svg xmlns="http://www.w3.org/2000/svg" width="122" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="anybadge_2">
<rect width="83" height="20" rx="3" fill="#fff"/>
<mask id="anybadge_44">
<rect width="122" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#anybadge_2)">
<g mask="url(#anybadge_44)">
<path fill="#555" d="M0 0h41v20H0z"/>
<path fill="#000000" d="M41 0h42v20H41z"/>
<path fill="url(#b)" d="M0 0h83v20H0z"/>
<path fill="#000000" d="M41 0h81v20H41z"/>
<path fill="url(#b)" d="M0 0h122v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
<text x="20.5" y="14">Color</text>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="63.0" y="15" fill="#010101" fill-opacity=".3">black</text>
<text x="62.0" y="14">black</text>
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="76" height="20">
<svg xmlns="http://www.w3.org/2000/svg" width="115" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="anybadge_3">
<rect width="76" height="20" rx="3" fill="#fff"/>
<mask id="anybadge_45">
<rect width="115" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#anybadge_3)">
<g mask="url(#anybadge_45)">
<path fill="#555" d="M0 0h41v20H0z"/>
<path fill="#0000FF" d="M41 0h35v20H41z"/>
<path fill="url(#b)" d="M0 0h76v20H0z"/>
<path fill="#0000FF" d="M41 0h74v20H41z"/>
<path fill="url(#b)" d="M0 0h115v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
<text x="20.5" y="14">Color</text>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="59.5" y="15" fill="#010101" fill-opacity=".3">blue</text>
<text x="58.5" y="14">blue</text>
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="95" height="20">
<svg xmlns="http://www.w3.org/2000/svg" width="134" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="anybadge_6">
<rect width="95" height="20" rx="3" fill="#fff"/>
<mask id="anybadge_48">
<rect width="134" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#anybadge_6)">
<g mask="url(#anybadge_48)">
<path fill="#555" d="M0 0h41v20H0z"/>
<path fill="#FF00FF" d="M41 0h54v20H41z"/>
<path fill="url(#b)" d="M0 0h95v20H0z"/>
<path fill="#FF00FF" d="M41 0h93v20H41z"/>
<path fill="url(#b)" d="M0 0h134v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
<text x="20.5" y="14">Color</text>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="69.0" y="15" fill="#010101" fill-opacity=".3">fuchsia</text>
<text x="68.0" y="14">fuchsia</text>
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="78" height="20">
<svg xmlns="http://www.w3.org/2000/svg" width="118" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="anybadge_7">
<rect width="78" height="20" rx="3" fill="#fff"/>
<mask id="anybadge_49">
<rect width="118" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#anybadge_7)">
<g mask="url(#anybadge_49)">
<path fill="#555" d="M0 0h41v20H0z"/>
<path fill="#808080" d="M41 0h37v20H41z"/>
<path fill="url(#b)" d="M0 0h78v20H0z"/>
<path fill="#808080" d="M41 0h77v20H41z"/>
<path fill="url(#b)" d="M0 0h118v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
<text x="20.5" y="14">Color</text>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="60.5" y="15" fill="#010101" fill-opacity=".3">gray</text>
<text x="59.5" y="14">gray</text>
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="85" height="20">
<svg xmlns="http://www.w3.org/2000/svg" width="125" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="anybadge_8">
<rect width="85" height="20" rx="3" fill="#fff"/>
<mask id="anybadge_50">
<rect width="125" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#anybadge_8)">
<g mask="url(#anybadge_50)">
<path fill="#555" d="M0 0h41v20H0z"/>
<path fill="#4c1" d="M41 0h44v20H41z"/>
<path fill="url(#b)" d="M0 0h85v20H0z"/>
<path fill="#4c1" d="M41 0h84v20H41z"/>
<path fill="url(#b)" d="M0 0h125v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
<text x="20.5" y="14">Color</text>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="64.0" y="15" fill="#010101" fill-opacity=".3">green</text>
<text x="63.0" y="14">green</text>
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="76" height="20">
<svg xmlns="http://www.w3.org/2000/svg" width="116" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="anybadge_10">
<rect width="76" height="20" rx="3" fill="#fff"/>
<mask id="anybadge_52">
<rect width="116" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#anybadge_10)">
<g mask="url(#anybadge_52)">
<path fill="#555" d="M0 0h41v20H0z"/>
<path fill="#00FF00" d="M41 0h35v20H41z"/>
<path fill="url(#b)" d="M0 0h76v20H0z"/>
<path fill="#00FF00" d="M41 0h75v20H41z"/>
<path fill="url(#b)" d="M0 0h116v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
<text x="20.5" y="14">Color</text>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="59.5" y="15" fill="#010101" fill-opacity=".3">lime</text>
<text x="58.5" y="14">lime</text>
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="95" height="20">
<svg xmlns="http://www.w3.org/2000/svg" width="138" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="anybadge_11">
<rect width="95" height="20" rx="3" fill="#fff"/>
<mask id="anybadge_53">
<rect width="138" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#anybadge_11)">
<g mask="url(#anybadge_53)">
<path fill="#555" d="M0 0h41v20H0z"/>
<path fill="#800000" d="M41 0h54v20H41z"/>
<path fill="url(#b)" d="M0 0h95v20H0z"/>
<path fill="#800000" d="M41 0h97v20H41z"/>
<path fill="url(#b)" d="M0 0h138v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
<text x="20.5" y="14">Color</text>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="69.0" y="15" fill="#010101" fill-opacity=".3">maroon</text>
<text x="68.0" y="14">maroon</text>
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="79" height="20">
<svg xmlns="http://www.w3.org/2000/svg" width="115" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="anybadge_12">
<rect width="79" height="20" rx="3" fill="#fff"/>
<mask id="anybadge_54">
<rect width="115" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#anybadge_12)">
<g mask="url(#anybadge_54)">
<path fill="#555" d="M0 0h41v20H0z"/>
<path fill="#000080" d="M41 0h38v20H41z"/>
<path fill="url(#b)" d="M0 0h79v20H0z"/>
<path fill="#000080" d="M41 0h74v20H41z"/>
<path fill="url(#b)" d="M0 0h115v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
<text x="20.5" y="14">Color</text>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="61.0" y="15" fill="#010101" fill-opacity=".3">navy</text>
<text x="60.0" y="14">navy</text>
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="20">
<svg xmlns="http://www.w3.org/2000/svg" width="123" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="anybadge_13">
<rect width="80" height="20" rx="3" fill="#fff"/>
<mask id="anybadge_55">
<rect width="123" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#anybadge_13)">
<g mask="url(#anybadge_55)">
<path fill="#555" d="M0 0h41v20H0z"/>
<path fill="#808000" d="M41 0h39v20H41z"/>
<path fill="url(#b)" d="M0 0h80v20H0z"/>
<path fill="#808000" d="M41 0h82v20H41z"/>
<path fill="url(#b)" d="M0 0h123v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
<text x="20.5" y="14">Color</text>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="61.5" y="15" fill="#010101" fill-opacity=".3">olive</text>
<text x="60.5" y="14">olive</text>
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="92" height="20">
<svg xmlns="http://www.w3.org/2000/svg" width="135" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="anybadge_14">
<rect width="92" height="20" rx="3" fill="#fff"/>
<mask id="anybadge_56">
<rect width="135" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#anybadge_14)">
<g mask="url(#anybadge_56)">
<path fill="#555" d="M0 0h41v20H0z"/>
<path fill="#fe7d37" d="M41 0h51v20H41z"/>
<path fill="url(#b)" d="M0 0h92v20H0z"/>
<path fill="#fe7d37" d="M41 0h94v20H41z"/>
<path fill="url(#b)" d="M0 0h135v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
<text x="20.5" y="14">Color</text>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="67.5" y="15" fill="#010101" fill-opacity=".3">orange</text>
<text x="66.5" y="14">orange</text>
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="89" height="20">
<svg xmlns="http://www.w3.org/2000/svg" width="129" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="anybadge_15">
<rect width="89" height="20" rx="3" fill="#fff"/>
<mask id="anybadge_57">
<rect width="129" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#anybadge_15)">
<g mask="url(#anybadge_57)">
<path fill="#555" d="M0 0h41v20H0z"/>
<path fill="#800080" d="M41 0h48v20H41z"/>
<path fill="url(#b)" d="M0 0h89v20H0z"/>
<path fill="#800080" d="M41 0h88v20H41z"/>
<path fill="url(#b)" d="M0 0h129v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
<text x="20.5" y="14">Color</text>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="66.0" y="15" fill="#010101" fill-opacity=".3">purple</text>
<text x="65.0" y="14">purple</text>
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="71" height="20">
<svg xmlns="http://www.w3.org/2000/svg" width="108" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="anybadge_16">
<rect width="71" height="20" rx="3" fill="#fff"/>
<mask id="anybadge_58">
<rect width="108" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#anybadge_16)">
<g mask="url(#anybadge_58)">
<path fill="#555" d="M0 0h41v20H0z"/>
<path fill="#e05d44" d="M41 0h30v20H41z"/>
<path fill="url(#b)" d="M0 0h71v20H0z"/>
<path fill="#e05d44" d="M41 0h67v20H41z"/>
<path fill="url(#b)" d="M0 0h108v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
<text x="20.5" y="14">Color</text>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="57.0" y="15" fill="#010101" fill-opacity=".3">red</text>
<text x="56.0" y="14">red</text>
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="86" height="20">
<svg xmlns="http://www.w3.org/2000/svg" width="127" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="anybadge_17">
<rect width="86" height="20" rx="3" fill="#fff"/>
<mask id="anybadge_59">
<rect width="127" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#anybadge_17)">
<g mask="url(#anybadge_59)">
<path fill="#555" d="M0 0h41v20H0z"/>
<path fill="#C0C0C0" d="M41 0h45v20H41z"/>
<path fill="url(#b)" d="M0 0h86v20H0z"/>
<path fill="#C0C0C0" d="M41 0h86v20H41z"/>
<path fill="url(#b)" d="M0 0h127v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
<text x="20.5" y="14">Color</text>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="64.5" y="15" fill="#010101" fill-opacity=".3">silver</text>
<text x="63.5" y="14">silver</text>
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="74" height="20">
<svg xmlns="http://www.w3.org/2000/svg" width="115" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="anybadge_18">
<rect width="74" height="20" rx="3" fill="#fff"/>
<mask id="anybadge_60">
<rect width="115" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#anybadge_18)">
<g mask="url(#anybadge_60)">
<path fill="#555" d="M0 0h41v20H0z"/>
<path fill="#008080" d="M41 0h33v20H41z"/>
<path fill="url(#b)" d="M0 0h74v20H0z"/>
<path fill="#008080" d="M41 0h74v20H41z"/>
<path fill="url(#b)" d="M0 0h115v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
<text x="20.5" y="14">Color</text>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="58.5" y="15" fill="#010101" fill-opacity=".3">teal</text>
<text x="57.5" y="14">teal</text>
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="81" height="20">
<svg xmlns="http://www.w3.org/2000/svg" width="123" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="anybadge_19">
<rect width="81" height="20" rx="3" fill="#fff"/>
<mask id="anybadge_61">
<rect width="123" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#anybadge_19)">
<g mask="url(#anybadge_61)">
<path fill="#555" d="M0 0h41v20H0z"/>
<path fill="#FFFFFF" d="M41 0h40v20H41z"/>
<path fill="url(#b)" d="M0 0h81v20H0z"/>
<path fill="#FFFFFF" d="M41 0h82v20H41z"/>
<path fill="url(#b)" d="M0 0h123v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
<text x="20.5" y="14">Color</text>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="62.0" y="15" fill="#010101" fill-opacity=".3">white</text>
<text x="61.0" y="14">white</text>
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="87" height="20">
<svg xmlns="http://www.w3.org/2000/svg" width="135" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="anybadge_20">
<rect width="87" height="20" rx="3" fill="#fff"/>
<mask id="anybadge_62">
<rect width="135" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#anybadge_20)">
<g mask="url(#anybadge_62)">
<path fill="#555" d="M0 0h41v20H0z"/>
<path fill="#dfb317" d="M41 0h46v20H41z"/>
<path fill="url(#b)" d="M0 0h87v20H0z"/>
<path fill="#dfb317" d="M41 0h94v20H41z"/>
<path fill="url(#b)" d="M0 0h135v20H0z"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
<text x="20.5" y="14">Color</text>
</g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="65.0" y="15" fill="#010101" fill-opacity=".3">yellow</text>
<text x="64.0" y="14">yellow</text>
<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>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -20,18 +20,20 @@ setup(
version=version,
author='Jon Grace-Cox',
author_email='jongracecox@gmail.com',
py_modules=['anybadge', 'anybadge_server'],
packages=['anybadge'],
py_modules=['anybadge_server'],
setup_requires=['setuptools', 'wheel'],
tests_require=[],
install_requires=['packaging'],
data_files=[],
package_data={'anybadge': ['templates/*.svg']},
options={
'bdist_wheel': {'universal': True}
'bdist_wheel': {'universal': False}
},
python_requires='>=3.4',
url='https://github.com/jongracecox/anybadge',
entry_points={
'console_scripts': ['anybadge=anybadge:main',
'anybadge-server=anybadge_server:main'],
'console_scripts': ['anybadge=anybadge.cli:main',
'anybadge-server=anybadge.server.cli:main'],
},
classifiers=[
'License :: OSI Approved :: MIT License'

View File

@@ -1,5 +1,6 @@
from unittest import TestCase
from anybadge import Badge, parse_args, main
from anybadge import Badge
from anybadge.cli import main, parse_args
class TestAnybadge(TestCase):