plotlp 0.1.1__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.
plotlp-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,47 @@
1
+ Metadata-Version: 2.3
2
+ Name: plotlp
3
+ Version: 0.1.1
4
+ Summary: A library wrapper around matplotlib for custom plots.
5
+ Requires-Dist: corelp
6
+ Requires-Dist: cycler
7
+ Requires-Dist: ipykernel
8
+ Requires-Dist: matplotlib
9
+ Requires-Dist: pytest
10
+ Requires-Dist: sphinx
11
+ Requires-Dist: sphinx-design
12
+ Requires-Dist: sphinx-rtd-theme
13
+ Requires-Dist: spyder-kernels
14
+ Requires-Dist: toml
15
+ Requires-Python: >=3.12
16
+ Description-Content-Type: text/markdown
17
+
18
+ # plotLP
19
+
20
+ ```text
21
+ Author : Lancelot PINCET
22
+ GitHub : https://github.com/LancelotPincet/plotLP
23
+ HTTPS : https://github.com/LancelotPincet/plotLP.git
24
+ SSH : git@github.com:LancelotPincet/plotLP.git
25
+ PyPI : https://pypi.org/project/plotLP
26
+ Docs : https://plotLP.readthedocs.io
27
+ ```
28
+
29
+ **A library wrapper around matplotlib for custom plots.**
30
+
31
+ plotLP is available on [PyPI](https://pypi.org/project/plotLP) for pip installs.
32
+ For more information, do not hesitate to consult the [Documentation](https://plotLP.readthedocs.io).
33
+
34
+ ---
35
+
36
+ ## MIT License
37
+
38
+ <details>
39
+ <summary>details</summary>
40
+
41
+ Intellectual property behind this Library is protected via an [MIT license](LICENSE). This means everyone can *freely* use it in a personnal, academic or commercial manner, if they **keep the copyright name** at the top of the codes.
42
+
43
+ The library can be redistributed, *with or without modifications*, in open or closed projects. However the **MIT license must be conserved**. For example in a commercial closed project, this means the **copyright and license must be visible somewhere**, like in the documentation or credits.
44
+
45
+ The license also explains that the **code performances are not warrantied**, and you are responsible for how you are using it. For more information on your rights and obligations please refer to [descriptive websites](https://en.wikipedia.org/wiki/MIT_License), or contact author for approvales.
46
+
47
+ </details>
plotlp-0.1.1/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # plotLP
2
+
3
+ ```text
4
+ Author : Lancelot PINCET
5
+ GitHub : https://github.com/LancelotPincet/plotLP
6
+ HTTPS : https://github.com/LancelotPincet/plotLP.git
7
+ SSH : git@github.com:LancelotPincet/plotLP.git
8
+ PyPI : https://pypi.org/project/plotLP
9
+ Docs : https://plotLP.readthedocs.io
10
+ ```
11
+
12
+ **A library wrapper around matplotlib for custom plots.**
13
+
14
+ plotLP is available on [PyPI](https://pypi.org/project/plotLP) for pip installs.
15
+ For more information, do not hesitate to consult the [Documentation](https://plotLP.readthedocs.io).
16
+
17
+ ---
18
+
19
+ ## MIT License
20
+
21
+ <details>
22
+ <summary>details</summary>
23
+
24
+ Intellectual property behind this Library is protected via an [MIT license](LICENSE). This means everyone can *freely* use it in a personnal, academic or commercial manner, if they **keep the copyright name** at the top of the codes.
25
+
26
+ The library can be redistributed, *with or without modifications*, in open or closed projects. However the **MIT license must be conserved**. For example in a commercial closed project, this means the **copyright and license must be visible somewhere**, like in the documentation or credits.
27
+
28
+ The license also explains that the **code performances are not warrantied**, and you are responsible for how you are using it. For more information on your rights and obligations please refer to [descriptive websites](https://en.wikipedia.org/wiki/MIT_License), or contact author for approvales.
29
+
30
+ </details>
@@ -0,0 +1,17 @@
1
+ [project]
2
+ name = "plotlp"
3
+ version = "0.1.1"
4
+ description = "A library wrapper around matplotlib for custom plots."
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [ "corelp", "cycler", "ipykernel", "matplotlib", "pytest", "sphinx", "sphinx-design", "sphinx-rtd-theme", "spyder-kernels", "toml",]
8
+
9
+ [build-system]
10
+ requires = [ "uv_build>=0.8.8,<0.9.0",]
11
+ build-backend = "uv_build"
12
+
13
+ [tool.uv]
14
+ required-environments = [ "sys_platform == 'linux' and platform_machine == 'x86_64'", "sys_platform == 'win32' and (platform_machine == 'AMD64' or platform_machine == 'x86_64')",]
15
+
16
+ [tool.uv.sources.corelp]
17
+ workspace = true
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2025-08-28
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : plotLP
7
+
8
+ """
9
+ A library wrapper around matplotlib for custom plots.
10
+ """
11
+
12
+
13
+
14
+ # %% Lazy imports
15
+ from corelp import getmodule
16
+ __getattr__, __all__ = getmodule(__file__)
17
+
18
+
19
+
20
+ # %% Test function run
21
+ if __name__ == "__main__":
22
+ from corelp import test
23
+ test(__file__)
File without changes
File without changes
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2025-11-17
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : plotLP
7
+ # Module : cmap
8
+
9
+ """
10
+ This module adds custom cmaps to matplotlib.
11
+ """
12
+
13
+
14
+
15
+ # %% Libraries
16
+ from corelp import selfkwargs, prop
17
+ from arrLP import normalize
18
+ from plotlp import color
19
+ from matplotlib.colors import LinearSegmentedColormap, to_rgba_array
20
+ import numpy as np
21
+ from scipy.special import erf,erfinv
22
+
23
+
24
+
25
+ # %% Function
26
+ def cmap(**kwargs) :
27
+ '''
28
+ This module adds custom cmaps to matplotlib.
29
+
30
+ Parameters
31
+ ----------
32
+ a : int or float
33
+ TODO.
34
+
35
+ Returns
36
+ -------
37
+ b : int or float
38
+ TODO.
39
+
40
+ Raises
41
+ ------
42
+ TypeError
43
+ TODO.
44
+
45
+ Examples
46
+ --------
47
+ >>> from plotlp import cmap
48
+ ...
49
+ >>> cmap() # TODO
50
+ '''
51
+
52
+ return None
53
+
54
+
55
+
56
+ class Cmap(LinearSegmentedColormap) :
57
+ name = 'cmapLP'
58
+
59
+ def __init__(self, **kwargs) :
60
+ selfkwargs(self,kwargs)
61
+ r, g, b, a = to_rgba_array(self.colors).T
62
+ nodes = self.nodes
63
+ cdict = {
64
+ "red": np.column_stack([nodes, r, r]),
65
+ "green": np.column_stack([nodes, g, g]),
66
+ "blue": np.column_stack([nodes, b, b]),
67
+ "alpha": np.column_stack([nodes, a, a]),
68
+ }
69
+ super().__init__(self.name,cdict)
70
+
71
+ _black = 'black'
72
+ @prop(variable=True)
73
+ def black(self) -> str :
74
+ if self._black is None :
75
+ return color(rgb=self(self.get_node(0.)))
76
+ return color(auto=self._black)
77
+
78
+ _white = 'white'
79
+ @prop(variable=True)
80
+ def white(self) -> str :
81
+ if self._white is None :
82
+ return color(rgb=self(self.get_node(1.)))
83
+ return color(auto=self._white)
84
+
85
+ _color = None
86
+ @prop(variable=True)
87
+ def color(self) :
88
+ if self._color is None :
89
+ return color(rgb=self(self.get_node(0.5)))
90
+ return color(auto=self._color)
91
+
92
+ _dark = None #dark color
93
+ @prop(variable=True)
94
+ def dark(self) -> str :
95
+ if self._dark is None :
96
+ return color(self(self.get_node(0.25)))
97
+ return color(self._dark)
98
+
99
+ _light = None #light color
100
+ @prop(variable=True)
101
+ def light(self) -> str :
102
+ if self._light is None :
103
+ return color(self(self.get_node(0.75)))
104
+ return color(self._light)
105
+
106
+ _colors = None #list of colors
107
+ @prop()
108
+ def colors(self) -> str :
109
+ return [self.black,self.dark,self.color,self.light,self.white]
110
+ @colors.setter
111
+ def colors(self, value) -> float :
112
+ self._colors = [color(auto=color) for color in value]
113
+ @property
114
+ def ncolors(self) :
115
+ return len(self.colors)
116
+
117
+ _nodes = None #nodes corresponding to colors
118
+ @prop()
119
+ def nodes(self) -> str :
120
+ nodes = np.linspace(0.,1.,self.ncolors)
121
+ return self.get_node(nodes)
122
+ nodebase = 0 #Base defining distribution of colors around center based on erf function, when approching 0 the function tends towards linear distribution
123
+ def get_node(self,node) :
124
+ if self.nodebase == 0 : return node
125
+ node = (erfinv(normalize(node,-erf(self.nodebase),erf(self.nodebase),offset=0,norm=1))/self.nodebase+1)/2
126
+ return (np.round(node * 1000)).astype(int)/1000
127
+
128
+
129
+ # %% Test function run
130
+ if __name__ == "__main__":
131
+ from corelp import test
132
+ test(__file__)
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2025-11-17
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : plotLP
7
+ # Module : cmap
8
+
9
+ """
10
+ This file allows to test cmap
11
+
12
+ cmap : This module adds custom cmaps to matplotlib.
13
+ """
14
+
15
+
16
+
17
+ # %% Libraries
18
+ from corelp import print, debug
19
+ import pytest
20
+ from plotlp import cmap
21
+ debug_folder = debug(__file__)
22
+
23
+
24
+
25
+ # %% Function test
26
+ def test_function() :
27
+ '''
28
+ Test cmap function
29
+ '''
30
+ print('Hello world!')
31
+
32
+
33
+
34
+ # %% Instance fixture
35
+ @pytest.fixture()
36
+ def instance() :
37
+ '''
38
+ Create a new instance at each test function
39
+ '''
40
+ return cmap()
41
+
42
+ def test_instance(instance) :
43
+ '''
44
+ Test on fixture
45
+ '''
46
+ pass
47
+
48
+
49
+ # %% Returns test
50
+ @pytest.mark.parametrize("args, kwargs, expected, message", [
51
+ #([], {}, None, ""),
52
+ ([], {}, None, ""),
53
+ ])
54
+ def test_returns(args, kwargs, expected, message) :
55
+ '''
56
+ Test cmap return values
57
+ '''
58
+ assert cmap(*args, **kwargs) == expected, message
59
+
60
+
61
+
62
+ # %% Error test
63
+ @pytest.mark.parametrize("args, kwargs, error, error_message", [
64
+ #([], {}, None, ""),
65
+ ([], {}, None, ""),
66
+ ])
67
+ def test_errors(args, kwargs, error, error_message) :
68
+ '''
69
+ Test cmap error values
70
+ '''
71
+ with pytest.raises(error, match=error_message) :
72
+ cmap(*args, **kwargs)
73
+
74
+
75
+
76
+ # %% Test function run
77
+ if __name__ == "__main__":
78
+ from corelp import test
79
+ test(__file__)
File without changes
@@ -0,0 +1,316 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2025-11-16
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : plotLP
7
+ # Module : color
8
+
9
+ """
10
+ Gets a custom Color object.
11
+ """
12
+
13
+
14
+
15
+ # %% Libraries
16
+ from dataclasses import dataclass
17
+ import matplotlib.colors as mcolors
18
+
19
+
20
+
21
+ colors = {
22
+
23
+ #Greys
24
+ 'xxdarkgreyLP' : "#1A1A1AFF",
25
+ 'xdarkgreyLP' : "#404040FF",
26
+ 'darkgreyLP' : "#606060FF",
27
+ 'greyLP' : "#808080FF",
28
+ 'lightgreyLP' : "#9F9F9FFF",
29
+ 'xlightgreyLP' : "#BFBFBFFF",
30
+ 'xxlightgreyLP' : "#E6E6E6FF",
31
+
32
+ #Blue
33
+ 'darkblueLP' : "#1F4B5BFF",
34
+ 'blueLP' : "#2F7089FF",
35
+ 'lightblueLP' : "#598DA1FF",
36
+ 'xlightblueLP' : "#82A9B8FF",
37
+ 'xxlightblueLP' : "#ACC6D0FF",
38
+
39
+ #Green
40
+ 'xxdarkgreenLP' : "#294C3AFF",
41
+ 'xdarkgreenLP' : "#3E725BFF",
42
+ 'darkgreenLP' : "#529878FF",
43
+ 'greenLP' : "#68BE95FF",
44
+ 'lightgreenLP' : "#9AD4B8FF",
45
+
46
+ #Red
47
+ 'darkredLP' : "#8F1E20FF",
48
+ 'redLP' : "#D52D2FFF",
49
+ 'lightredLP' : "#DE5758FF",
50
+ 'xlightredLP' : "#E68181FF",
51
+ 'xxlightredLP' : "#EFABADFF",
52
+
53
+ #Yellow
54
+ 'xxdarkyellowLP' : "#4D4212FF",
55
+ 'xdarkyellowLP' : "#746317FF",
56
+ 'darkyellowLP' : "#9A8420FF",
57
+ 'yellowLP' : "#C1A529FF",
58
+ 'lightyellowLP' : "#D6C36FFF",
59
+
60
+ #Cold
61
+ 'xdarkcoldLP' : "#264C46FF",
62
+ 'darkcoldLP' : "#39716CFF",
63
+ 'coldLP' : "#4C978FFF",
64
+ 'lightcoldLP' : "#78B1AAFF",
65
+ 'xlightcoldLP' : "#D3E5E4FF",
66
+
67
+ #Warm
68
+ 'xdarkwarmLP' : "#653517FF",
69
+ 'darkwarmLP' : "#994F20FF",
70
+ 'warmLP' : "#CB692CFF",
71
+ 'lightwarmLP' : "#D88F60FF",
72
+ 'xlightwarmLP' : "#E6B495FF",
73
+
74
+ }
75
+ mcolors.CSS4_COLORS.update(colors)
76
+ mcolors._colors_full_map.update(colors)
77
+
78
+
79
+
80
+ # %% Function
81
+ def color(*, name:str=None, hex:str=None, wl:float=None, gray:int=None, rgb:tuple=None, RGB:tuple=None, auto=None, alpha:float=None) :
82
+ '''
83
+ Gets a custom Color object.
84
+
85
+ Parameters
86
+ ----------
87
+ name : str
88
+ Name of color.
89
+ hex : str
90
+ hexadecimal string.
91
+ wl : str
92
+ wavelength [nm] float.
93
+ gray : str
94
+ gray luminosity [0-255].
95
+ rgb : tuple
96
+ rgb(a) tuple [0.-1.].
97
+ RGB : tuple
98
+ RGB(A) tuple [0-255].
99
+ auto : str or tuple or int or float
100
+ Defines what is asked automatically.
101
+
102
+
103
+ Returns
104
+ -------
105
+ instance : Color
106
+ Color object instance.
107
+
108
+ Examples
109
+ --------
110
+ >>> from plotlp import color
111
+ ...
112
+ >>> color(name="blue") # from plt
113
+ >>> color(name="blueLP") # from custom
114
+ >>> color(hex="#2F7089") # from hexadecimals
115
+ >>> color(wl=480) # from wavelenght
116
+ >>> color(gray=180) # from grayscale
117
+ >>> color(rgb=(0, 0, 255)) # from RGB
118
+ ...
119
+ >>> blued, greened = color(name="blueLP")
120
+ >>> desaturated = blued.desaturate(0.5) # 1=completely gray
121
+ >>> dark = blued.luminosity(-0.5) # <0 darker down to -1
122
+ >>> light = blued.luminosity(+0.5) # >0 lighter up to +1
123
+ >>> transparent = blued * 0.5 # color * alpha
124
+ >>> opaque = +blued
125
+ >>> invisible = -blued
126
+ >>> mixed = blued.mix(greened, 0.5) # 50/50 mixing blue/green
127
+ >>> complementary = ~blued # complementary color
128
+ '''
129
+
130
+ # Name
131
+ if name is not None :
132
+ r, g, b = mcolors.to_rgb(name)
133
+ return color(rgb=(r*255, g*255, b*255), alpha=alpha)
134
+
135
+ # Hexadecimals
136
+ if hex is not None :
137
+ if hex.startswith('#') :
138
+ hex = hex[1:]
139
+ _alpha = int(hex[6:8], 16)/255 if len(hex)==8 else None
140
+ alpha = _alpha if alpha is None else alpha
141
+ return color(rgb=(int(hex[0:2],16), int(hex[2:4],16), int(hex[4:6], 16)), alpha=alpha)
142
+
143
+ # Wavelength
144
+ if wl is not None :
145
+ gamma = 0.8
146
+ wl = min(max(float(wl),380.),750.) #Wavelength is put to borders
147
+ if wl >= 380 and wl <= 440:
148
+ attenuation = 0.3 + 0.7 * (wl - 380) / (440 - 380)
149
+ r, g, b = ((-(wl - 440) / (440 - 380)) * attenuation) ** gamma, 0.0, (1.0 * attenuation) ** gamma
150
+ elif wl >= 440 and wl <= 490:
151
+ r, g, b = 0.0, ((wl - 440) / (490 - 440)) ** gamma, 1.0
152
+ elif wl >= 490 and wl <= 510:
153
+ r, g, b = 0.0, 1.0, (-(wl - 510) / (510 - 490)) ** gamma
154
+ elif wl >= 510 and wl <= 580:
155
+ r, g, b = ((wl - 510) / (580 - 510)) ** gamma, 1.0, 0.0
156
+ elif wl >= 580 and wl <= 645:
157
+ r, g, b = 1.0, (-(wl - 645) / (645 - 580)) ** gamma, 0.0
158
+ elif wl >= 645 and wl <= 750:
159
+ attenuation = 0.3 + 0.7 * (750 - wl) / (750 - 645)
160
+ r, g, b = (1.0 * attenuation) ** gamma, 0.0, 0.0
161
+ else:
162
+ r, g, b = 0.0, 0.0, 0.0
163
+ return color(rgb=(r*255, g*255, b*255), alpha=alpha)
164
+
165
+ # gray
166
+ if gray is not None :
167
+ return color(rgb=(gray, gray, gray), alpha=alpha)
168
+
169
+ # rgb
170
+ if rgb is not None :
171
+ _alpha = rgb[3] if len(rgb)==4 else 1.
172
+ alpha = _alpha if alpha is None else alpha
173
+ return Color(rgb[0]*255, rgb[1]*255, rgb[2]*255, alpha=alpha)
174
+
175
+ # RGB
176
+ if RGB is not None :
177
+ _alpha = RGB[3]/255 if len(RGB)==4 else 1.
178
+ alpha = _alpha if alpha is None else alpha
179
+ return Color(RGB[0], RGB[1], RGB[2], alpha=alpha)
180
+
181
+ # Auto
182
+ if auto is not None :
183
+ if isinstance(auto, str) :
184
+ if auto.startswith('#') : return color(hex=auto, alpha=alpha)
185
+ else : return color(name=auto, alpha=alpha)
186
+ elif isinstance(auto, tuple) or isinstance(auto, list) :
187
+ if max(*auto) <= 1 : return color(rgb=auto, alpha=alpha)
188
+ else : return color(RGB=auto, alpha=alpha)
189
+ else : # int/float
190
+ if auto <= 255 : return color(gray=auto, alpha=alpha)
191
+ else : return color(wl=auto, alpha=alpha)
192
+
193
+ raise SyntaxError('No valid input was asked for color')
194
+
195
+
196
+
197
+ # %% Class
198
+ @dataclass(slots=True, frozen=True)
199
+ class Color(str) :
200
+ '''
201
+ Gets a custom Color object.
202
+
203
+ Parameters
204
+ ----------
205
+ R : float
206
+ Red value [0-255].
207
+ G : float
208
+ Green value [0-255].
209
+ B : float
210
+ Blue value [0-255].
211
+ alpha : float
212
+ Alpha value [0-1] (0: transparent, 1: opaque).
213
+
214
+ Attributes
215
+ ----------
216
+ _attr : int or float
217
+ TODO.
218
+
219
+ Examples
220
+ --------
221
+ >>> from plotlp import color
222
+ ...
223
+ >>> instance = color(TODO)
224
+ '''
225
+
226
+ # Attributes
227
+ R : int
228
+ G : int
229
+ B : int
230
+ alpha : float = 1.
231
+
232
+
233
+
234
+ # new
235
+ def __new__(cls, R, G, B, alpha=1) :
236
+ hex = '#{:02x}{:02x}{:02x}{:02x}'.format(int(R), int(G), int(B), int(alpha*255)).upper()
237
+ instance = str.__new__(cls, hex)
238
+ object.__setattr__(instance, 'R', int(R))
239
+ object.__setattr__(instance, 'G', int(G))
240
+ object.__setattr__(instance, 'B', int(B))
241
+ object.__setattr__(instance, 'alpha', float(alpha))
242
+ return instance
243
+
244
+ # RGBA
245
+ @property
246
+ def RGB(self) :
247
+ return (self.R, self.G, self.B)
248
+ @property
249
+ def RGBA(self) :
250
+ return (self.R, self.G, self.B, int(self.alpha * 255))
251
+
252
+ # rgba
253
+ @property
254
+ def r(self) :
255
+ return self.R / 255
256
+ @property
257
+ def g(self) :
258
+ return self.G / 255
259
+ @property
260
+ def b(self) :
261
+ return self.B / 255
262
+ @property
263
+ def a(self) :
264
+ return self.A / 255
265
+ @property
266
+ def rgb(self) :
267
+ return (self.r, self.g, self.b)
268
+ @property
269
+ def rgba(self) :
270
+ return (self.r, self.g, self.b, self.a)
271
+
272
+ # greys
273
+ @property
274
+ def K(self) :
275
+ return int(0.299 * self.R + 0.587 * self.G + 0.114 * self.B)
276
+ @property
277
+ def k(self) :
278
+ return self.K / 255
279
+
280
+
281
+
282
+ #Complementary color
283
+ def __invert__(self) : # ~color
284
+ return Color(255-self.R, 255-self.G, 255-self.B, alpha=self.alpha)
285
+
286
+ #Play on transparency
287
+ def __mul__(self, alpha) : # color * alpha
288
+ return Color(*self.RGB, alpha=alpha)
289
+ def __pos__(self) : # +color
290
+ return Color(self.RGB, alpha=1)
291
+ def __neg__(self) : # -color
292
+ return Color(self.RGB, alpha=0)
293
+
294
+ # color mixing
295
+ def mix(self, other_color, other_fact=0.5) :
296
+ self_fact = 1 - other_fact
297
+ R = self.R * self_fact + other_color.R * other_fact
298
+ G = self.G * self_fact + other_color.G * other_fact
299
+ B = self.B * self_fact + other_color.B * other_fact
300
+ return Color(R, G, B, alpha=self.alpha)
301
+ #Desaturation
302
+ def desaturate(self, fact) : # desat [0-1]
303
+ gray = Color(self.K, self.K, self.K)
304
+ return self.mix(gray, fact)
305
+ #luminosity
306
+ def luminosity(self, fact) : # desat [-1, +1]
307
+ k = int(fact>=0) * 255
308
+ lum = Color(k, k, k)
309
+ return self.mix(lum, abs(fact))
310
+
311
+
312
+
313
+ # %% Test function run
314
+ if __name__ == "__main__":
315
+ from corelp import test
316
+ test(__file__)