How to make pages in MkDocs's side menu expandable? - python

I tried searching for this, but was unable to get any sort of answer that would be pointing to a solution. Let me start with some background first i suppose.
My Goal:
I'm currently figuring out how to get a decent automated documentation created for a certain python script repository. Its a library, so not a 'project' per se. It also lives in it's own place, as it's not maintained by a standalone python installation, but a Python 2.7 implementation that comes with Autodesk Maya.
As such, there's a bunch of modules in the library, organized the way it makes sense for us, and all functions have a docstring that is written according to the reStructuredText (:param parameterName: Description, etc..).
I was looking for something that would scrub through the whole repository, and create a nice html documentation that would look neat and make it easy to search for existing functions, thinking it must exist, but alas, i was unable to find anything that would be that user friendly.
What i tried:
Sphinx, pdoc3 - both proven various levels of not-idealness.
What i decide to do:
The last i tried is mkdocs. It seems fairly simplistic, not only because it doesnt actually do anything but create a stub of a page that gets filled based on one .yaml config file and per-page .md files that the user has to supply.
Now FYI, im new to both yaml and md, as i never had to use either.
I have decided to create myself a wrapper (for one-button-to-make-docs function) that would basically run through my python library, creating a .md page per module, and filling it with the functions/classes and their docstrings. I would have to parse the docstrings myself, but that's not that big a deal.
This would basically turn mkdocs into a pretty decent automated documentation with no needed manual input.
Now... I do like the theme readthedocs that you can set for your documentation in the yaml config file, but this is where i get stuck.
My Issue
When i 'mkdocs serve' my site, i'd like anything that has a hierarchy in the side menu, to be collapsible, like in this example (note the collapse/expand icons on the left sidebar):
But what I'm getting is no collapsability:
It should be noted, i suppose, that in my case, the 'attr' section is one .md site, that has just a simple structure of a h3 header per function name, and some description under, before having a horizontal rule to divide from the next function.
Also, in the yaml config file for the mydocs structure, the structure is kept very simple:
site_name: My Repo
nav:
- Home: 'index.md'
- 'Modules':
- attr: 'pageA.md'
- pageB: 'pageB.md'
- pageC: 'pageC.md'
- About: 'about.md'
theme:
name: readthedocs
include_homepage_in_sidebar: false
highlightjs: true
hijs_languages:
- python
markdown_extensions:
- toc:
permalink: True
separator: "_"
So the question is - how would i go about making my sidebar have little expand/collapse icons? (namely the attr site at this point)
Is it doable? Is it doable through the yaml config file, or the .md files of each page?
And potentially, is there a better alternative to what im doing, that wouldn't require me to write my own docs-making one-button solution?
Thank you!

Perhaps, you just need to write it differently. This is how it works in my project:
nav:
- 'index.md'
- 'glossary.md'
- 'Subject1':
- 'dir/file1.md'
- 'dir/file2.md'
- 'Subject2':
- 'NestedSubject2_1':
- 'dir/file1.md'
- 'dir/file2.md'
- 'NestedSubject2_2':
- 'dir/file.md'
- 'dir/file2.md'
- 'dir/file3.md'
- 'dir/file4.md'
And this is the least I can show, sorry. That's the structure my project has.

Related

Splitting code into modules (conventions)

