-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Creating a parser to transform the Matlab documentation into a structure #20
base: master
Are you sure you want to change the base?
Conversation
ok for now
that's a problem-- but should easily be overcome
indeed @gaspardcereza: in general, the first message of the PR thread is supposed to be "final", without having to bring too many modifications to it. If you wish to report progress report (ie section "What has been done so far"), i suggest to do it in subsequent posts, so that:
|
@jcohenadad I think the parser is ready to be merged now that it can handle the absence of some sections. The .md generator could be the subject of another PR as part of your "bit by bit" policy on GitHub. |
parser/test.m
Outdated
@@ -0,0 +1,40 @@ | |||
function [output1, output2] = test(arg1, arg2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't the convention to call test functions with suffix as the function to test, i.e.: test_parse_doc.m
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
didn't know that. I'll change it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i don't know either-- i'm basing it just from python's pytest-- obviously things might be different in matlab-- please inform yourself how those tests are run-- @po09i @gab-berest @rtopfer might know
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say that test names (as for unit test if I understand correctly) should have the name of the tested function AND the result. Maybe with more complexe function you'll need to test if the function does the correct thing, but also exceptions. For example:
test_parse_header_complete_pass
test_parse_header_missing_field_pass
test_parse_header_wrong_field_fail
In this case, the first one check if a full header makes a good call and successful return, the second checks if a missing field still returns ok and the third one check that if you give a wring input it fails successfully.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes big function names, but when testing it will be clearer when something fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is test.m actually tested by the CI?
parser/test.m
Outdated
output1 = arg1+arg2; | ||
% We don't want that kind of comments to appear in the parsed documentation. | ||
output2 = arg1-arg2; % Neither this kind of comments | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what are the assertions for this test? how can it fail?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That test function is just some dummy function that will be parsed (it could even be simply deleted and we could test the parsing directly on parse_doc
). It is not a unit test. I would like to do an general unit test that includes the .md part once everything is good to go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bits-by-bits-- good practice is: for every code that is written, a test should go along it.
never "postpone" writing tests
things that we postpone might take a while to be implemented and then we forget about it
so the philosophy is: do it now, not tomorrow
also: a test that never fails is not a test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I'll create a unit test right now then. Is it ok if I create a test
folder at the root of the repo ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@po09i is currently looking into this and can let us know what to do
in that case, pls open the pending issues that will not be addressed by this PR, and reference those new issues in this PR-- otherwise it will be forgotten |
@gaspardcereza @jcohenadad CI is currently not set up on helpDocMd but will implement soon. You can also run the tests locally by calling the You can refer to shimming-toolbox : /tests to have a general view. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I said a lot of things in this review. some are more relevant to the way the documentation is done to make it more flexible.
|
||
switch header | ||
case 'SYNTAX' | ||
docStruct.syntax = erase(functionDoc(sectionStart:sectionEnd),' '); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code is really whitespace dependent and the problem is that there is a lot of characters that are refered as whitespaces (tab, space, null, ...). When someone will use a different number of spaces, everything will break. A good way of reducing this problem and making the documentation more flexible is adding a symbol in front of specific information ('->' in front of inputs, '<- in front of outputs', '@' in front of notes, '_' in front of types). This way you know that everything that is after a certain symbol is a certian type until you reach the next symbol.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes you're right but is it really easier to make sure everyone adds the right symbol in the right place or that they just respect the number of white spaces ? That's something I've been asking myself when I was coding but I don't have a clear preference for any of these options so I'm open to discussion.
Also, the way the code is implemented right now, there's no need for a symbol in description
and notes
because the code will just take everything that's in these sections and store it as a paragraph. I also don't see a genuine utility in having different symbols for the inputs and outputs as they are in different sections and thus can't be mixed up. But it might however be more visual to have these different arrows.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem with the whitespaces is actually the implementation (tabs vs spaces vs null characters, etc.). There's a lot of ways to add space in a text editor. However with a symbol it's easy to know which one.
The question of someone putting the right character is not relevant since it is the same for every way we find (number of spaces, etc.).
you are right for the description and notes, but I'm talking more about the input/outputs.
@@ -0,0 +1,88 @@ | |||
function docStruct = parse_doc(functionPath) | |||
%PARSE_DOC Generates a structure from the input function's documentation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's something that was agreed previously, but is it necessary to have a struct? Isn't it a better idea to have a dictionary (map) so you can input any information needed in the header like examples, etc. and make a test at the end of the parse to check that minimal sections are present?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to go with a struct mainly because it was something I knew how to use. 😉 I'm not familiar with dictionaries but if you think that might be a better solution I'd be glad to discuss about it ! Is this what you are referring to ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's exactly what I'm thinking about. I think maps or set depending on what we want could be more versatile.
parser/parse_doc.m
Outdated
% Initialize a cell that will receive the lines in the description | ||
functionDoc = []; | ||
|
||
textLine = fgetl(functionTxt); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is duplicated with the one at line 55. could it be possible to fgetl at the begining of the while loop so you have it only once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition for my while loop is length(textLine) >= 1
so I need to call it before entering the loop...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. I just really don't like duplicated code. You could also test in an if statement with a break command:
while 1
textLine = fgetl(functionTxt);
if length(textLine) >= 1
break;
end
...
end
@@ -0,0 +1,19 @@ | |||
function test_parse_doc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be for me other tests. This test coverage is really minimal. When we merge, we should have the certitude that every way to use the function or not use the function is tested and working (specially for parsing functions)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just wanted to draft something here to understand how unit testing works (never did it before). But I totally agree that a maximum of things have to be covered during the test. 👍
% | ||
% That function makes sure that parse_doc is working properly and returns | ||
% the expected structure for the documentation. | ||
cd('../parser') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There will be an error if the function is run from another directory than the one tested (and in Matlab, it is possible to run a script or function from a different directory). Maybe you should check with a pwd function end extrapolate the correct way to get to the place you want. You could also check this link to know more about finding an item location in Matlab:
https://www.mathworks.com/help/matlab/ref/which.html
This way your code could be more robust.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should consider removing this line entirely and make the code work whether this is called from anywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that's something I why trying to do but I could only find the path to the function using which if I was already in the right folder (not really useful in our case...). One solution would be to add helpDocMd/parser/
to the Matlab path but It would also require to know the full path toward it. @po09i do you know how this is usually done with unit testing ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The which command in Matlab does this. It returns the absolute path of the file you asked. See the link I added in my previous comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be a startup.m script like the one in shimming-toolbox which adds all the source files to the Matlab path. This script would be launched when running the tests. So my point of view is you should assume all of helpDocMd is on the Matlab path. If it is not yet created in this repo, I think you should create it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some reviews.
docStruct.description = strjoin(strip(functionDoc(sectionStart:sectionEnd)),' '); | ||
case 'INPUTS' | ||
section = functionDoc(sectionStart:sectionEnd); | ||
docStruct.inputs.names = strip(section(cellfun('isempty', strfind(section,' ')))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try to be carreful with embeding functions in other functions. A lot of parenthesis can make the code less readable. sometimes, it is better to separate in multiple lines.
Context
We are currently trying to define how to document our functions within the Matlab code. We also want to gather all these descriptions on our website.
Closes #19 .
Problem
What looks good in the Matlab code isn't always visually satisfying once on the website (and vice-versa).
Solution
Parsing the Matlab code to gather all the useful information (description, syntax, inputs, outputs...) as a structure or that will then be used to create the .md files for the website's generation (and displayed as we want).
Playing with the parser will also help us defining what could be the best convention for documenting our functions (related to this issue).
What has been done so far
I implemented a
parser
that reads a Matlab functions and returns all the information contained in the documentation section in a structure. I started fromtest.m
as template for the documentation (though I think the final convention won't exactly look like this).Here's what the Matlab documentation looks like:
And the returned structure contains the fields:
Though I'm still not satisfied with the parser for 2 main reasons:
SYNTAX
,DESCRIPTION
, etc...I think there are many other defaults in the code but it might be a good start with some useful parsing Matlab features that I found.
The next step is to find a way to reorganize that structure as a .md file.
Feel free to comment if you have ideas, advice or anything...