EasyDocPy 1.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- easydocpy-1.2.0/EasyDocPy.egg-info/PKG-INFO +193 -0
- easydocpy-1.2.0/EasyDocPy.egg-info/SOURCES.txt +15 -0
- easydocpy-1.2.0/EasyDocPy.egg-info/dependency_links.txt +1 -0
- easydocpy-1.2.0/EasyDocPy.egg-info/entry_points.txt +2 -0
- easydocpy-1.2.0/EasyDocPy.egg-info/top_level.txt +1 -0
- easydocpy-1.2.0/LICENSE +7 -0
- easydocpy-1.2.0/PKG-INFO +193 -0
- easydocpy-1.2.0/README.md +180 -0
- easydocpy-1.2.0/easydoc/__init__.py +11 -0
- easydocpy-1.2.0/easydoc/__main__.py +29 -0
- easydocpy-1.2.0/easydoc/analyser.py +359 -0
- easydocpy-1.2.0/easydoc/config/custom_comment_lines.json +10 -0
- easydocpy-1.2.0/easydoc/generator.py +155 -0
- easydocpy-1.2.0/easydoc/objects.py +44 -0
- easydocpy-1.2.0/easydoc/templates/template.md +20 -0
- easydocpy-1.2.0/pyproject.toml +24 -0
- easydocpy-1.2.0/setup.cfg +4 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: EasyDocPy
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: python documentation generator
|
|
5
|
+
Author: epsilonkn
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/epsilonkn/EasyDoc
|
|
8
|
+
Project-URL: Issues, https://github.com/epsilonkn/EasyDoc/issues
|
|
9
|
+
Requires-Python: >=3.12
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
14
|
+
# Doc
|
|
15
|
+
|
|
16
|
+
**Welcome to the page of the EasyDoc project**
|
|
17
|
+
|
|
18
|
+
The idea of that package is to create a technical documentation of a python source file, from the docstrings and the commentary in the source code : yes, the more you comment your code, the more the module will scrap.
|
|
19
|
+
|
|
20
|
+
For more information, read the wiki : https://github.com/epsilonkn/EasyDoc/wiki
|
|
21
|
+
|
|
22
|
+
## To use it :
|
|
23
|
+
|
|
24
|
+
### To comment your code :
|
|
25
|
+
|
|
26
|
+
#### **declarations :**
|
|
27
|
+
|
|
28
|
+
Is accepted for classes
|
|
29
|
+
|
|
30
|
+
class obj(*parents):
|
|
31
|
+
|
|
32
|
+
or
|
|
33
|
+
|
|
34
|
+
class obj:
|
|
35
|
+
|
|
36
|
+
Is accepted for functions :
|
|
37
|
+
|
|
38
|
+
def foo(arg, agr2 = 10, arg 3 : str = "poo", *args, **kwargs) -> None :
|
|
39
|
+
|
|
40
|
+
or
|
|
41
|
+
|
|
42
|
+
def foo(arg,
|
|
43
|
+
agr2 = 10,
|
|
44
|
+
arg 3 : str = "poo",
|
|
45
|
+
*args,
|
|
46
|
+
**kwargs) -> None :
|
|
47
|
+
|
|
48
|
+
Note : the tabs before the "def" are obviously accepted, but the module will assume a function with tabs before is a method of a class.
|
|
49
|
+
|
|
50
|
+
#### **docstrings :**
|
|
51
|
+
|
|
52
|
+
The docstrings MUST be defined by 3 double quotes at the beginning and same at the end, otherwise it won't work.
|
|
53
|
+
\
|
|
54
|
+
You can place docstrings below your classes, methods and functions to detail them, they can be juste below the declaration, or some lines below. There can be 1 or multiple docstrings, but they will all get concatenated into one.
|
|
55
|
+
|
|
56
|
+
Examples :
|
|
57
|
+
|
|
58
|
+
class foo:
|
|
59
|
+
"""
|
|
60
|
+
a detail
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
class foo:
|
|
64
|
+
"""a detail"""
|
|
65
|
+
|
|
66
|
+
def foo(*args):
|
|
67
|
+
"""
|
|
68
|
+
detail
|
|
69
|
+
|
|
70
|
+
args:
|
|
71
|
+
args : detail
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def foo(*args):
|
|
75
|
+
"""detail"""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
#### **custom comment lines :**
|
|
79
|
+
|
|
80
|
+
To enhance your documentation, there are a few custom comments you can do :
|
|
81
|
+
|
|
82
|
+
**#/actual_version**
|
|
83
|
+
\
|
|
84
|
+
Define the version of the file
|
|
85
|
+
|
|
86
|
+
Use :
|
|
87
|
+
#/actual_version : V.1.9.25
|
|
88
|
+
|
|
89
|
+
**#/author :**
|
|
90
|
+
\
|
|
91
|
+
Define the author the file
|
|
92
|
+
|
|
93
|
+
Use :
|
|
94
|
+
#/author : epsilonkn
|
|
95
|
+
|
|
96
|
+
**#/creation_date :**
|
|
97
|
+
\
|
|
98
|
+
Date of creation of the file
|
|
99
|
+
|
|
100
|
+
Use :
|
|
101
|
+
#/creation_date : 01/01/1900
|
|
102
|
+
|
|
103
|
+
**#/last_release_date :**
|
|
104
|
+
\
|
|
105
|
+
Last date of release of the file
|
|
106
|
+
|
|
107
|
+
Use :
|
|
108
|
+
#/last_release_date : 02/01/1900
|
|
109
|
+
|
|
110
|
+
**#/TODO :**
|
|
111
|
+
\
|
|
112
|
+
List all the todo in the file
|
|
113
|
+
|
|
114
|
+
Use :
|
|
115
|
+
#/TODO Find time to write the doc
|
|
116
|
+
#/TODO Write the doc when dev is done
|
|
117
|
+
#/TODO Write the doc of this file
|
|
118
|
+
|
|
119
|
+
**#/planned :**
|
|
120
|
+
\
|
|
121
|
+
List all the planned future versions
|
|
122
|
+
|
|
123
|
+
Use :
|
|
124
|
+
#/planned V2 : rewrite the code for better scalability
|
|
125
|
+
#/planned V2.1 : patch the errors of the new code
|
|
126
|
+
#/planned V2.2 : ......
|
|
127
|
+
|
|
128
|
+
**#/file_intro :**
|
|
129
|
+
\
|
|
130
|
+
Marks the begin of the file intro. the file intro follows the same rules as the function's docstrings.
|
|
131
|
+
|
|
132
|
+
Use :
|
|
133
|
+
#/file_intro
|
|
134
|
+
"""
|
|
135
|
+
this file is meant to provide the result of the operation 2+2
|
|
136
|
+
it takes as a paramter....
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
**#/const :**
|
|
140
|
+
\
|
|
141
|
+
Explains a constant in the code. for now, all the constants are written at the begining of the doc no matter if they're class's constants or file's constants.
|
|
142
|
+
|
|
143
|
+
Use :
|
|
144
|
+
#/const CONST defines the gravitation force for calculus purposes
|
|
145
|
+
CONST = 10
|
|
146
|
+
#/const DEFAULT defines the default values for the empty strings
|
|
147
|
+
DEFAULT = "VOID"
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
#### Generate the documentation :
|
|
151
|
+
|
|
152
|
+
### In a terminal :
|
|
153
|
+
|
|
154
|
+
Here is the only way implemented to generate a documentation in command line :
|
|
155
|
+
|
|
156
|
+
easydoc file "/your/path/to/file.py"
|
|
157
|
+
|
|
158
|
+
Note 1 : your terminal must be in the directory where you want to see the documentation generated.
|
|
159
|
+
|
|
160
|
+
Note 2 : you can pass the path without double quotes, however it is better to keep them if your path got spaces in it.
|
|
161
|
+
|
|
162
|
+
### In a python program :
|
|
163
|
+
|
|
164
|
+
Disclaimer : Running the module in a program by calling directly the classes can be possible, but this might also opens a pandora box of bugs.
|
|
165
|
+
|
|
166
|
+
The reason you would need to run the module manually in a python file is to get a better control over the process.
|
|
167
|
+
Unfortunately, in the actual state of the module, the control over the module is still very little, this will be improved in the next updates.
|
|
168
|
+
|
|
169
|
+
If you still wish to do it in a program rather than in command line, here is the base :
|
|
170
|
+
|
|
171
|
+
From easydoc import Parser, MarkdownGenerator
|
|
172
|
+
|
|
173
|
+
parser = Parser(path = "/path/to/file.py", automatic = False)
|
|
174
|
+
parse_list = parser.get_parse()
|
|
175
|
+
file_header = parser.get_intro()
|
|
176
|
+
|
|
177
|
+
MarkdownGenerator(parse_list, file_header, doc_file_name)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
## Next updates :
|
|
182
|
+
|
|
183
|
+
|Version | Improvement|
|
|
184
|
+
|-|-|
|
|
185
|
+
|V 1.2.0 | adding custom comment line to add info to the documentation|
|
|
186
|
+
|V 1.3.0| better control over the process when done in a program |
|
|
187
|
+
|V 1.4.0| adding doc generation for an entire directory |
|
|
188
|
+
|V 1.5.0| adding user configuration |
|
|
189
|
+
|......||
|
|
190
|
+
|
|
191
|
+
## API :
|
|
192
|
+
|
|
193
|
+
For now there is no released API entry points in the module, you'll have to wait for the V1.3 and V1.5 for these parts
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
EasyDocPy.egg-info/PKG-INFO
|
|
5
|
+
EasyDocPy.egg-info/SOURCES.txt
|
|
6
|
+
EasyDocPy.egg-info/dependency_links.txt
|
|
7
|
+
EasyDocPy.egg-info/entry_points.txt
|
|
8
|
+
EasyDocPy.egg-info/top_level.txt
|
|
9
|
+
easydoc/__init__.py
|
|
10
|
+
easydoc/__main__.py
|
|
11
|
+
easydoc/analyser.py
|
|
12
|
+
easydoc/generator.py
|
|
13
|
+
easydoc/objects.py
|
|
14
|
+
easydoc/config/custom_comment_lines.json
|
|
15
|
+
easydoc/templates/template.md
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
easydoc
|
easydocpy-1.2.0/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright (c) 2026 Ywan Maxime GERARD
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
easydocpy-1.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: EasyDocPy
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: python documentation generator
|
|
5
|
+
Author: epsilonkn
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/epsilonkn/EasyDoc
|
|
8
|
+
Project-URL: Issues, https://github.com/epsilonkn/EasyDoc/issues
|
|
9
|
+
Requires-Python: >=3.12
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
14
|
+
# Doc
|
|
15
|
+
|
|
16
|
+
**Welcome to the page of the EasyDoc project**
|
|
17
|
+
|
|
18
|
+
The idea of that package is to create a technical documentation of a python source file, from the docstrings and the commentary in the source code : yes, the more you comment your code, the more the module will scrap.
|
|
19
|
+
|
|
20
|
+
For more information, read the wiki : https://github.com/epsilonkn/EasyDoc/wiki
|
|
21
|
+
|
|
22
|
+
## To use it :
|
|
23
|
+
|
|
24
|
+
### To comment your code :
|
|
25
|
+
|
|
26
|
+
#### **declarations :**
|
|
27
|
+
|
|
28
|
+
Is accepted for classes
|
|
29
|
+
|
|
30
|
+
class obj(*parents):
|
|
31
|
+
|
|
32
|
+
or
|
|
33
|
+
|
|
34
|
+
class obj:
|
|
35
|
+
|
|
36
|
+
Is accepted for functions :
|
|
37
|
+
|
|
38
|
+
def foo(arg, agr2 = 10, arg 3 : str = "poo", *args, **kwargs) -> None :
|
|
39
|
+
|
|
40
|
+
or
|
|
41
|
+
|
|
42
|
+
def foo(arg,
|
|
43
|
+
agr2 = 10,
|
|
44
|
+
arg 3 : str = "poo",
|
|
45
|
+
*args,
|
|
46
|
+
**kwargs) -> None :
|
|
47
|
+
|
|
48
|
+
Note : the tabs before the "def" are obviously accepted, but the module will assume a function with tabs before is a method of a class.
|
|
49
|
+
|
|
50
|
+
#### **docstrings :**
|
|
51
|
+
|
|
52
|
+
The docstrings MUST be defined by 3 double quotes at the beginning and same at the end, otherwise it won't work.
|
|
53
|
+
\
|
|
54
|
+
You can place docstrings below your classes, methods and functions to detail them, they can be juste below the declaration, or some lines below. There can be 1 or multiple docstrings, but they will all get concatenated into one.
|
|
55
|
+
|
|
56
|
+
Examples :
|
|
57
|
+
|
|
58
|
+
class foo:
|
|
59
|
+
"""
|
|
60
|
+
a detail
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
class foo:
|
|
64
|
+
"""a detail"""
|
|
65
|
+
|
|
66
|
+
def foo(*args):
|
|
67
|
+
"""
|
|
68
|
+
detail
|
|
69
|
+
|
|
70
|
+
args:
|
|
71
|
+
args : detail
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def foo(*args):
|
|
75
|
+
"""detail"""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
#### **custom comment lines :**
|
|
79
|
+
|
|
80
|
+
To enhance your documentation, there are a few custom comments you can do :
|
|
81
|
+
|
|
82
|
+
**#/actual_version**
|
|
83
|
+
\
|
|
84
|
+
Define the version of the file
|
|
85
|
+
|
|
86
|
+
Use :
|
|
87
|
+
#/actual_version : V.1.9.25
|
|
88
|
+
|
|
89
|
+
**#/author :**
|
|
90
|
+
\
|
|
91
|
+
Define the author the file
|
|
92
|
+
|
|
93
|
+
Use :
|
|
94
|
+
#/author : epsilonkn
|
|
95
|
+
|
|
96
|
+
**#/creation_date :**
|
|
97
|
+
\
|
|
98
|
+
Date of creation of the file
|
|
99
|
+
|
|
100
|
+
Use :
|
|
101
|
+
#/creation_date : 01/01/1900
|
|
102
|
+
|
|
103
|
+
**#/last_release_date :**
|
|
104
|
+
\
|
|
105
|
+
Last date of release of the file
|
|
106
|
+
|
|
107
|
+
Use :
|
|
108
|
+
#/last_release_date : 02/01/1900
|
|
109
|
+
|
|
110
|
+
**#/TODO :**
|
|
111
|
+
\
|
|
112
|
+
List all the todo in the file
|
|
113
|
+
|
|
114
|
+
Use :
|
|
115
|
+
#/TODO Find time to write the doc
|
|
116
|
+
#/TODO Write the doc when dev is done
|
|
117
|
+
#/TODO Write the doc of this file
|
|
118
|
+
|
|
119
|
+
**#/planned :**
|
|
120
|
+
\
|
|
121
|
+
List all the planned future versions
|
|
122
|
+
|
|
123
|
+
Use :
|
|
124
|
+
#/planned V2 : rewrite the code for better scalability
|
|
125
|
+
#/planned V2.1 : patch the errors of the new code
|
|
126
|
+
#/planned V2.2 : ......
|
|
127
|
+
|
|
128
|
+
**#/file_intro :**
|
|
129
|
+
\
|
|
130
|
+
Marks the begin of the file intro. the file intro follows the same rules as the function's docstrings.
|
|
131
|
+
|
|
132
|
+
Use :
|
|
133
|
+
#/file_intro
|
|
134
|
+
"""
|
|
135
|
+
this file is meant to provide the result of the operation 2+2
|
|
136
|
+
it takes as a paramter....
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
**#/const :**
|
|
140
|
+
\
|
|
141
|
+
Explains a constant in the code. for now, all the constants are written at the begining of the doc no matter if they're class's constants or file's constants.
|
|
142
|
+
|
|
143
|
+
Use :
|
|
144
|
+
#/const CONST defines the gravitation force for calculus purposes
|
|
145
|
+
CONST = 10
|
|
146
|
+
#/const DEFAULT defines the default values for the empty strings
|
|
147
|
+
DEFAULT = "VOID"
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
#### Generate the documentation :
|
|
151
|
+
|
|
152
|
+
### In a terminal :
|
|
153
|
+
|
|
154
|
+
Here is the only way implemented to generate a documentation in command line :
|
|
155
|
+
|
|
156
|
+
easydoc file "/your/path/to/file.py"
|
|
157
|
+
|
|
158
|
+
Note 1 : your terminal must be in the directory where you want to see the documentation generated.
|
|
159
|
+
|
|
160
|
+
Note 2 : you can pass the path without double quotes, however it is better to keep them if your path got spaces in it.
|
|
161
|
+
|
|
162
|
+
### In a python program :
|
|
163
|
+
|
|
164
|
+
Disclaimer : Running the module in a program by calling directly the classes can be possible, but this might also opens a pandora box of bugs.
|
|
165
|
+
|
|
166
|
+
The reason you would need to run the module manually in a python file is to get a better control over the process.
|
|
167
|
+
Unfortunately, in the actual state of the module, the control over the module is still very little, this will be improved in the next updates.
|
|
168
|
+
|
|
169
|
+
If you still wish to do it in a program rather than in command line, here is the base :
|
|
170
|
+
|
|
171
|
+
From easydoc import Parser, MarkdownGenerator
|
|
172
|
+
|
|
173
|
+
parser = Parser(path = "/path/to/file.py", automatic = False)
|
|
174
|
+
parse_list = parser.get_parse()
|
|
175
|
+
file_header = parser.get_intro()
|
|
176
|
+
|
|
177
|
+
MarkdownGenerator(parse_list, file_header, doc_file_name)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
## Next updates :
|
|
182
|
+
|
|
183
|
+
|Version | Improvement|
|
|
184
|
+
|-|-|
|
|
185
|
+
|V 1.2.0 | adding custom comment line to add info to the documentation|
|
|
186
|
+
|V 1.3.0| better control over the process when done in a program |
|
|
187
|
+
|V 1.4.0| adding doc generation for an entire directory |
|
|
188
|
+
|V 1.5.0| adding user configuration |
|
|
189
|
+
|......||
|
|
190
|
+
|
|
191
|
+
## API :
|
|
192
|
+
|
|
193
|
+
For now there is no released API entry points in the module, you'll have to wait for the V1.3 and V1.5 for these parts
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Doc
|
|
2
|
+
|
|
3
|
+
**Welcome to the page of the EasyDoc project**
|
|
4
|
+
|
|
5
|
+
The idea of that package is to create a technical documentation of a python source file, from the docstrings and the commentary in the source code : yes, the more you comment your code, the more the module will scrap.
|
|
6
|
+
|
|
7
|
+
For more information, read the wiki : https://github.com/epsilonkn/EasyDoc/wiki
|
|
8
|
+
|
|
9
|
+
## To use it :
|
|
10
|
+
|
|
11
|
+
### To comment your code :
|
|
12
|
+
|
|
13
|
+
#### **declarations :**
|
|
14
|
+
|
|
15
|
+
Is accepted for classes
|
|
16
|
+
|
|
17
|
+
class obj(*parents):
|
|
18
|
+
|
|
19
|
+
or
|
|
20
|
+
|
|
21
|
+
class obj:
|
|
22
|
+
|
|
23
|
+
Is accepted for functions :
|
|
24
|
+
|
|
25
|
+
def foo(arg, agr2 = 10, arg 3 : str = "poo", *args, **kwargs) -> None :
|
|
26
|
+
|
|
27
|
+
or
|
|
28
|
+
|
|
29
|
+
def foo(arg,
|
|
30
|
+
agr2 = 10,
|
|
31
|
+
arg 3 : str = "poo",
|
|
32
|
+
*args,
|
|
33
|
+
**kwargs) -> None :
|
|
34
|
+
|
|
35
|
+
Note : the tabs before the "def" are obviously accepted, but the module will assume a function with tabs before is a method of a class.
|
|
36
|
+
|
|
37
|
+
#### **docstrings :**
|
|
38
|
+
|
|
39
|
+
The docstrings MUST be defined by 3 double quotes at the beginning and same at the end, otherwise it won't work.
|
|
40
|
+
\
|
|
41
|
+
You can place docstrings below your classes, methods and functions to detail them, they can be juste below the declaration, or some lines below. There can be 1 or multiple docstrings, but they will all get concatenated into one.
|
|
42
|
+
|
|
43
|
+
Examples :
|
|
44
|
+
|
|
45
|
+
class foo:
|
|
46
|
+
"""
|
|
47
|
+
a detail
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
class foo:
|
|
51
|
+
"""a detail"""
|
|
52
|
+
|
|
53
|
+
def foo(*args):
|
|
54
|
+
"""
|
|
55
|
+
detail
|
|
56
|
+
|
|
57
|
+
args:
|
|
58
|
+
args : detail
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def foo(*args):
|
|
62
|
+
"""detail"""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
#### **custom comment lines :**
|
|
66
|
+
|
|
67
|
+
To enhance your documentation, there are a few custom comments you can do :
|
|
68
|
+
|
|
69
|
+
**#/actual_version**
|
|
70
|
+
\
|
|
71
|
+
Define the version of the file
|
|
72
|
+
|
|
73
|
+
Use :
|
|
74
|
+
#/actual_version : V.1.9.25
|
|
75
|
+
|
|
76
|
+
**#/author :**
|
|
77
|
+
\
|
|
78
|
+
Define the author the file
|
|
79
|
+
|
|
80
|
+
Use :
|
|
81
|
+
#/author : epsilonkn
|
|
82
|
+
|
|
83
|
+
**#/creation_date :**
|
|
84
|
+
\
|
|
85
|
+
Date of creation of the file
|
|
86
|
+
|
|
87
|
+
Use :
|
|
88
|
+
#/creation_date : 01/01/1900
|
|
89
|
+
|
|
90
|
+
**#/last_release_date :**
|
|
91
|
+
\
|
|
92
|
+
Last date of release of the file
|
|
93
|
+
|
|
94
|
+
Use :
|
|
95
|
+
#/last_release_date : 02/01/1900
|
|
96
|
+
|
|
97
|
+
**#/TODO :**
|
|
98
|
+
\
|
|
99
|
+
List all the todo in the file
|
|
100
|
+
|
|
101
|
+
Use :
|
|
102
|
+
#/TODO Find time to write the doc
|
|
103
|
+
#/TODO Write the doc when dev is done
|
|
104
|
+
#/TODO Write the doc of this file
|
|
105
|
+
|
|
106
|
+
**#/planned :**
|
|
107
|
+
\
|
|
108
|
+
List all the planned future versions
|
|
109
|
+
|
|
110
|
+
Use :
|
|
111
|
+
#/planned V2 : rewrite the code for better scalability
|
|
112
|
+
#/planned V2.1 : patch the errors of the new code
|
|
113
|
+
#/planned V2.2 : ......
|
|
114
|
+
|
|
115
|
+
**#/file_intro :**
|
|
116
|
+
\
|
|
117
|
+
Marks the begin of the file intro. the file intro follows the same rules as the function's docstrings.
|
|
118
|
+
|
|
119
|
+
Use :
|
|
120
|
+
#/file_intro
|
|
121
|
+
"""
|
|
122
|
+
this file is meant to provide the result of the operation 2+2
|
|
123
|
+
it takes as a paramter....
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
**#/const :**
|
|
127
|
+
\
|
|
128
|
+
Explains a constant in the code. for now, all the constants are written at the begining of the doc no matter if they're class's constants or file's constants.
|
|
129
|
+
|
|
130
|
+
Use :
|
|
131
|
+
#/const CONST defines the gravitation force for calculus purposes
|
|
132
|
+
CONST = 10
|
|
133
|
+
#/const DEFAULT defines the default values for the empty strings
|
|
134
|
+
DEFAULT = "VOID"
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
#### Generate the documentation :
|
|
138
|
+
|
|
139
|
+
### In a terminal :
|
|
140
|
+
|
|
141
|
+
Here is the only way implemented to generate a documentation in command line :
|
|
142
|
+
|
|
143
|
+
easydoc file "/your/path/to/file.py"
|
|
144
|
+
|
|
145
|
+
Note 1 : your terminal must be in the directory where you want to see the documentation generated.
|
|
146
|
+
|
|
147
|
+
Note 2 : you can pass the path without double quotes, however it is better to keep them if your path got spaces in it.
|
|
148
|
+
|
|
149
|
+
### In a python program :
|
|
150
|
+
|
|
151
|
+
Disclaimer : Running the module in a program by calling directly the classes can be possible, but this might also opens a pandora box of bugs.
|
|
152
|
+
|
|
153
|
+
The reason you would need to run the module manually in a python file is to get a better control over the process.
|
|
154
|
+
Unfortunately, in the actual state of the module, the control over the module is still very little, this will be improved in the next updates.
|
|
155
|
+
|
|
156
|
+
If you still wish to do it in a program rather than in command line, here is the base :
|
|
157
|
+
|
|
158
|
+
From easydoc import Parser, MarkdownGenerator
|
|
159
|
+
|
|
160
|
+
parser = Parser(path = "/path/to/file.py", automatic = False)
|
|
161
|
+
parse_list = parser.get_parse()
|
|
162
|
+
file_header = parser.get_intro()
|
|
163
|
+
|
|
164
|
+
MarkdownGenerator(parse_list, file_header, doc_file_name)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
## Next updates :
|
|
169
|
+
|
|
170
|
+
|Version | Improvement|
|
|
171
|
+
|-|-|
|
|
172
|
+
|V 1.2.0 | adding custom comment line to add info to the documentation|
|
|
173
|
+
|V 1.3.0| better control over the process when done in a program |
|
|
174
|
+
|V 1.4.0| adding doc generation for an entire directory |
|
|
175
|
+
|V 1.5.0| adding user configuration |
|
|
176
|
+
|......||
|
|
177
|
+
|
|
178
|
+
## API :
|
|
179
|
+
|
|
180
|
+
For now there is no released API entry points in the module, you'll have to wait for the V1.3 and V1.5 for these parts
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from .analyser import Parser
|
|
3
|
+
import pathlib
|
|
4
|
+
|
|
5
|
+
from importlib.metadata import version
|
|
6
|
+
|
|
7
|
+
if "--version" == sys.argv[1]:
|
|
8
|
+
print("PyEasyDoc", version("PyEasyDoc"))
|
|
9
|
+
exit(0)
|
|
10
|
+
|
|
11
|
+
types = ["file", "dir"]
|
|
12
|
+
|
|
13
|
+
if sys.argv[1] not in types :
|
|
14
|
+
raise ValueError(f"The parameter {sys.argv[1]} is invalid\neasydoc accepts only \"file\" and \"dir\"")
|
|
15
|
+
|
|
16
|
+
path = pathlib.Path(sys.argv[2])
|
|
17
|
+
|
|
18
|
+
if not path.exists() :
|
|
19
|
+
raise ValueError(f"The path {sys.argv[2]} is doesn't exists")
|
|
20
|
+
|
|
21
|
+
match sys.argv[1] :
|
|
22
|
+
|
|
23
|
+
case "file":
|
|
24
|
+
if path.suffix != ".py" :
|
|
25
|
+
raise ValueError(f"The path {sys.argv[2]} doesn't point to a python file")
|
|
26
|
+
Parser(path)
|
|
27
|
+
case "group":
|
|
28
|
+
if path.suffix != ".py" :
|
|
29
|
+
raise NotImplementedError("the directory documentation mode hasn't been developped yet.")
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
#/file_intro
|
|
2
|
+
"""
|
|
3
|
+
Module d'analyse d'un fichier source python
|
|
4
|
+
contient les classes de données ainsi que la classe Analyse qui se charge du parsing
|
|
5
|
+
|
|
6
|
+
Raises:
|
|
7
|
+
IndexError: raise IndexError si la classe Analyse détecte une parenthèse non fermée
|
|
8
|
+
empêchant la détection de la fin d'une fonction
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
#/actual_version : 1.2.0
|
|
13
|
+
#/last_release_date : 20/02/2026
|
|
14
|
+
#/author : Ywan GERARD
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
from importlib.resources import files
|
|
18
|
+
import json
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
import re
|
|
21
|
+
import sys
|
|
22
|
+
from .objects import *
|
|
23
|
+
from .generator import MarkdownGenerator
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Parser:
|
|
28
|
+
"""
|
|
29
|
+
Cette classe parcours le fichier source, identifie les classes et fonctions
|
|
30
|
+
et identifie les docstrings présents pour chaque classe et fonction
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, path, automatic : bool = True):
|
|
34
|
+
"""
|
|
35
|
+
initialise les attributs de la classe :
|
|
36
|
+
-fpath contient le chemin vers le fichier source
|
|
37
|
+
-fname contient le nom du fichier source
|
|
38
|
+
-parse est une liste contenant les classes et fonctions indépendantes scrappées
|
|
39
|
+
-intro contient le docstring en en-tête du fichier source
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
path (str): chemin vers le fichier source.
|
|
43
|
+
automatic (bool): chemin vers le fichier source.
|
|
44
|
+
"""
|
|
45
|
+
self.auto = automatic
|
|
46
|
+
self.fpath = Path(path)
|
|
47
|
+
self.fname = self.fpath.stem
|
|
48
|
+
self.parse : list[Parsed_class, Parsed_function] = []
|
|
49
|
+
self.customs : dict = self.open_custom_config()
|
|
50
|
+
self.file_data : list[Custom_comment] = []
|
|
51
|
+
self.pointer = 0
|
|
52
|
+
self.parse_source()
|
|
53
|
+
if self.auto :
|
|
54
|
+
MarkdownGenerator(self.parse, self.file_data, self.fname)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_parse(self):
|
|
59
|
+
return self.parse
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def parse_source(self):
|
|
63
|
+
"""
|
|
64
|
+
parcours le code à la recherche d'une déclaration de classe, de fonction
|
|
65
|
+
et recherche un docstring rattaché à aucune classe ni fonction
|
|
66
|
+
ce docstring indépendant est interprété comme une explication du fichier source
|
|
67
|
+
"""
|
|
68
|
+
source : list[str]= self.get_source()
|
|
69
|
+
print("classes and functions found :")
|
|
70
|
+
while self.pointer < len(source):
|
|
71
|
+
if self.is_class(source[ self.pointer]):
|
|
72
|
+
print(source[ self.pointer])
|
|
73
|
+
self.pointer += self.class_parser(source[ self.pointer:])
|
|
74
|
+
|
|
75
|
+
elif self.is_function(source[ self.pointer:]):
|
|
76
|
+
print(source[ self.pointer])
|
|
77
|
+
self.pointer += self.function_parser(source[ self.pointer:])
|
|
78
|
+
|
|
79
|
+
elif custom:=self.is_custom(source[ self.pointer]) :
|
|
80
|
+
self.pointer += self.parse_custom(custom, source[self.pointer:])
|
|
81
|
+
|
|
82
|
+
else :
|
|
83
|
+
self.pointer += 1
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def is_custom(self, line : str) -> bool:
|
|
87
|
+
for custom in self.customs:
|
|
88
|
+
if custom in line :
|
|
89
|
+
return custom
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def parse_custom(self, custom : str, lines : list[str]):
|
|
94
|
+
pointer = 0
|
|
95
|
+
content = ""
|
|
96
|
+
match custom :
|
|
97
|
+
case "#/file_intro" :
|
|
98
|
+
pointer += 1
|
|
99
|
+
if self.is_oneline_docstring(lines[pointer]) :
|
|
100
|
+
|
|
101
|
+
content = self.format_string(lines[pointer].replace('"""', ""))
|
|
102
|
+
pointer +=1
|
|
103
|
+
|
|
104
|
+
elif self.is_docstring(lines[pointer]) :
|
|
105
|
+
content = self.format_string(lines[pointer].replace('"""', ""))
|
|
106
|
+
pointer +=1
|
|
107
|
+
while not self.is_docstring(lines[pointer]):
|
|
108
|
+
content += self.format_string(lines[pointer].replace('"""', "")).replace("\t", "")
|
|
109
|
+
pointer +=1
|
|
110
|
+
pointer +=1
|
|
111
|
+
|
|
112
|
+
case _ :
|
|
113
|
+
content = lines[pointer].replace(custom, "").replace("\n", "")
|
|
114
|
+
pointer += 1
|
|
115
|
+
obj = Custom_comment(self.customs[custom]["type"], self.customs[custom]["ref"], self.customs[custom]["is_list"], content )
|
|
116
|
+
self.file_data.append(obj)
|
|
117
|
+
return pointer
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def is_class(self, line: str) -> bool:
|
|
121
|
+
"""
|
|
122
|
+
Vérifie si une ligne est une déclaration de classe
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
line (str): ligne à vérifier
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
bool: retourne True si la ligne est une déclaration de classe, False sinon
|
|
129
|
+
"""
|
|
130
|
+
return bool(re.search(r"^class\s.*:", line))
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def is_function(self, lines : list[str], in_class = False) -> bool:
|
|
134
|
+
"""
|
|
135
|
+
Vérifie si la ligne du pointer est une fonction, ou le début d'une fonction dont l'en-tête est sur plusieurs lignes :
|
|
136
|
+
ex :
|
|
137
|
+
|
|
138
|
+
def funct( param1 = "foo", param2 = ("poo", 1)) -> None:
|
|
139
|
+
|
|
140
|
+
ou
|
|
141
|
+
|
|
142
|
+
def funct(
|
|
143
|
+
param1 = "foo",
|
|
144
|
+
param2 = ("poo", 1)
|
|
145
|
+
) -> None:
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
line (list[str]): lignes à vérifier
|
|
149
|
+
in_class (bool, optional): indique si il s'agit d'une méthode ou d'une fonction. Defaults to False.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
bool: retourne True si il s'agit d'une déclaration de fonction, False sinon
|
|
153
|
+
"""
|
|
154
|
+
if not in_class : pat = r"^def\s+[a-zA-Z_]\w*\s*\(.*"
|
|
155
|
+
else : pat = r"^\s+def\s+[a-zA-Z_]\w*\s*\(.*"
|
|
156
|
+
pointer = 0
|
|
157
|
+
|
|
158
|
+
if re.search(pat, lines[0]) :
|
|
159
|
+
opened = lines.count("(") - lines.count(")")
|
|
160
|
+
while opened != 0 and pointer < len(lines):
|
|
161
|
+
pointer += 1
|
|
162
|
+
opened += lines.count("(") - lines.count(")")
|
|
163
|
+
if opened == 0:
|
|
164
|
+
return True
|
|
165
|
+
if not opened and pointer >= len(lines):
|
|
166
|
+
raise IndexError(f"a parenthesis was opened but never closed on line \n {lines[0]}\nFailed to parse the module")
|
|
167
|
+
else :
|
|
168
|
+
return False
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def get_function_declaration(self, lines : list[str]) -> tuple[str, int]:
|
|
172
|
+
"""
|
|
173
|
+
Récupère la déclaration de la fonction et la retoure
|
|
174
|
+
ex :
|
|
175
|
+
|
|
176
|
+
def funct( param1 = "foo", param2 = ("poo", 1)) -> None:
|
|
177
|
+
|
|
178
|
+
ou
|
|
179
|
+
|
|
180
|
+
def funct(
|
|
181
|
+
param1 = "foo",
|
|
182
|
+
param2 = ("poo", 1)
|
|
183
|
+
) -> None:
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
line (list[str]): lignes où se trouvent la déclaration
|
|
187
|
+
in_class (bool, optional): indique si il s'agit d'une méthode ou d'une fonction. Defaults to False.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
str: retourne la déclaration de la fonction sous forme d'un string
|
|
191
|
+
"""
|
|
192
|
+
decla = ""
|
|
193
|
+
pointer = 0
|
|
194
|
+
|
|
195
|
+
opened = lines[pointer].count("(") - lines[pointer].count(")")
|
|
196
|
+
decla = lines[0]
|
|
197
|
+
while opened != 0 and pointer < len(lines):
|
|
198
|
+
pointer += 1
|
|
199
|
+
opened += lines[pointer].count("(") - lines[pointer].count(")")
|
|
200
|
+
decla += lines[pointer]
|
|
201
|
+
if opened == 0:
|
|
202
|
+
return self.format_string(decla), pointer + 1
|
|
203
|
+
if not opened and pointer >= len(lines):
|
|
204
|
+
raise IndexError(f"a parenthesis was opened but never closed on line \n {lines[0]}\nFailed to parse the module")
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def class_parser(self, sub_source : list[str]) -> int:
|
|
208
|
+
"""
|
|
209
|
+
isole la déclaration d'une classe, son docstring éventuel,
|
|
210
|
+
puis parcours le code de la classe à la recherche de méthodes
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
sub_source (list[str]): code source commençant à partir de la déclaration de la classe
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
int: retourne la taille de la classe en nombre de ligne, évite que le parseur principal repasse sur du code déjà parsé
|
|
217
|
+
"""
|
|
218
|
+
obj = Parsed_class(sub_source[0], "")
|
|
219
|
+
pointer = 1
|
|
220
|
+
docstring = ""
|
|
221
|
+
while pointer < len(sub_source) and not self.is_class(sub_source[pointer]) and not self.is_function(sub_source[pointer:]) :
|
|
222
|
+
|
|
223
|
+
if self.is_oneline_docstring(sub_source[pointer]) :
|
|
224
|
+
docstring = self.format_string(sub_source[pointer].replace('"""', ""))
|
|
225
|
+
pointer +=1
|
|
226
|
+
|
|
227
|
+
elif self.is_docstring(sub_source[pointer]) :
|
|
228
|
+
docstring += self.format_string(sub_source[pointer].replace('"""', ""))
|
|
229
|
+
pointer +=1
|
|
230
|
+
while not self.is_docstring(sub_source[pointer]):
|
|
231
|
+
docstring += self.format_string(sub_source[pointer].replace('"""', ""))
|
|
232
|
+
pointer +=1
|
|
233
|
+
pointer +=1
|
|
234
|
+
|
|
235
|
+
elif self.is_function(sub_source[pointer:], in_class=True) :
|
|
236
|
+
print(sub_source[pointer])
|
|
237
|
+
pointer += self.function_parser(sub_source[pointer:], obj)
|
|
238
|
+
|
|
239
|
+
else :
|
|
240
|
+
pointer += 1
|
|
241
|
+
obj.docstring = docstring
|
|
242
|
+
self.parse.append(obj)
|
|
243
|
+
return pointer
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def function_parser(self, sub_source : list[str], parent : Parsed_class = None) -> int:
|
|
247
|
+
"""
|
|
248
|
+
isole la déclaration d'une fonction et récupère son docstring éventuel,
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
sub_source (list[str]): code source commençant à partir de la déclaration de la fonction
|
|
252
|
+
parent (Parsed_class, optional): si le paramètre est fourni, alors function_parser
|
|
253
|
+
considèrera que la fonction à scraper est une méthode appartenant à la classe "parent". Defaults to None.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
int: retourne la taille de la fonction en nombre de ligne, évite que le parseur principal repasse sur du code déjà parsé
|
|
257
|
+
"""
|
|
258
|
+
declaration, pointer = self.get_function_declaration(sub_source)
|
|
259
|
+
docstring = ""
|
|
260
|
+
while pointer < len(sub_source) \
|
|
261
|
+
and not self.is_function(sub_source[pointer:], in_class=True) \
|
|
262
|
+
and not self.is_class(sub_source[pointer]) \
|
|
263
|
+
and not self.is_function(sub_source[pointer:]) :
|
|
264
|
+
|
|
265
|
+
if self.is_oneline_docstring(sub_source[pointer]) :
|
|
266
|
+
|
|
267
|
+
docstring = self.format_string(sub_source[pointer].replace('"""', ""))
|
|
268
|
+
pointer +=1
|
|
269
|
+
|
|
270
|
+
elif self.is_docstring(sub_source[pointer]) :
|
|
271
|
+
docstring += self.format_string(sub_source[pointer].replace('"""', ""))
|
|
272
|
+
pointer +=1
|
|
273
|
+
while not self.is_docstring(sub_source[pointer]):
|
|
274
|
+
docstring += self.format_string(sub_source[pointer].replace('"""', ""))
|
|
275
|
+
pointer +=1
|
|
276
|
+
pointer +=1
|
|
277
|
+
else :
|
|
278
|
+
pointer += 1
|
|
279
|
+
obj = Parsed_function(declaration, docstring)
|
|
280
|
+
if parent :
|
|
281
|
+
parent.add_method(obj)
|
|
282
|
+
else :
|
|
283
|
+
self.parse.append(obj)
|
|
284
|
+
return pointer
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def is_docstring(self, line: str) -> bool:
|
|
288
|
+
"""
|
|
289
|
+
Vérifie si une ligne est le début d'un docstring sur plusieurs lignes
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
line (str): ligne à vérifier
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
bool: retourne True si il s'agit du début d'un docstring, False sinon
|
|
296
|
+
"""
|
|
297
|
+
return bool(re.search(r"(?<!')\"{3}", line))
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def is_oneline_docstring(self, line: str) -> bool:
|
|
301
|
+
"""
|
|
302
|
+
Vérifie si une ligne est une doctring sur une seule ligne, par exemple :
|
|
303
|
+
|
|
304
|
+
'''doctring d'une fonction'''
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
line (str): ligne à vérifier
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
bool: retourne True si il s'agit d'un docstring sur une ligne, False sinon
|
|
311
|
+
"""
|
|
312
|
+
return line.count('"""') == 2
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def format_string(self, string : str) -> str :
|
|
316
|
+
"""
|
|
317
|
+
Formate le string passé en paramètre,
|
|
318
|
+
remplace les chaine de tabs supérieures à 3 par une double tab,
|
|
319
|
+
si aucune tab n'est présente dans la chaine, la fonction en ajoute une en début de chaine
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
string (str): string à traiter
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
str: retourne la chaine traité selon l'algorithme défini plus haut
|
|
326
|
+
"""
|
|
327
|
+
string = re.sub("\s{4}", "\t", string)
|
|
328
|
+
if "\t" in string :
|
|
329
|
+
nb_tab : str = "\t" * string.count("\t")
|
|
330
|
+
|
|
331
|
+
string = string.replace(nb_tab, "\t")
|
|
332
|
+
else :
|
|
333
|
+
string = "\t" + string
|
|
334
|
+
return string
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def get_source(self) -> list[str]:
|
|
338
|
+
"""
|
|
339
|
+
Ouvre et retourne le code source du fichier choisi
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
list[str]: retourne une liste dont chaque élément est une ligne du fichier source à parser
|
|
343
|
+
"""
|
|
344
|
+
with open(self.fpath, 'r', encoding='utf-8') as f:
|
|
345
|
+
return f.readlines()
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
@staticmethod
|
|
349
|
+
def open_custom_config() -> dict:
|
|
350
|
+
with open(files("easydoc.config").joinpath("custom_comment_lines.json"), 'r', encoding='utf-8') as f :
|
|
351
|
+
return json.load(f)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
if __name__ == "__main__" :
|
|
355
|
+
if len(sys.argv) >= 2 :
|
|
356
|
+
path = sys.argv[1]
|
|
357
|
+
Parser(path)
|
|
358
|
+
else :
|
|
359
|
+
raise IndexError("Aucun fichier source fourni, arrêt du programme")
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"#/actual_version" : {"ref" : "%version%", "type" : "Version", "is_list" : false},
|
|
3
|
+
"#/author" : {"ref" : "%author%", "type" : "Author", "is_list" : false},
|
|
4
|
+
"#/creation_date" : {"ref" : "%creation_date%", "type" : "Creation_date", "is_list" : false},
|
|
5
|
+
"#/last_release_date" : {"ref" : "%last_release_date%", "type" : "Last_release_date", "is_list" : false},
|
|
6
|
+
"#/TODO" : {"ref" : "%TODO%", "type" : "TODO", "is_list" : true},
|
|
7
|
+
"#/planned" : {"ref" : "%roadmap%", "type" : "Roadmap", "is_list" : true},
|
|
8
|
+
"#/file_intro" : {"ref" : "%intro%", "type" : "Sum-up", "is_list" : false},
|
|
9
|
+
"#/const" : {"ref" : "%const%", "type" : "Constants", "is_list" : true}
|
|
10
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#-----------------------------------------------------------------------------------------
|
|
2
|
+
# Fichier : analyser.py
|
|
3
|
+
# Version : 1.2
|
|
4
|
+
# Dernier changement : 21/02/2026
|
|
5
|
+
# dernier éditeur : Ywan GERARD
|
|
6
|
+
# Créateur : Ywan GERARD
|
|
7
|
+
#
|
|
8
|
+
#-----------------------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import re
|
|
12
|
+
|
|
13
|
+
from .objects import Parsed_class, Parsed_function, Custom_comment
|
|
14
|
+
import os
|
|
15
|
+
from importlib.resources import files
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MarkdownGenerator:
|
|
19
|
+
|
|
20
|
+
def __init__(self, obj_list, custom_list : list[Custom_comment], fname):
|
|
21
|
+
body : str = self.open_pattern()
|
|
22
|
+
customs = self.open_custom_config()
|
|
23
|
+
custom_done = []
|
|
24
|
+
body = body.replace("%module_name%", fname)
|
|
25
|
+
for elt in custom_list :
|
|
26
|
+
if elt.type_ in custom_done:
|
|
27
|
+
continue
|
|
28
|
+
elif elt.is_list :
|
|
29
|
+
part = self.generate_custom_list(custom_list, elt.type_)
|
|
30
|
+
body = body.replace(elt.ref, part)
|
|
31
|
+
custom_done.append(elt.type_)
|
|
32
|
+
else :
|
|
33
|
+
body = body.replace(elt.ref, f"{self._custom_header_wrap(elt.type_.replace("_"," "))}{elt.content}\n\\")
|
|
34
|
+
|
|
35
|
+
custom_done.append(elt.type_)
|
|
36
|
+
|
|
37
|
+
for elt in customs :
|
|
38
|
+
if customs[elt]["type"] not in custom_done :
|
|
39
|
+
print(customs[elt]["ref"])
|
|
40
|
+
body = body.replace(f"{customs[elt]["ref"]}\n", "")
|
|
41
|
+
|
|
42
|
+
for elt in obj_list :
|
|
43
|
+
if isinstance(elt, Parsed_class):
|
|
44
|
+
body += self.generate_class(elt)
|
|
45
|
+
|
|
46
|
+
for elt in obj_list :
|
|
47
|
+
if isinstance(elt, Parsed_function):
|
|
48
|
+
body += self.generate_function(elt)
|
|
49
|
+
|
|
50
|
+
body = re.sub(r"\\\n[^a-zA-Z]*\n", "\n", body)
|
|
51
|
+
|
|
52
|
+
self.create_file(fname, body)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# --------------- default wrapper functions ------------------
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@staticmethod
|
|
59
|
+
def _class_wrap(name) :
|
|
60
|
+
return f"\n### Classe {name} :\n---\n"
|
|
61
|
+
@staticmethod
|
|
62
|
+
def _method_wrap(name) :
|
|
63
|
+
return f"\n#### **Methode {name} :**\n"
|
|
64
|
+
@staticmethod
|
|
65
|
+
def _function_wrap(name) :
|
|
66
|
+
return f"\n### Fonction {name} :\n"
|
|
67
|
+
@staticmethod
|
|
68
|
+
def _main_name_wrap(name) :
|
|
69
|
+
return f"\n### Fonction {name} :\n"
|
|
70
|
+
@staticmethod
|
|
71
|
+
def _custom_list_wrap(name) :
|
|
72
|
+
return f"\n### {name} :\n"
|
|
73
|
+
@staticmethod
|
|
74
|
+
def _custom_header_wrap(name) :
|
|
75
|
+
return f"**{name}**\n"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# --------------- hook functions ------------------
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
def set_class_wrap(cls, wrapper) :
|
|
83
|
+
cls._class_wrap = wrapper
|
|
84
|
+
return wrapper
|
|
85
|
+
|
|
86
|
+
@classmethod
|
|
87
|
+
def set_method_wrap(cls, wrapper) :
|
|
88
|
+
cls._method_wrap = wrapper
|
|
89
|
+
return wrapper
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def set_function_wrap(cls, wrapper) :
|
|
93
|
+
cls._function_wrap = wrapper
|
|
94
|
+
return wrapper
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def set_main_name_wrap(cls, wrapper) :
|
|
98
|
+
cls._main_name_wrap = wrapper
|
|
99
|
+
return wrapper
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# --------------- files related functions ------------------
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def open_pattern():
|
|
107
|
+
return files("easydoc.templates").joinpath("template.md").read_text()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def open_custom_config() -> dict:
|
|
112
|
+
with open(files("easydoc.config").joinpath("custom_comment_lines.json"), 'r', encoding='utf-8') as f :
|
|
113
|
+
return json.load(f)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
def create_file(name, body):
|
|
118
|
+
with open(f"{os.getcwd()}/{name}_doc.md", 'w', encoding="utf-8") as f:
|
|
119
|
+
return f.write(body)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# --------------- generation functions ------------------
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def generate_custom_list(self, customs : list[Custom_comment], type_):
|
|
126
|
+
md = self._custom_list_wrap(type_)
|
|
127
|
+
elem_list = [cust for cust in customs if cust.type_ == type_]
|
|
128
|
+
for elem in elem_list:
|
|
129
|
+
md += elem.content + "\n\\\n"
|
|
130
|
+
return md
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def generate_class(self, classe : Parsed_class):
|
|
135
|
+
subbody = self._class_wrap(classe.name)
|
|
136
|
+
subbody += f"\nDéclaration :\n\n\t{classe.declaration}"
|
|
137
|
+
subbody += f"\nDescription :\n{classe.docstring}"
|
|
138
|
+
for func in classe.methods:
|
|
139
|
+
subbody += self.generate_function(func, True)
|
|
140
|
+
|
|
141
|
+
return subbody
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def generate_function(self, func : Parsed_function, in_class : bool = False):
|
|
145
|
+
if in_class :
|
|
146
|
+
subbody = self._method_wrap(func.name)
|
|
147
|
+
else :
|
|
148
|
+
subbody = self._function_wrap(func.name)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
subbody += f"\nDéclaration :\n\n{func.declaration}"
|
|
152
|
+
if func.docstring:
|
|
153
|
+
subbody += f"\nDescription :\n\n{func.docstring}"
|
|
154
|
+
|
|
155
|
+
return subbody
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
class Parsed_function:
|
|
4
|
+
|
|
5
|
+
def __init__(self, declaration, docstring):
|
|
6
|
+
self.name : str = ""
|
|
7
|
+
self.declaration : str = ""
|
|
8
|
+
self.set_declaration(declaration)
|
|
9
|
+
self.docstring : str = docstring
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def set_declaration(self, declaration):
|
|
13
|
+
self.declaration = declaration
|
|
14
|
+
self.name = re.search(r"^\s*(?:def|class)\s+([a-zA-Z_]\w*)", declaration).group(1)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Custom_comment:
|
|
18
|
+
|
|
19
|
+
def __init__(self, type_, ref, is_list, content):
|
|
20
|
+
self.type_ : str = type_
|
|
21
|
+
self.ref : str = ref
|
|
22
|
+
self.is_list : bool = is_list
|
|
23
|
+
self.content : str = content
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Parsed_class:
|
|
28
|
+
|
|
29
|
+
def __init__(self, declaration, docstring):
|
|
30
|
+
self.name : str = ""
|
|
31
|
+
self.declaration : str = ""
|
|
32
|
+
self.set_declaration(declaration)
|
|
33
|
+
self.docstring : str = docstring
|
|
34
|
+
self.methods : list[Parsed_function] = []
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def set_declaration(self, declaration):
|
|
38
|
+
self.declaration = declaration
|
|
39
|
+
self.name = re.search(r"^\s*(?:def|class)\s+([a-zA-Z_]\w*)", declaration).group(1)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def add_method(self, method : Parsed_function):
|
|
43
|
+
self.methods.append(method)
|
|
44
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "EasyDocPy"
|
|
3
|
+
version = "1.2.0"
|
|
4
|
+
description = "python documentation generator"
|
|
5
|
+
dependencies = []
|
|
6
|
+
authors = [{ name="epsilonkn" }]
|
|
7
|
+
requires-python = ">=3.12"
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
license = "MIT"
|
|
10
|
+
license-files = ["LICENSE"]
|
|
11
|
+
|
|
12
|
+
[build-system]
|
|
13
|
+
requires = ["setuptools>=61", "wheel"]
|
|
14
|
+
build-backend = "setuptools.build_meta"
|
|
15
|
+
|
|
16
|
+
[project.scripts]
|
|
17
|
+
easydoc = "easydoc.cli:main"
|
|
18
|
+
|
|
19
|
+
[tool.setuptools.package-data]
|
|
20
|
+
"easydoc" = ["templates/*.md", "templates/*.html", "config/*.json"]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://github.com/epsilonkn/EasyDoc"
|
|
24
|
+
Issues = "https://github.com/epsilonkn/EasyDoc/issues"
|