Why does the "package" has a "package" in the Doxygen tree? - python

I generate the documentation of my Python code from the docstrings via Doxygen (1.9.1) in addition with doxypypy (git version from today).
My project is called Project and the packages name in it (which should be imported) is mypackage. When I look into the tree sidebar of the generated html it looks like this:
└── Project
   └── Packages
      └── Packages
└──mypackages
The two packages are linking to the same target: ../html/namespaces.html.
The files and folders are structured like this
Project
├── LICENSE
├── README.md
├── docs
└── ...
└── src
├── mypackage
│   ├── a.py
│   ├── __init__.py
│   └── _mypackage.py
├── setup.cfg
└── setup.py
The Doxyfile is located in Project/docs, doxygen is run in there and use ../src/mypackage as INPUT directory.
More details
__init__.py
__version__ = '0.0.1a'
from ._mypackage import *
_mypackage.py
# -*- coding: utf-8 -*-
"""Example __init__.py short.
Now some longer with multiple lines. Here
comes the scond line.
"""
def foo(bar):
"""
This is foo() in mypackage.
Args:
bar (str): A paramenter.
Returns:
(int): Fixed seven.
"""
print(bar)
return 7
a.py
# -*- coding: utf-8 -*-
"""This is mypackage.a
"""
import mypackage
def bar(bar):
"""
This is the function named bar.
The function calls `mypackage.foo()` and returns an 'a'.
Paramters:
bar (str): Just a parameter.
Returns:
str: Just an 'a'.
"""
mypackage.foo(bar)
return('a')
Some (maybe) related Doxyfile settings
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ALWAYS_DETAILED_SEC = NO
FULL_PATH_NAMES = YES
JAVADOC_AUTOBRIEF = NO
PYTHON_DOCSTRING = YES
OPTIMIZE_OUTPUT_FOR_C = NO
OPTIMIZE_OUTPUT_JAVA = YES
OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
OPTIMIZE_OUTPUT_SLICE = NO
MARKDOWN_SUPPORT = YES
EXTRACT_ALL = YES
EXTRACT_PRIVATE = YES
EXTRACT_PACKAGE = YES
EXTRACT_STATIC = YES
EXTRACT_LOCAL_CLASSES = YES
EXTRACT_LOCAL_METHODS = YES
EXTRACT_ANON_NSPACES = NO
RESOLVE_UNNAMED_PARAMS = YES
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INPUT = ../src/mypackage
FILE_PATTERNS =
RECURSIVE = YES
FILTER_PATTERNS = *.py=./py_filter
GENERATE_HTML = YES
GENERATE_TREEVIEW = YES
I opened an Issue about that.

Related

Building Hierarchy Graph from Strings

I am trying to build a hierarchy graph from a list of strings I have. Each string just consists of its absolute hierarchy seperated by dots. Example Strings:
memberA.memberB.memberC
memberA.memberE.memberG
memberA.memberE
memberA.memberB
memberA.memberF.memberX
memberA.memberF
memberA.memberF.memberG #in this case this should be treated as a seperate leaf node and not the same as in memberA.memberE.memberG
I tried using Anytree and Treelib to achieve this but I could not come up with a working solution. Although this problem looks simple (might not be) I just can not figure it out.
You'd need to keep track of which node objects correspond to a certain path. For that you can use a dictionary, that maps a given path to a node object.
With AnyTree it could look like this:
from anytree import Node, RenderTree
strings = [
"memberA.memberB.memberC",
"memberA.memberE.memberG",
"memberA.memberE",
"memberA.memberB",
"memberA.memberF.memberX",
"memberA.memberF",
"memberA.memberF.memberG"
]
d = {}
root = Node("root")
for s in strings:
path = "root"
parent = root
for name in s.split("."):
path += "." + name
if path not in d:
d[path] = Node(name, parent=parent)
parent = d[path]
print(RenderTree(root))
Output:
Node('/root')
└── Node('/root/memberA')
├── Node('/root/memberA/memberB')
│ └── Node('/root/memberA/memberB/memberC')
├── Node('/root/memberA/memberE')
│ └── Node('/root/memberA/memberE/memberG')
└── Node('/root/memberA/memberF')
├── Node('/root/memberA/memberF/memberX')
└── Node('/root/memberA/memberF/memberG')
In case you want "memberA" to be the root, then you need to make sure your input data only has strings that start with "memberA". And then at the end of the above script do:
root = root.children[0]
root.parent = None
print(RenderTree(root))
Output:
Node('/memberA')
├── Node('/memberA/memberB')
│ └── Node('/memberA/memberB/memberC')
├── Node('/memberA/memberE')
│ └── Node('/memberA/memberE/memberG')
└── Node('/memberA/memberF')
├── Node('/memberA/memberF/memberX')
└── Node('/memberA/memberF/memberG')