So I've been searching for a bit and couldn't find anything on Google or PEP discussing this.
I am doing a project with tkinter and I had a file, that is part of a project, that was only 200 lines of code (excluding all the commented out code). While the entire file was related to the GUI portion of the project, it felt a bit long and a bit broad to me.
I ended up splitting the file into 4 different files that each has its own portion of the GUI.
Basically, the directory looks like this:
project/
guiclasses/
statisticsframe.py
textframes.py
windowclass.py
main_gui.py
...
statisticsframe has a class of a frame that shows statistics about stuff.
textframes holds 3 classes of frames holding textareas, one of them inherits Frame, the others inherit the first one.
windowclass basically creates the root of the window and all the general initialization for a tkinter GUI.
main_gui isn't actually the name but it simply combines all the above three and runs the mainloop()
Overall, each file is now 40-60 lines of code.
I am wondering if there are any conventions regarding this. The rule of thumb in most languages is that if you can reuse the functions/ classes elsewhere then you should split, though in Python it is less of a problem since you can import specific classes and functions from modules.
Sorry if it isn't coherent enough, nearly 3AM here and it is simply sitting in the back of my head.
I'm not familiar with tkinter, so my advice would be rather broad.
You can use any split into modules which you feel is better, but
as readability counts try making names coherent and do not repeat yourself: guiclasses - your enire progarm is about GUI, and there obviously classes somewhere, why repeath that in a name? imagine typing all that in in import, make it meaningful to type
flat structure is better than nested, three modules do not have to go to submodule
best split is across layers of abstraction (this is probably hardest and specific to tkinter)
anything in a module shoudl be rather self-sufficient and quite isolated from other parts of the program
modules should make good entitites for unit testing (eg share same fixtures)
can you write an understandable docstring for a module? then it's a good one.
try learning by example, I often seek wisdom for naming and package structure in Barry Warsaw mailman, maybe you can try finding some reputable repo with tkinter to follow (eg IDLE?).
From purely syntatic view I would have named the modules as:
- <package_name>
- baseframe
- textframe
- window
- main

Execution Code Tracking - How to know which code has been executed in project?

Let say that I have open source project from which I would like to borrow some functionality. Can I get some sort of report generated during execution and/or interaction of this project?
Report should contain e.g.:
which functions has been called,
in which order,
which classes has been instantiated etc.?
Would be nice to have some graphic output for that... you know, if else tree and highlighted the executed branch etc.
I am mostly interested in python and C (perl would be fine too) but if there is any universal tool that cover multiple languages (or one tool per language) for that, it would be very nice.
PS: I am familiar with debuggers but I do not want to step every singe line of code and check if this is the correct instruction. I'm assuming that if functions/methods/classes etc. are properly named then one can get some hints about where to find desired piece of code. But only naming is not enough because you do not know (from brief overview of code) if hopefully looking function foo() does not require some data that was generated by obscure function bar() etc. For that reason I am looking for something that can visualize relations between running code.
PS: Do not know if this is question for SO or programmers.stackexchange. Feel free to move if you wish. PS: I've noticed that tags that I've used are not recommended but execution flow tracking is the best phrase to describe this process
Check out Ned Batchelder's coverage and perhaps the graphviz/dot library called pycallgraph. May not be exactly what you need and also (python-only) but in the ballpark.
Pycallgraph is actually likelier to be of interest because it shows the execution path, not just what codelines got executed. It only renders to PDF normally, but it wasn't too difficult to get it to do SVG instead (dot/graphviz supports svg and other formats, pycallgraph was hardcoding pdf rendering).
Neither will do exactly what you want but they are a start.

Navigating a big Python codebase faster

As programmers we read more than we write. I've started working at a company that uses a couple of "big" Python packages; packages or package-families that have a high KLOC. Case in point: Zope.
My problem is that I have trouble navigating this codebase fast/easily. My current strategy is
I start reading a module I need to change/understand
I hit an import which I need to know more of
I find out where the source code for that import is by placing a Python debug (pdb) statement after the imports and echoing the module, which tells me it's source file
I navigate to it, in shell or the Vim file explorer.
most of the time the module itself imports more modules and before I know it I've got 10KLOC "on my plate"
Alternatively:
I see a method/class I need to know more of
I do a search (ack-grep) for the definition of that method/class across the whole codebase (which can be a pain because the codebase is partly in ~/.buildout-eggs)
I find one or more pieces of code that define that method/class
I have to deduce which one of them is the one I need to read
This costs a lot of time, which is understandable for a big codebase. But I get the feeling that navigating a large and unknown Python codebase is a common enough problem.
So I'm looking for technical tools or strategic solutions for this problem.
...
I just can't imagine hardcore Python programmers using the strategies outlined above.
on Vim, I like NERDTree (a file browser) and taglist.vim (source code browser --> http://www.vim.org/scripts/script.php?script_id=273)
also in Vim, you can use CTRL-] to jump to a definition (:h CTRL-]):
download exuberant ctags http://ctags.sourceforge.net/
follow the install directions and put it somewhere on your PATH
from the 'root' directory of your source code, make a tags file from the shell: "ctags -R"
(make sure you have :set noautochdir, and make sure :pwd is the root directory from step 3)
go into Vim, cursor over some function or class name, hit CTRL-]
by default, if there's multiple matches for the tag, it shows you everywhere it was imported, and where it was declared
if the tag only has one match, it immediately jumps to it
...then use Ctrl+O and Ctrl+I to move back and forth from where you were
(repeat above steps for the source code of particular libraries you use, i usually keep a separate Vim window open to study stuff)
I use ipython's ?? command
You just need to figure out how to import the things you want to look for, then add ?? to the end of the module or class or function or method name to view their source code. And the command completion helps on figuring out long names as well.
Try red pill: https://github.com/klen/python-mode

