Skip to content

Commit 939e175

Browse files
authored
Rewrite for sc (#24)
* Treat files properly * Move python scripts to dedicated directory * Move jinja templates to dedicated directory * Remove old script * Modularize relative import in python script * Update CMake and run python as module * Factor out file opening * Tidy up old comments and dead code * Factor out hardcoded default doc data * Move lower-casing into templates, in prep for SC Temporarily comment out fft sub params that were being missed before, so that interim tests passed * Fix RST errors * Add draft docutils writer for rst 2 scdoc * Some tidying and commenting, ahead of substantial refactor * Refactor for modularity * Improe logging and reporting * Add some doc and tidy a bit * move scdoc docutils writer * rename scdoc writer * Add scdoc generation still todo: * weird cases with multiple inputs like NMFMorph and AudioTransport * cleverer categorisation * Fix control lookup and eliminate some duplication in validation code * Enable per-host defaults and fix lookup problems. Also prettify log messages * Add requirements.txt * tidy drivers and defaults a bit * add SC example code files harvested from *current* help files (script also committed) * include sc example code from external files in rendered template * minor tidying to SC child templates * write warning log to file and add option to surpress console output * housekeeping * enable testing with CTest * fix missing arg 'type' in merge, add test * test merging, fix validation tests * Update README * Add some very rough code documentation * update gitignore * Robustly handle case where message's `args` block is completely missing * correct templates folder * fix whacky indentation * scdoc template: setters don't need explicit handling * Don't die if a driver passes defaults as None * Correct type for data in CLI namer * get rid of double dots in topic file extentions * Stop SC being sad if RELATED is empty * rst to scdoc: render section headings and properly separate paragraphs * Don't generate rst in undocumented argument names * Properly escape plural in mlprgressor doc
1 parent d38df4e commit 939e175

File tree

182 files changed

+8799
-531
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

182 files changed

+8799
-531
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ sphinxout
88
doctrees
99
schelp
1010
*.pyc
11+
interfaces/*
12+
*.log

CMakeLists.txt

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ set(FLUID_PATH "" CACHE PATH "The top level of the flucoma-core repo. Will clone
2626
set(MAX_DOC_OUT "${CMAKE_BINARY_DIR}/max_ref" CACHE PATH "")
2727
set(PD_DOC_OUT "${CMAKE_BINARY_DIR}/pd_ref" CACHE PATH "")
2828
set(CLI_DOC_OUT "${CMAKE_BINARY_DIR}/cli_ref" CACHE PATH "")
29-
29+
set(SC_DOC_OUT "${CMAKE_BINARY_DIR}/sc_ref" CACHE PATH "")
30+
# option(TESTS,"Run tests?")
3031
include(FetchContent)
3132

3233
set(FETCHCONTENT_QUIET FALSE)
@@ -82,7 +83,7 @@ endif()
8283

8384
set(FLUID_JSON_DOC "${CMAKE_BINARY_DIR}/json")
8485
set(FLUID_YAML_DOC "${CMAKE_CURRENT_SOURCE_DIR}/doc")
85-
set(FLIUD_JINJA_TEMPLATES "${CMAKE_CURRENT_SOURCE_DIR}/script/templates")
86+
set(FLIUD_JINJA_TEMPLATES "${CMAKE_CURRENT_SOURCE_DIR}/flucoma/doc/templates")
8687

8788
# Make sure to build targets from flucoma-core
8889
macro(subdirlist result curdir)
@@ -125,14 +126,36 @@ macro(add_ref_target platform comment)
125126

126127
add_custom_target(MAKE_${platform_uc}_REF
127128
COMMAND ${CMAKE_COMMAND} -E make_directory "${${platform_uc}_DOC_OUT}"
128-
COMMAND ${PYTHON_3} "${CMAKE_CURRENT_SOURCE_DIR}/script/MakeRef.py" "${platform_lc}" ${FLUID_JSON_DOC} ${FLUID_YAML_DOC} "${${platform_uc}_DOC_OUT}" ${FLIUD_JINJA_TEMPLATES}
129+
COMMAND ${PYTHON_3} "-mflucoma.MakeRef" "${platform_lc}" ${FLUID_JSON_DOC} ${FLUID_YAML_DOC} "${${platform_uc}_DOC_OUT}" ${FLIUD_JINJA_TEMPLATES}
130+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
129131
COMMENT ${comment}
130132
DEPENDS ${json_files}
131133
VERBATIM
132134
)
133135
endmacro()
134136

135-
136137
add_ref_target(max "Making Max ref")
137138
add_ref_target(pd "Making PD ref")
138139
add_ref_target(cli "Making CLI ref")
140+
add_ref_target(sc "Making SC ref")
141+
142+
enable_testing()
143+
144+
execute_process(
145+
COMMAND "python" "list_python_tests.py"
146+
OUTPUT_VARIABLE FLUCOMA-DOC-PYTHON_TESTS
147+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
148+
OUTPUT_STRIP_TRAILING_WHITESPACE
149+
ERROR_STRIP_TRAILING_WHITESPACE
150+
)
151+
152+
separate_arguments(TEST_LIST UNIX_COMMAND ${FLUCOMA-DOC-PYTHON_TESTS})
153+
154+
foreach(THIS_TEST ${TEST_LIST})
155+
message(STATUS ${THIS_TEST})
156+
add_test(
157+
NAME ${THIS_TEST}
158+
COMMAND ${PYTHON_3} "-m" "unittest" "-v" ${THIS_TEST}
159+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
160+
)
161+
endforeach()

README.md

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,40 @@
1-
Generate JSON files of parameters from [Fluid Corpus Manipulation Library](https://github.com/flucoma/flucoma-core) objects
1+
Produce documentation for [Fluid Corpus Manipulation Library](https://github.com/flucoma/flucoma-core) objects, from a combination of handwritten material and generated data.
22

3-
This is quick and dirty, so don't be surprised at weirdness.
3+
## Overview
4+
The generated data comes from building an running a set of C++ executables that produce JSON files detailing the interface of FluCoMa objects (parameter and message names, etc.).
45

5-
The JSON will turn up in a folder called `json` in the top level directory, and is generated as part of compilation (by CMake).
6+
The handwritten documentation is (currently) in YAML files, that are meant to match up with the generated data. Some Python code then makes sure this assumption is true (i.e. validates the completeness of the handwritten docs against the generated data), and produces rendered documentation for a choice of environments (Max, Pure Data, Supercollider and the command line).
67

7-
So, simple build steps should be along the lines of
8+
This is intended principally as internal tooling, so there may well be rough edges and weird things. Operation on platforms other than Mac OS or Linux is untested.
9+
10+
## Quick Start
11+
12+
The routine way to interact with everything is via CMake. In addition to this you will need: git, a working C++ toolchain, and Python >= 3.8 with the packages listed in `requirements.txt`. (use `pip install -r requirements.txt` to install all these in one go. )
13+
14+
To configure the project and generate the JSON run
815
```bash
916
mkdir build
1017
cd build
11-
cmake -DFLUID_PATH=/your/path/to/your/fluid-corpus-manipulation/repo ..
18+
cmake ..
1219
make
1320
```
1421

15-
This requires python 3 with the docutils, jinja2, and pyYAML (>=5.1) packages.
22+
If you have a local checkout of the `flucoma-core` repo, then this can be used by adding `-DFLUID_PATH=<path to your flucoma core>`.
23+
24+
The JSON files are generated into `build/json`.
25+
26+
The CMake exposes targets for producing docs for particular environments (`MAKE_MAX_REF`, `MAKE_PD_REF`, `MAKE_SC_REF`,`MAKE_CLI_REF`). These targets will (re-)generate the JSON if needed and then run a Python script to produce the actual docs.
27+
28+
The output paths for the docs default to `build/<host>_ref` but can be overridden by setting CMake cache variables (`MAX_DOC_OUT`,`PD_DOC_OUT`,`CLI_DOC_OUT`,`SC_DOC_OUT`)
29+
30+
### Python Herding
31+
On platforms with multiple Pythons installed (like Macs), CMake can sometimes need some heavy hints in order to avoid settling on system Python. If you get error messages about missing packages, it is likely that CMake has found the 'wrong' one.
32+
33+
For recent versions of CMake (>= 3.15?), adding `-DPYTHON_EXECUTABLE=<path to your python of choice>` is a blunt instrument, but normally works. You can find the path via `which python` in the terminal, if needed. Considerably more options can be found in the [documentation for CMake's `find_python` module](https://cmake.org/cmake/help/latest/module/FindPython.html).
34+
35+
## Documentation
1636

17-
If you have multiple python 3s on your system, using variables like CMAKE_FIND_FRAMEWORK might help CMake find the right one.
37+
Is still thin, but will gather mass in the `code-docs` directory.
1838

1939
## Credits
2040
#### FluCoMa core development team (in alphabetical order)

code-docs/flow.png

111 KB
Loading

code-docs/index.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
2+
# flucoma-docs code proto-documentation
3+
4+
Some code proto-documentation, aimed at those who need to extend or help maintain `flucoma-docs`. The very high level sketch is that there's some C++ code that scrapes some data from the `Clients` in `flucoma-core`, and some Python code that unites this data with structured, handwritten documentation and renders the result into a choice of formats, targeting the environments Flucoma deploys to.
5+
6+
## The C++ Bit
7+
8+
We shall gloss over for now, because it has Change In its Future, pending some significant refactoring and reorganising work on `flucoma-core` in the coming months. It's unfortunately pretty hard to follow unless you know the details of the innards of core, and no-one but me fits that brief currently. In any case, yes I know it takes a while to build. No, I don't know when that will improve.
9+
10+
11+
## The Python Bit
12+
13+
The Python portion has recently had quite an extensive reorganisation, to transform it from the well-known idiom of one-hooj-scary-function, and to pave the way for extending it to produce Supercollider docs. This means it's now a good deal more readable–and documentable–than before, albeit still slightly shonky almost everywhere you look.
14+
15+
Below is the basic flow:
16+
17+
![python code flow](./flow.png)
18+
19+
The Python code all lives in the `flucoma` folder and its children (and so, is in various subpackages of `flucoma` in Python). The main entry point is `flucoma.MakeRef`, which is normally invoked via CMake (but can be invoked by hand). It expects
20+
* a string telling it which CCE to render for
21+
* a collection of paths telling it where to find data, and where to write output.
22+
23+
The `main` function parses the program arguments, dynamically loads a 'driver' for the required host (see below), and then goes through the steps of validating, merging and rendering for each file it encounters in the JSON folder.
24+
25+
Nothing else currently lives in the top level package: all the action is down in `flucoma.doc`.
26+
27+
### Data (`flucoma.doc.data`)
28+
29+
* We have JSON files, containing the generated data for a `Client`. This is taken to be the canonical Source of Truth for the names, number, types, etc. of parameters / controls, messages and so forth exposed by the object (because it comes from the code that defines the object).
30+
31+
* We also have YAML files, that have actual human crafted words that explain these various data. They are only YAML for now – we've decided to port them to reStructuredText once this update is settled. But YAML they are, which makes them easy for computers, but fiddly for humans.
32+
33+
* Blocks of prose *within* the YAML, however, still might want some inline markup, for emphasis, lists, links to other objects etc. *That's* in reStructuredText, just to add to the fiddly for humans bit (hence the forthcoming change).
34+
35+
* Nonetheless, basic takeaway: two sources of data, and they may or may not match. If they *don't* match, it would be good to tell a human person, so that the handwritten documentation can be brought back into sync with all that is good and true.
36+
37+
### Validation (`flucoma.doc.validate`)
38+
39+
This is where the most complex work happens. Given our canonical generated data for each object, we want to check that the handwritten documentation covers everything, and warn the world if there's stuff missing. However, there are instances where parameters or messages that are common across a bunch of objects might have some common documentation to be used, to cut down on redundancy and chances for inconsistency. There are also occasional slight differences between different environments and how stuff needs to be documented. So, things can get a bit edge-casey.
40+
41+
Validation proceeds hierarchically. First through the object, checking for the top-level stuff like a digest and description for each Client, and then through controls (parameters) and messages (and message arguments).
42+
43+
The original incarnation of this code was a forest of `if` trees and manual `dict` mining. It wasn't nice, and was almost impossible to extend, so now we (ab)use the [schema](https://github.com/keleshev/schema) package to try for something a little clearer and more configurable. To be frank, it's still littered with code crimes, but hopefully more legible ones (and definitely more elegant). `Schema` basically works by being fed a data structure the same-ish shape as the thing you want to validate, and checking input against that. Because we're deriving Our Truth from the JSON, we build these structures on the fly.
44+
45+
`Schema` also allows for neat stuff, like denoting certain things as optional, or giving different options for how a value can be valid. However, its basic inclination when something is missing is to bail, whereas the strategy here is *always* to complete, but to provide meaningful and annoying feedback when stuff is missing. In any case where something is missing, we will fallback to checking to see if there is a 'default' for it. There are two sources of these defaults: one global (`flucoma.docs.defaults`), and one host-specific (provided by the 'driver': each host sub-package has a `defaults.py`). Host-specific entries will always override global ones. If a lookup fails, then a warning is issued to the logging system *and* rendered into the generated documentation. Y'know, for motivation.
46+
47+
The code to do all this is a bit gnarly, and a future version of these docs will go into much more detail about how its achieved, but the potted version:
48+
1. doing this fallback trick with `Schema` at the same time as ascertaining what was there and what was missing seemed too horrible, so the validation for controls ('parameters' in soon-to-be-obsolete-speak) and messages happens in two passes, and missing data is filled out with `None`.
49+
2. the `Fallback` stuff itself lives in `flucoma.doc.validate.common`, and takes some mild liberties in order to try and do lookups through hierarchically structured data.
50+
3. our desire to actually tell the world useful things about what is missing results in the greatest number of code crimes and gratuitous cuteness. We're using the built-in `logging` package, and would like to be able to issue warnings that record which object, control, message argument or whatever is missing documentation. To do this, we have a 'context' variable, which is updated using Python a `contextmanager` (basically a thing you can use in a `with` block). To be able to actually keep that up to date whilst `Schema` does its stuff has involved making a little `Schema` wrapper (`RecordContext`) in `common` that records the context as it iterates over structures. At the cost of readability.
51+
52+
### Merging
53+
54+
Once we've finished checking and fleshing out our documentation, the generated and handwritten data are merged to a single `dict`y structure to be passed on to rendering. At this stage, controls that are non-modulate-able are separated off, because each host treats them differently to modulate-able ones.
55+
56+
However, because the needs of different environments are, well, different, there is a post-processing stage following the merge. The most glaring difference here is that whilst for Max and PD we need our controls and messages to be sorted, in SC they need to remain in the order declared.
57+
58+
### Rendering
59+
60+
Still happens via `Jinja`. The templates for Max, PD and CLI are substantively unchanged. There are new templates for SC, which use Jinja's inheritance feature so that the very different shapes of different species of object can be parcelled off cleanly.
61+
62+
As with the validation, I've had to do some cute things in order to try and keep track of 'context' whilst the render is going on, in order to log any warnings from docutils when it renders reStructuredText. The gist of this is that a view type is wrapped around the dictionaries for controls and messages that records the current context when they are iterated by Jinja (see `flucoma.doc.logger.ContextView`).
63+
64+
A note on rendering links to other Flucoma objects. The most frequent cause of reStructuredText errors seems to be with trying to pluralise a `:fluid-obj:` reference like
65+
66+
```reStructuredText
67+
.. won't work:
68+
:fluid-object:`DataSet`s
69+
```
70+
I don't have a completely good solution to this yet. reStructuredText reference syntax would let us have different text, whilst still linking to the correct entity:
71+
72+
```reStructuredText
73+
:fluid-obj:`DataSets<DataSet>`
74+
```
75+
76+
would print `DataSets` but still link properly in SC (i.e. to `Classes/FluidDataSet`), PD and CLI, but I don't think there's a way in Max to link to another object and have alternate link text: what we need to have rendered is `<o>fluid.dataset~</o>`.
77+
78+
### Logging
79+
80+
Ah, I've already gone on about this a bit. Some effort was made to try and improve the quality of information given back. Notably:
81+
1. running make ref will produce a `flucoma-makeref.log` file, with any warnings. These will still be written to `stderr` as well, unless the script is run with `--quiet`
82+
2. A problem before was that the docutils warnings weren't very helpful (partly because we process in fragments, so the line numbers were meaningless). I now try and record both the broad context, and retrieve the actual troublesome markup.
83+
3. It's all a bit Heath-Robinson, in that I've had to wrap the logging framework in and around all these other packages. But, for all that it's not so obvious how it all works, it's not very intrusive: it can be altered, or turned off entirely very easily.
84+
85+
### Drivers
86+
87+
Contain all that is needed to account for the differences between our different rendering targets. It's all still a bit heterogenous and scrappy. Perhaps making the data structures more categorically clear with some hierarchy, and being more Pythonic about using `kwargs` would make it all seem less ad hoc.
88+
89+
## TODOs
90+
91+
* Test coverage is pretty woeful, partly because I'm getting near to the stuff that's hard to test, like rendering.
92+
* There should be some authoring docs that detail the few bits of reStructuredText magic we use (and give a more comprehensive cookbook once we've transitioned to reST properly)
93+
* These code docs could obviously be much more extensive. One possibility would be to use Sphinx to manage these and host on the github pages for this repo (which might be an interesting test case ahead of documenting core)
94+
* The C++ remains mysterious to all but I
95+
* Rendering control constraints is still a horror show, but less than it was
96+
* All the code could be made more readable still

doc/AmpGate.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# (grant agreement No 725899).
88
---
99
digest: Amplitude-based Gating Slicer
10+
species: slicer
1011
sc-categories: Libraries>FluidDecomposition
1112
sc-related: Guides/FluCoMa, Guides/FluidDecomposition
1213
see-also: BufAmpGate, AmpSlice, OnsetSlice, NoveltySlice, TransientSlice

doc/AmpSlice.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# (grant agreement No 725899).
88
---
99
digest: Amplitude-based Detrending Slicer
10+
species: slicer
1011
sc-categories: Libraries>FluidDecomposition
1112
sc-related: Guides/FluCoMa, Guides/FluidDecomposition
1213
see-also: BufAmpSlice, AmpGate, OnsetSlice, NoveltySlice, TransientSlice

doc/AudioTransport.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# (grant agreement No 725899).
88
---
99
digest: Interpolate between sounds
10+
species: transformer
1011
sc-categories: FluidManipulation
1112
sc-related: Classes/FluidBufAudioTransport
1213
see-also:

doc/BufAmpGate.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# (grant agreement No 725899).
88
---
99
digest: Amplitude-based Gating Slicer for Buffers
10+
species: buffer-proc
1011
sc-categories: Libraries>FluidDecomposition
1112
sc-related: Guides/FluCoMa, Guides/FluidDecomposition
1213
see-also: AmpGate, BufAmpSlice, BufOnsetSlice, BufNoveltySlice, BufTransientSlice

doc/BufAmpSlice.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# (grant agreement No 725899).
88
---
99
digest: Amplitude-based Detrending Slicer for Buffers
10+
species: buffer-proc
1011
sc-categories: Libraries>FluidDecomposition
1112
sc-related: Guides/FluCoMa, Guides/FluidDecomposition
1213
see-also: AmpSlice, BufAmpGate, BufOnsetSlice, BufNoveltySlice, BufTransientSlice

0 commit comments

Comments
 (0)