Using inventory group variables in dynamic inventory script

This question is exactly like this stackoverflow question. And I am hoping I get some different answer with this one. I have been trying to achieve this for over a year. but can't seem to achieve it.
Stripped down version of my ansible directory looks like this:
[root#python-test ansible]# tree
.
├── files
├── inventory
│   ├── prod
│   │   ├── group_vars
│   │   │   └── all
│   │   └── hosts -> ../../scripts/inventory.py
│   └── staging
│   ├── group_vars
│   │   └── all
│   └── hosts -> ../../scripts/inventory.py
├── roles
│   ├── ant
│   ├── build
│   ├── jdk
│   └── python
├── scripts
│   └── inventory.py
├── templates
└── vars
└── all.yaml
I would like to use some of the variables declared in group_vars/all file. It has some endpoint details that I can use during the script execution. A striped down version of this group_vars/all looks like this:
inv_cloudprovider: aws
inv_environment: prod
inv_environment_type: production
inv_vpc_cidr: 10.0.0.0/16
inv_build_url: 'https://build.{{inv_environment}}.local'
inv_cloud_vpc_name: '{{inv_environment}}-vpc'
inv_vpc_id: '{{inv_environment_type}}-{{inv_cloud_vpc_name}}'
inv_glassfish_version: 4
At this point I am loading this file using yaml.safe_loads() and then using them in script execution. but problem with that is there are variables which are recursive jinja templates. and I am having hard time getting to make those variables work. So I was wondering if I can use ansible to do this for me.
I am using ansible==2.2.3.0 and python 2.7.
The closest I have been to achieving this in python by doing this:
from ansible.parsing.dataloader import DataLoader
from ansible.vars import VariableManager
from ansible.inventory import Inventory
inventory_path = '/root/ansible/inventory/prod/hosts'
inventory = Inventory(DataLoader(), VariableManager(), inventory_path)
group = inventory.get_group('all')
group_vars = group.get_vars()
Which is probably not the correct way because my script tries to execute itself and that goes on recursively.
Is it possible to parse group vars variable using ansible? If no, how do I best get final value of variables from that file?
The stripped down version of code that can do what this question asked is:
#!/usr/bin/env python
import json
import os
import sys
import yaml
from jinja2 import Environment
class VarLoader(yaml.SafeLoader):
#staticmethod
def construct_python_string(text):
return str(text.value)
def yaml_remove_parse(self, text):
self.construct_python_string(text)
env_name = os.environ.get('ENV_NAME')
script_path = os.path.dirname(os.path.abspath(__file__))
ansible_dir_path = os.path.abspath(script_path + '/../')
global_vars_path = ansible_dir_path + '/vars/all.yaml'
inventory_vars_path = ansible_dir_path + '/inventory/' + env_name + '/group_vars/all'
with open(global_vars_path) as f1, open(inventory_vars_path) as f2:
global_vars_data = f1.read()
inventory_vars_data = f2.read()
all_vars_data = global_vars_data + '\n' + inventory_vars_data
VarLoader.add_constructor('tag:yaml.org,2002:float', VarLoader.yaml_remove_parse)
data = yaml.load(all_vars_data, Loader=VarLoader)
jinja_env = Environment()
template_file = jinja_env.from_string(all_vars_data)
for key in data:
val = data[key]
if isinstance(val, str) and '{{' in val:
template = jinja_env.from_string(val)
data[key] = template.render(**data)
yaml_data = yaml.load(template_file.render(data), Loader=VarLoader)
print(json.dumps(yaml_data))
This works with both python3 and python2. Output:
[root#python-test ansible]# python scripts/inventory.py | jq .
{
"inv_environment": "prod",
"inv_environment_type": "production",
"inv_cloudprovider": "aws",
"gv_redis_port": 6379,
"inv_glassfish_version": 4,
"inv_vpc_id": "production-prod-vpc",
"gv_redis_version": "4.0.11",
"inv_build_url": "https://build.prod.local",
"gv_glassfish_admin_user": "admin",
"inv_vpc_cidr": "10.0.0.0/16",
"gv_glassfish_asadmin_path": "/usr/local/glassfish/bin/asadmin",
"gv_java_home": "/usr/local/java/default",
"inv_cloud_vpc_name": "prod-vpc",
"gv_tomcat_home": "/usr/local/tomcat",
"gv_java_path": "/usr/local/java/default/bin/java"
}

How do I pass in a self argument to python cProfile

I am trying to use cProfiling with python.
My python project has the following directory structure:
my-project
├── src
│ ├── lib
│ └── app
│ └── data
│ └── car_sim.py
│
│
│
│
├── ptests
│ ├── src
│ └── lib
│ └── app
│ └── data
│ └── cprofile_test.py
I have a function inside car_sim.py that I want to cprofile and it is called "sim_text". It contains a function called:
#car_sim.py
import os
class RootSimulator:
def sim_text(self, text):
return text
I use the following code inside cprofile_test.py:
#cprofile_test.py
import cProfile
import pstats
import io
import src.lib.app.data.car_sim as car_sim_functions
pr = cProfile.Profile()
pr.enable()
text = 'my blabla sentence' #i can pass in this text below i guess...
#how do i pass to the below????!!
my_result = car_sim_functions.RootSimulator.sim_text()
pr.disable()
s = io.StringIO()
ps = pstats.Stats(pr, stream=s).sort_stats('tottime')
ps.print_stats()
with open('test.txt', 'w+') as f:
f.write(s.getvalue())
Now... when I run it using the command
python -m cProfile ptests/src/lib/app/data/cprofile_test.py
I get the following error:
TypeError: sim_text() missing 2 required positional arguments: 'self' and 'text'
My question is... It expects 2 args, so how do I pass in the "self" arg. For the 2nd arg, "text" I can pass in a value no problem.
class RootSimulator:
def sim_text(self, text):
return text
Defines an instance method on instances of RootSimulator. You are trying to call sim_text from the class itself. You need to create an instance:
simulator = car_sim_functions.RootSimulator()
my_result = simulator.sim_text()
If sim_text() does not actually need to be attached to an instance of the simulator, perhaps you don't need a class at all (just make it a plain function), or you could make it a static method:
class RootSimulator:
#staticmethod
def sim_text(text):
return text
Note that it doesn't need self anymore.

Bazel: Reading a file with relative path to package, not workspace

Suppose we have a project like this:
project-path
├── root
│   ├── BUILD
│   ├── gen
│   │   ├── a2.txt
│   │   └── a.txt
│   └── use.py
└── WORKSPACE
And in use.py:
f = open("gen/a.txt", "r")
f2 = open("gen/a2.txt", "r")
print(f.read())
print(f2.read())
And BUILD:
py_binary(
name = "use",
srcs = ["use.py"],
data = ["gen/a.txt", "gen/a2.txt"],
)
when I bazel run root:use, it errors:
FileNotFoundError: [Errno 2] No such file or directory: 'gen/a.txt'
It expects paths relative to the WORKSPACE directory, not the current package (root/gen/a.txt here). But I want to access files relative to each package.
Add Skylib to your project, i.e. extend you WORKSPACE file:
WORKSPACE
load("#bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "bazel_skylib",
sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
],
)
load("#bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
bazel_skylib_workspace()
Replace the path in the bash script using text replacement:
load("#bazel_skylib//rules:expand_template.bzl", "expand_template")
expand_template(
name = "modifiy_use_for_bazel",
out = "prepared_for_bazel_use.py",
substitutions = {
"gen/a.txt": "root/gen/a.txt",
"gen/a2.txt": "root/gen/a.txt",
},
template = "use.py",
)
py_binary(
name = "use",
main = "prepared_for_bazel_use.py",
srcs = ["prepared_for_bazel_use.py"],
data = [
"gen/a.txt",
"gen/a2.txt",
],
)
You can run now the script via bazel run root:use
Note: Tested with Bazel 6.0.0
To make this all a bit more convenient to use you could implement your own rule for it since this seems to be a common problem (e.g. when supporting two build systems at the same time where Bazel is not the primary build system and the other build system can cope with relative path names.)
A similar problem is described here: Change test execution directory in Bazel?

Sphinx not removing doctest flags in html output

I cannot eliminate the doctest flags (ie. <BLANKLINE>, # doctest: +ELLIPSIS) for the html output. I am able to generate the documentation as I would like, so no errors there but it includes theses flags which I would like removed. Sphinx documentation here claims this is possible so I must be doing something wrong. My documentation examples are in numpy style and I have tried using both the napoleon and numpydoc extensions.
Here are the steps I have taken.
run sphinx-quickstart (enabling autodoc and doctest extensions)
run sphinx-apidoc to generate .rst files
run make doctest (all tests are passing)
run make html
I have tried the setting trim_doctest_flags and doctest_test_doctest_blocks variables in conf.py with no success.
Is there something I am missing to trigger sphinx to remove these for the html docs? I am hoping this is enough information to get pointed in the right direction since the docs look good except for this one issue. However, I can provide more details or an example if necessary.
Update: MCV Example (Using Sphinx 1.8.2)
directory and file structure
.
├── trial
│   ├── __init__.py
│   └── trial.py
└── trialDocs
├── build
├── Makefile
└── source
├── _static
├── _templates
├── conf.py
├── index.rst
├── modules.rst
└── trial.rst
conf.py
# -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
import os
import sys
sys.path.insert(0, os.path.abspath('../../trial'))
project = 'trial'
copyright = '2019, trial'
author = 'trial'
version = ''
release = 'trial'
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.doctest',
'sphinx.ext.napoleon',
]
templates_path = ['_templates']
source_suffix = '.rst'
master_doc = 'index'
language = None
exclude_patterns = []
pygments_style = None
html_theme = 'alabaster'
htmlhelp_basename = 'trialdoc'
latex_elements = {}
latex_documents = [(master_doc, 'trial.tex', 'trial Documentation', 'trial', 'manual'),]
man_pages = [(master_doc, 'trial', 'trial Documentation', [author], 1)]
texinfo_documents = [(master_doc, 'trial', 'trial Documentation', author, 'trial', 'One line description of project.', 'Miscellaneous'),]
epub_title = project
epub_exclude_files = ['search.html']
doctest_global_setup = """
from trial import *
"""
trim_doctest_flags=True
trial.rst - this was generated using sphinx-apidoc
trial package
=============
Module contents
---------------
.. automodule:: trial
:members:
:undoc-members:
:show-inheritance:
trial.py
def withBlankline():
"""
Use blanklines in example.
Determine if sphinx will eliminate <BLANKLINE> for html.
Examples
--------
>>> withBlankline()
<BLANKLINE>
blanklines above and below
<BLANKLINE>
"""
print()
print('blanklines above and below')
print()
class Example():
def __init__(self):
pass
def withEllipsis(self):
"""
Use ellipsis in example.
Determine if sphinx will eliminate # doctest: +ELLIPSIS for html.
Examples
--------
>>> e = Example()
>>> e.withEllipsis() # doctest: +ELLIPSIS
abc...xyz
"""
print('abcdefghijklmnopqrstuvwxyz')
using make html or sphinx-build -b html source build
trial.html output:
Based on the comment by #mzjn, it appears that this was a bug, fixed in Sphinx 2.2.0:
Issue: doctest comments not getting trimmed since Sphinx 1.8.0 - Issue #6545

Categories

Resources