Using cscope to browse Python code with VIM?

Has anyone managed successfully using cscope with Python code? I have VIM 7.2 and the latest version of cscope installed, however it doesn't get my code's tags correctly (always off by a couple of lines). I tried the pycscope script but its output isn't supported by the modern version of cscope.
Any ideas? Or an alternative for browsing Python code with VIM? (I'm specifically interested in the extra features cscope offers beyond the simple tags of ctags)
EDIT: I'm going to run through the process step by step:
Preparing the sources:
exhuberant ctags, has an option: -x
Alternatively, ctags can generate a cross reference file which lists,
in human readable form, information about the various source objects
found in a set of language files.
This is the key to the problem:
ctags -x $(ls **/*.py); # replace with find if no zsh
will give you your database of source objects in a known, format, described under
man ctags; # make sure you use exuberant ctags!
Gnu Global is not limited to only the "out of the box" type of files. Any regular file format will serve.
Also, you can use gtags-cscope, which comes with global as mentioned in section 3.7 of the manual, for a possible shortcut using gtags. You'll end up with an input of a ctags tabular file which Global/gtags can parse to get your objects, or you can use the source for pycscope together with your ctags file of known format to get an input for the vim cscope commands in
if_cscope.txt.
Either way it's quite doable.
Perhaps you'd prefer idutils?
Definintely possible since
z3c.recipe.tags
on pypi makes use of both ctags and idutils to create tag files for a buildout, which is a method I shall investigate in short while.
Of course, you could always use the greputils script below, it has support for idutils , we know idutils works with python, and if that fails, there is also something called vimentry from this year that also uses python, idutils and vim.
Reference links (not complete list):
gtags vimscript, uses Gnu global. updated 2008
greputils vimscript, contains support for the *id idutils, 2005
lid vimscript, Ancient, but this guy is pretty good, his tag and buffer howtos are amazing 2002
An updated version of pyscope, 2010
Hopefully this helps you with your problem, I certainly helped me. I would have been quite sad tonight with a maggoty pycscope.
This seems to work for me:
Change to the top directory of your python code. Create a file called cscope.files:
find . -name '*.py' > cscope.files
cscope -R
You may need to perform a cscope -b first if the cross references don't get built properly.
From a correspondence with the maintainer of cscope, this tool isn't designed to work with Python, and there are no plans to implement that compatibility. Whatever works now, apparently works by mistake, and there is no promise whatsoever that it will keep working.
It appears I've been using an out-of-date version of pycscope. The latest version 0.3 is supported by the cscope DB. The author of pycscope told me that he figured out the output format for the cscope DB from reading the source code of cscope. That format isn't documented, on purpose, but nevertheless it currently works with pycsope 0.3, which is the solution I'll be using.
I'm going to accept this answer since unfortunately no other answer provided help even after bounty was declared. No answers are upvoted, so I honestly have no idea where the bounty will go.
There is a wonderful Python-mode-klen plugin. If you have it and rope (python refactoring library) installed, then going to the definition of a particular term is as simple as <C-c>g or <C-c>rag (first is filetype mapping, second is a global one). There are much more useful features, some useless for me. All of them are disableable. Features from list of questions found at cscope-intro:
Where is this symbol used? <C-c>f. Rather confusing though, as results in quickfix list do show - instead of the actual lines (though they point to the correct location). Maybe it will be fixed.
Where is it defined?, What is this global symbol's definition?, Where is this function in the source files? <C-c>g
What is <...> global symbol's definition? <C-c>raj
Not very much, but I am not too experienced user of ropevim.
I got the same question you got, after browsing the internet, I found a way to fix this:
create a python script: cscope_scan.py
import os
codeRootDir = os.getcwd()
__revision__ = '0.1'
__author__ = 'lxd'
FILE_TYPE_LIST= ['py']
if __name__ == '__main__':
import os
f = open('cscope.files','w')
for root,dirs,files in os.walk(codeRootDir):
for file in files:
for file_type in FILE_TYPE_LIST:
if file.split('.')[-1] == file_type:
f.write('%s\n' %os.path.join(root,file))
f.close()
cmd = 'cscope -bk'
os.system(cmd)
excute this script under you code's root folder, this will generate the cscope.files and then excute cscope -b I don't know what happens to my computer, the last two lines aren't working well, but I think manually type a cscope -bk is acceptable:)
This hack also seems to force cscope to go through Python files:
cscope -Rb -s *
If you accept that cscope is apparently not designed to work with Python.
Superset any language any tool question: How to find all occurrences of a variable in Vim?

Is it ever polite to put code in a python configuration file?

One of my favorite features about python is that you can write configuration files in python that are very simple to read and understand. If you put a few boundaries on yourself, you can be pretty confident that non-pythonistas will know exactly what you mean and will be perfectly capable of reconfiguring your program.
My question is, what exactly are those boundaries? My own personal heuristic was
Avoid flow control. No functions, loops, or conditionals. Those wouldn't be in a text config file and people aren't expecting to have understand them. In general, it probably shouldn't matter the order in which your statements execute.
Stick to literal assignments. Methods and functions called on objects are harder to think through. Anything implicit is going to be a mess. If there's something complicated that has to happen with your parameters, change how they're interpreted.
Language keywords and error handling are right out.
I guess I ask this because I came across a situation with my Django config file where it seems to be useful to break these rules. I happen to like it, but I feel a little guilty. Basically, my project is deployed through svn checkouts to a couple different servers that won't all be configured the same (some will share a database, some won't, for example). So, I throw a hook at the end:
try:
from settings_overrides import *
LOCALIZED = True
except ImportError:
LOCALIZED = False
where settings_overrides is on the python path but outside the working copy. What do you think, either about this example, or about python config boundaries in general?
There is a Django wiki page, which addresses exactly the thing you're asking.
http://code.djangoproject.com/wiki/SplitSettings
Do not reinvent the wheel. Use configparser and INI files. Python files are to easy to break by someone, who doesn't know Python.
Your heuristics are good. Rules are made so that boundaries are set and only broken when it's obviously a vastly better solution than the alternate.
Still, I can't help but wonder that the site checking code should be in the parser, and an additional configuration item added that selects which option should be taken.
I don't think that in this case the alternative is so bad that breaking the rules makes sense...
-Adam
I think it's a pain vs pleasure argument.
It's not wrong to put code in a Python config file because it's all valid Python, but it does mean you could confuse a user who comes in to reconfigure an app. If you're that worried about it, rope it off with comments explaining roughly what it does and that the user shouldn't edit it, rather edit the settings_overrides.py file.
As for your example, that's nigh on essential for developers to test then deploy their apps. Definitely more pleasure than pain. But you should really do this instead:
LOCALIZED = False
try:
from settings_overrides import *
except ImportError:
pass
And in your settings_overrides.py file:
LOCALIZED = True
... If nothing but to make it clear what that file does.. What you're doing there splits overrides into two places.
As a general practice, see the other answers on the page; it all depends. Specifically for Django, however, I see nothing fundamentally wrong with writing code in the settings.py file... after all, the settings file IS code :-)
The Django docs on settings themselves say:
A settings file is just a Python module with module-level variables.
And give the example:
assign settings dynamically using normal Python syntax. For example:
MY_SETTING = [str(i) for i in range(30)]
Settings as code is also a security risk. You import your "config", but in reality you are executing whatever code is in that file. Put config in files that you parse first and you can reject nonsensical or malicious values, even if it is more work for you. I blogged about this in December 2008.

Categories