Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Louis
pypimonitor
Commits
854e0378
Commit
854e0378
authored
Jul 15, 2018
by
Louis
Browse files
Format code using black
parent
c05d4f6f
Pipeline
#62748
passed with stages
in 1 minute and 16 seconds
Changes
17
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
.gitlab-ci.yml
View file @
854e0378
image
:
paternal/pythons
image
:
paternal/pythons
:buster
tox
:
script
:
...
...
.pre-commit-config.yaml
0 → 100644
View file @
854e0378
repos
:
-
repo
:
https://github.com/ambv/black
rev
:
stable
hooks
:
-
id
:
black
language_version
:
python3.6
doc/conf.py
View file @
854e0378
...
...
@@ -19,49 +19,49 @@ import os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys
.
path
.
insert
(
0
,
os
.
path
.
abspath
(
'
..
'
))
sys
.
path
.
insert
(
0
,
os
.
path
.
abspath
(
"
..
"
))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
#
needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions
=
[
'
sphinx.ext.autodoc
'
,
'
sphinx.ext.doctest
'
,
'
sphinx.ext.intersphinx
'
,
'
sphinx.ext.viewcode
'
,
'
sphinxarg.ext
'
,
"
sphinx.ext.autodoc
"
,
"
sphinx.ext.doctest
"
,
"
sphinx.ext.intersphinx
"
,
"
sphinx.ext.viewcode
"
,
"
sphinxarg.ext
"
,
]
# Add any paths that contain templates here, relative to this directory.
templates_path
=
[
'
_templates
'
]
templates_path
=
[
"
_templates
"
]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md']
source_suffix
=
'
.rst
'
source_suffix
=
"
.rst
"
# The encoding of source files.
#source_encoding = 'utf-8-sig'
#
source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc
=
'
index
'
master_doc
=
"
index
"
# General information about the project.
project
=
'
pypimonitor
'
copyright
=
'
2016, Louis Paternault
'
author
=
'
Louis Paternault
'
project
=
"
pypimonitor
"
copyright
=
"
2016, Louis Paternault
"
author
=
"
Louis Paternault
"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version
=
'
0.3.1
'
version
=
"
0.3.1
"
# The full version, including alpha/beta/rc tags.
release
=
version
...
...
@@ -74,201 +74,200 @@ language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
#
today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
#
today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns
=
[
'
_build
'
,
'
Thumbs.db
'
,
'
.DS_Store
'
]
exclude_patterns
=
[
"
_build
"
,
"
Thumbs.db
"
,
"
.DS_Store
"
]
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
#
default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
#
add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
#
add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
#
show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style
=
'
sphinx
'
pygments_style
=
"
sphinx
"
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
#
modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
#
keep_warnings = False
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos
=
False
# Autodoc options
autodoc_member_order
=
'
bysource
'
autodoc_member_order
=
"
bysource
"
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#html_theme = 'alabaster'
#
html_theme = 'alabaster'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
#
html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
#
html_theme_path = []
# The name for this set of Sphinx documents.
# "<project> v<release> documentation" by default.
#html_title = 'pypimonitor v0.2.0'
#
html_title = 'pypimonitor v0.2.0'
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
#
html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
#
html_logo = None
# The name of an image file (relative to this directory) to use as a favicon of
# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
#
html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path
=
[
'
_static
'
]
html_static_path
=
[
"
_static
"
]
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
#
html_extra_path = []
# If not None, a 'Last updated on:' timestamp is inserted at every page
# bottom, using the given strftime format.
# The empty string is equivalent to '%b %d, %Y'.
#html_last_updated_fmt = None
#
html_last_updated_fmt = None
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
#
html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
#
html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
#
html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
#
html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
#
html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
#
html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
#
html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
#
html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
#
html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
#
html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
#
html_file_suffix = None
# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh'
#html_search_language = 'en'
#
html_search_language = 'en'
# A dictionary with options for the search language support, empty by default.
# 'ja' uses this config value.
# 'zh' user can custom change `jieba` dictionary path.
#html_search_options = {'type': 'default'}
#
html_search_options = {'type': 'default'}
# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
#html_search_scorer = 'scorer.js'
#
html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder.
htmlhelp_basename
=
'
pypimonitordoc
'
htmlhelp_basename
=
"
pypimonitordoc
"
# -- Options for LaTeX output ---------------------------------------------
latex_elements
=
{
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
# Latex figure (float) alignment
#'figure_align': 'htbp',
# The paper size ('letterpaper' or 'a4paper').
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
# 'preamble': '',
# Latex figure (float) alignment
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents
=
[
(
master_doc
,
'pypimonitor.tex'
,
'PypiMonitor Documentation'
,
'Louis Paternault'
,
'manual'
),
(
master_doc
,
"pypimonitor.tex"
,
"PypiMonitor Documentation"
,
"Louis Paternault"
,
"manual"
,
)
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
#
latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
#
latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
#
latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
#
latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
#
latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
#
latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages
=
[
(
master_doc
,
'pypimonitor'
,
'PypiMonitor Documentation'
,
[
author
],
1
)
]
man_pages
=
[(
master_doc
,
"pypimonitor"
,
"PypiMonitor Documentation"
,
[
author
],
1
)]
# If true, show URL addresses after external links.
#man_show_urls = False
#
man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
...
...
@@ -277,23 +276,29 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents
=
[
(
master_doc
,
'pypimonitor'
,
'PypiMonitor Documentation'
,
author
,
'pypimonitor'
,
'One line description of project.'
,
'Miscellaneous'
),
(
master_doc
,
"pypimonitor"
,
"PypiMonitor Documentation"
,
author
,
"pypimonitor"
,
"One line description of project."
,
"Miscellaneous"
,
)
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
#
texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
#
texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
#
texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
#
texinfo_no_detailmenu = False
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping
=
{
'
https://docs.python.org/3
'
:
None
}
intersphinx_mapping
=
{
"
https://docs.python.org/3
"
:
None
}
pylintrc
View file @
854e0378
...
...
@@ -5,4 +5,7 @@ dummy-variables-rgx=_|dummy
ignored-modules = straight
[MESSAGES CONTROL]
disable= logging-format-interpolation, I
disable=
logging-format-interpolation,
# Let black handle formatting
bad-continuation,
pypimonitor/__init__.py
View file @
854e0378
...
...
@@ -40,23 +40,20 @@ from pypimonitor.cell.base import Error
VERSION
=
"0.3.1"
LOGGER
=
logging
.
getLogger
(
__name__
)
CLIENT
=
xmlrpclib
.
ServerProxy
(
'https://pypi.python.org/pypi'
)
CLIENT
=
xmlrpclib
.
ServerProxy
(
"https://pypi.python.org/pypi"
)
def
default_data
(
name
):
"""Return default dummy data for a given package.
It is used for packages that are not published on Pypi.
"""
return
{
"info"
:
{
"name"
:
name
,
},
"releases"
:
{},
"urls"
:
[],
}
return
{
"info"
:
{
"name"
:
name
},
"releases"
:
{},
"urls"
:
[]}
class
Counter
:
"""Each call to :meth:`Counter.count` increments a counter, and return its value."""
# pylint: disable=too-few-public-methods
def
__init__
(
self
):
...
...
@@ -67,6 +64,7 @@ class Counter:
self
.
value
+=
1
return
self
.
value
def
random_color
(
seed
,
saturation
,
value
):
"""Return a pseudo-random color."""
old_seed
=
random
.
random
()
...
...
@@ -75,11 +73,13 @@ def random_color(seed, saturation, value):
random
.
seed
(
old_seed
)
return
"#%02x%02x%02x"
%
tuple
([
int
(
value
*
255
)
for
value
in
rgb
])
def
iter_user_packages
(
user
):
"""Iterate over packages owned by a given user."""
for
_
,
package
in
CLIENT
.
user_packages
(
user
):
yield
package
class
Renderer
:
"""Get information about packages, and produce an HTML dashboard to monitor them."""
...
...
@@ -89,53 +89,60 @@ class Renderer:
# Processing config
self
.
config
=
config
if
"cells"
not
in
self
.
config
:
self
.
config
[
'cells'
]
=
list
(
set
(
itertools
.
chain
(
*
[
package
.
keys
()
for
package
in
self
.
config
[
'packages'
].
values
()
])))
for
package
in
self
.
config
[
'packages'
]:
if
self
.
config
[
'packages'
][
package
]
is
None
:
self
.
config
[
'packages'
][
package
]
=
{}
for
cell
in
self
.
config
[
'cells'
]:
if
self
.
config
[
'packages'
][
package
].
get
(
cell
,
None
)
is
None
:
self
.
config
[
'packages'
][
package
][
cell
]
=
{}
self
.
config
[
"cells"
]
=
list
(
set
(
itertools
.
chain
(
*
[
package
.
keys
()
for
package
in
self
.
config
[
"packages"
].
values
()
]
)
)
)
for
package
in
self
.
config
[
"packages"
]:
if
self
.
config
[
"packages"
][
package
]
is
None
:
self
.
config
[
"packages"
][
package
]
=
{}
for
cell
in
self
.
config
[
"cells"
]:
if
self
.
config
[
"packages"
][
package
].
get
(
cell
,
None
)
is
None
:
self
.
config
[
"packages"
][
package
][
cell
]
=
{}
# Set default values
for
key
in
self
.
config
.
get
(
'default'
,
{}):
if
key
not
in
self
.
config
[
'packages'
][
package
]:
self
.
config
[
'packages'
][
package
][
key
]
=
self
.
config
[
'default'
][
key
]
if
'cell'
not
in
self
.
config
[
'packages'
][
package
][
cell
]:
self
.
config
[
'packages'
][
package
][
cell
][
'cell'
]
=
cell
for
key
in
self
.
config
.
get
(
"default"
,
{}):
if
key
not
in
self
.
config
[
"packages"
][
package
]:
self
.
config
[
"packages"
][
package
][
key
]
=
self
.
config
[
"default"
][
key
]
if
"cell"
not
in
self
.
config
[
"packages"
][
package
][
cell
]:
self
.
config
[
"packages"
][
package
][
cell
][
"cell"
]
=
cell
# Processing packages
self
.
packages
=
{}
for
name
in
sorted
(
config
[
'
packages
'
]):
for
name
in
sorted
(
config
[
"
packages
"
]):
try
:
self
.
packages
[
name
]
=
Package
(
name
,
require_pypi
=
require_pypi
)
except
Exception
as
error
:
# pylint: disable=broad-except
except
Exception
as
error
:
# pylint: disable=broad-except
self
.
log_error
(
"Ignoring package '{}': {}"
.
format
(
name
,
error
))
continue
# Processing form
self
.
form
=
form
if
self
.
form
is
not
None
:
for
key
in
[
'pkg'
,
'user'
,
'col'
]:
self
.
form
[
key
]
=
","
.
join
(
set
(
itertools
.
chain
(
*
[
item
.
split
(
","
)
for
item
in
self
.
form
.
get
(
key
,
[])])
))
for
key
in
[
"pkg"
,
"user"
,
"col"
]:
self
.
form
[
key
]
=
","
.
join
(
set
(
itertools
.
chain
(
*
[
item
.
split
(
","
)
for
item
in
self
.
form
.
get
(
key
,
[])]
)
)
)
# Loading plugins
self
.
cells
=
{
plugin
.
keyword
:
plugin
(
self
)
for
plugin
in
load_cell_plugins
()
}
self
.
cells
[
'error'
]
=
Error
(
self
)
self
.
cells
=
{
plugin
.
keyword
:
plugin
(
self
)
for
plugin
in
load_cell_plugins
()}
self
.
cells
[
"error"
]
=
Error
(
self
)
@
classmethod
def
from_yaml
(
cls
,
filename
,
form
=
None
):
"""Factory to return a :class:`Renderer` from a yaml file."""
with
open
(
filename
,
'r'
)
as
stream
:
with
open
(
filename
,
"r"
)
as
stream
:
return
cls
(
yaml
.
safe_load
(
stream
.
read
()),
form
=
form
)
@
classmethod
...
...
@@ -147,31 +154,31 @@ class Renderer:
if
packages
is
None
:
packages
=
[]
packages
=
[
item
.
strip
()
for
item
in
itertools
.
chain
(
*
[
item
.
split
(
","
)
for
item
in
packages
])
]
item
.
strip
()
for
item
in
itertools
.
chain
(
*
[
item
.
split
(
","
)
for
item
in
packages
])
]
# Processing users
if
users
is
None
:
users
=
[]
users
=
[
item
.
strip
()
for
item
in
itertools
.
chain
(
*
[
item
.
split
(
","
)
for
item
in
users
])]
users
=
[
item
.
strip
()
for
item
in
itertools
.
chain
(
*
[
item
.
split
(
","
)
for
item
in
users
])
]
for
user
in
users
:
try
:
packages
.
extend
(
iter_user_packages
(
user
))
except
urllib
.
error
.
HTTPError
as
error
:
LOGGER
.
error
(
"Ignoring user '{}': {}"
.
format
(
user
,
str
(
error
),
))
LOGGER
.
error
(
"Ignoring user '{}': {}"
.
format
(
user
,
str
(
error
)))
config
[
'
packages
'
]
=
{
pkg
:
{}
for
pkg
in
set
(
packages
)}
config
[
"
packages
"
]
=
{
pkg
:
{}
for
pkg
in
set
(
packages
)}
# Processing cells
if
cells
is
None
:
cells
=
[]
cells
=
list
(
itertools
.
chain
(
*
[
item
.
split
(
","
)
for
item
in
cells
]))
if
cells
:
config
[
'
cells
'
]
=
cells
config
[
"
cells
"
]
=
cells
return
cls
(
config
,
form
=
form
,
require_pypi
=
True
)
...
...
@@ -189,27 +196,30 @@ class Renderer:
@
staticmethod
def
_get_environment
():
templatedir
=
os
.
path
.
join
(
pkg_resources
.
resource_filename
(
__name__
,
'data'
),
'templates'
,
)
pkg_resources
.
resource_filename
(
__name__
,
"data"
),
"templates"
)
env
=
jinja2
.
Environment
(
loader
=
jinja2
.
FileSystemLoader
(
templatedir
))
env
.
globals
[
'
counter
'
]
=
Counter
().
count
env
.
globals
[
'
now
'
]
=
datetime
.
datetime
.
now
()
env
.
filters
[
'
datetime
'
]
=
operator
.
methodcaller
(
'
strftime
'
,
"%Y-%m-%d %H:%M:%S"
)
env
.
filters
[
'
color
'
]
=
random_color
env
.
globals
[
"
counter
"
]
=
Counter
().
count
env
.
globals
[
"
now
"
]
=
datetime
.
datetime
.
now
()
env
.
filters
[
"
datetime
"
]
=
operator
.
methodcaller
(
"
strftime
"
,
"%Y-%m-%d %H:%M:%S"
)
env
.
filters
[
"
color
"
]
=
random_color
return
env
def
render
(
self
):
"""Return the HTML code."""
return
self
.
_get_environment
().
get_template
(
"index.html"
).
render
(
# pylint: disable=no-member
config
=
self
.
config
,
cells
=
self
.
cells
,
packages
=
self
.
packages
,
form
=
self
.
form
,
errors
=
self
.
errors
,
version
=
VERSION
,
return
(
self
.
_get_environment
()
.
get_template
(
"index.html"
)
.
render
(
# pylint: disable=no-member
config
=
self
.
config
,
cells
=
self
.
cells
,
packages
=
self
.
packages
,
form
=
self
.
form
,
errors
=
self
.
errors
,
version
=
VERSION
,
)
)
def
log_error
(
self
,
message
):
"""Log error, both to HTML page and console."""
...
...
@@ -219,40 +229,38 @@ class Renderer:
class
Package
:
"""Gather information about a single package."""
# pylint: disable=too-few-public-methods
def
__init__
(
self
,
name
,
require_pypi
=
False
):
LOGGER
.
info
(
"Retrieving information about pypi package '{}'...
\n
"
.
format
(
name
))
try
:
self
.
rawdata
=
json
.
loads
(
requests
.
get
(
"http://pypi.python.org/pypi/{}/json"
.
format
(
name
)
).
text
)
except
Exception
as
error
:
# pylint: disable=broad-except
requests
.
get
(
"http://pypi.python.org/pypi/{}/json"
.
format
(
name
)).
text
)