@alloy-js/python 0.3.0-dev.5 → 0.3.0

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.
Files changed (54) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/src/components/FutureStatement.d.ts +35 -0
  3. package/dist/src/components/FutureStatement.d.ts.map +1 -0
  4. package/dist/src/components/FutureStatement.js +31 -0
  5. package/dist/src/components/FutureStatement.js.map +1 -0
  6. package/dist/src/components/SourceFile.d.ts +8 -2
  7. package/dist/src/components/SourceFile.d.ts.map +1 -1
  8. package/dist/src/components/SourceFile.js +124 -8
  9. package/dist/src/components/SourceFile.js.map +1 -1
  10. package/dist/src/components/index.d.ts +1 -0
  11. package/dist/src/components/index.d.ts.map +1 -1
  12. package/dist/src/components/index.js +1 -0
  13. package/dist/src/components/index.js.map +1 -1
  14. package/dist/test/classdeclarations.test.js +2 -0
  15. package/dist/test/classdeclarations.test.js.map +1 -1
  16. package/dist/test/dataclassdeclarations.test.js +13 -0
  17. package/dist/test/dataclassdeclarations.test.js.map +1 -1
  18. package/dist/test/enums.test.js +7 -0
  19. package/dist/test/enums.test.js.map +1 -1
  20. package/dist/test/externals.test.js +2 -0
  21. package/dist/test/externals.test.js.map +1 -1
  22. package/dist/test/functiondeclaration.test.js +2 -0
  23. package/dist/test/functiondeclaration.test.js.map +1 -1
  24. package/dist/test/methoddeclaration.test.js +1 -0
  25. package/dist/test/methoddeclaration.test.js.map +1 -1
  26. package/dist/test/namepolicies.test.js +1 -0
  27. package/dist/test/namepolicies.test.js.map +1 -1
  28. package/dist/test/propertydeclaration.test.js +1 -0
  29. package/dist/test/propertydeclaration.test.js.map +1 -1
  30. package/dist/test/pydocs.test.js +1 -1
  31. package/dist/test/pydocs.test.js.map +1 -1
  32. package/dist/test/sourcefiles.test.js +921 -39
  33. package/dist/test/sourcefiles.test.js.map +1 -1
  34. package/dist/test/utils.d.ts +2 -2
  35. package/dist/test/utils.d.ts.map +1 -1
  36. package/dist/test/utils.js +9 -9
  37. package/dist/test/utils.js.map +1 -1
  38. package/dist/tsconfig.tsbuildinfo +1 -1
  39. package/package.json +6 -6
  40. package/src/components/FutureStatement.tsx +38 -0
  41. package/src/components/SourceFile.tsx +128 -8
  42. package/src/components/index.ts +1 -0
  43. package/temp/api.json +122 -2
  44. package/test/classdeclarations.test.tsx +2 -0
  45. package/test/dataclassdeclarations.test.tsx +13 -0
  46. package/test/enums.test.tsx +7 -0
  47. package/test/externals.test.tsx +2 -0
  48. package/test/functiondeclaration.test.tsx +2 -0
  49. package/test/methoddeclaration.test.tsx +1 -0
  50. package/test/namepolicies.test.tsx +1 -0
  51. package/test/propertydeclaration.test.tsx +1 -0
  52. package/test/pydocs.test.tsx +1 -1
  53. package/test/sourcefiles.test.tsx +754 -48
  54. package/test/utils.tsx +10 -5
@@ -2,11 +2,8 @@ import { Prose } from "@alloy-js/core";
2
2
  import { d } from "@alloy-js/core/testing";
3
3
  import { expect, it } from "vitest";
4
4
  import * as py from "../src/index.js";
5
- import {
6
- assertFileContents,
7
- toSourceText,
8
- toSourceTextMultiple,
9
- } from "./utils.jsx";
5
+ import { dataclassesModule } from "../src/index.js";
6
+ import { toSourceText } from "./utils.jsx";
10
7
 
11
8
  /**
12
9
  * toSourceText wraps the children in a SourceFile component
@@ -14,11 +11,10 @@ import {
14
11
  */
15
12
  it("renders an empty source file", () => {
16
13
  const result = toSourceText([]);
17
- const expected = d`
14
+ expect(result).toRenderTo(d`
18
15
 
19
16
 
20
- `;
21
- expect(result).toRenderTo(expected);
17
+ `);
22
18
  });
23
19
 
24
20
  it("correct formatting of source file", () => {
@@ -99,7 +95,7 @@ it("correct formatting of source file", () => {
99
95
  <py.MemberExpression.Part key={"special-prop"} />
100
96
  </py.MemberExpression>,
101
97
  ]);
102
- const expected = d`
98
+ expect(result).toRenderTo(d`
103
99
  class SomeClass:
104
100
  def some_method() -> str:
105
101
  x: int = 42
@@ -131,8 +127,7 @@ it("correct formatting of source file", () => {
131
127
 
132
128
  a.b["special-prop"]
133
129
 
134
- `;
135
- expect(result).toRenderTo(expected);
130
+ `);
136
131
  });
137
132
 
138
133
  it("renders module documentation correctly", () => {
@@ -162,69 +157,780 @@ it("renders module documentation correctly", () => {
162
157
  );
163
158
 
164
159
  const content = (
165
- <py.SourceFile path="utils.py" doc={moduleDoc}>
160
+ <py.SourceFile path="test.py" doc={moduleDoc}>
166
161
  <py.VariableDeclaration name="DEFAULT_TIMEOUT" initializer={30} />
167
162
  <py.VariableDeclaration name="MAX_RETRIES" initializer={3} />
168
163
  <py.FunctionDeclaration name="process_data">pass</py.FunctionDeclaration>
169
164
  </py.SourceFile>
170
165
  );
171
166
 
172
- const res = toSourceTextMultiple([content]);
173
- const file = res.contents.find(
174
- (f) => f.kind === "file" && f.path === "utils.py",
175
- );
176
- expect(file).toBeDefined();
177
-
178
- assertFileContents(res, {
179
- "utils.py": d`
180
- """
181
- This module provides utility functions for data processing. It includes
182
- functions for validation, transformation, and analysis.
183
-
184
- Attributes:
185
- DEFAULT_TIMEOUT (int): Default timeout value in seconds.
167
+ expect(toSourceText(content)).toRenderTo(d`
168
+ """
169
+ This module provides utility functions for data processing. It includes
170
+ functions for validation, transformation, and analysis.
186
171
 
187
- MAX_RETRIES (int): Maximum number of retry attempts.
172
+ Attributes:
173
+ DEFAULT_TIMEOUT (int): Default timeout value in seconds.
188
174
 
189
- Todo:
190
- * Add caching functionality
191
- * Improve error messages
192
- """
175
+ MAX_RETRIES (int): Maximum number of retry attempts.
193
176
 
177
+ Todo:
178
+ * Add caching functionality
179
+ * Improve error messages
180
+ """
194
181
 
195
- default_timeout = 30
182
+ default_timeout = 30
196
183
 
197
- max_retries = 3
184
+ max_retries = 3
198
185
 
199
- def process_data():
200
- pass
186
+ def process_data():
187
+ pass
201
188
 
202
189
 
203
- `,
204
- });
190
+ `);
205
191
  });
206
192
 
207
193
  it("renders source file without documentation correctly", () => {
208
194
  const content = (
209
- <py.SourceFile path="simple.py">
195
+ <py.SourceFile path="test.py">
210
196
  <py.FunctionDeclaration name="hello_world">
211
197
  print("Hello, World!")
212
198
  </py.FunctionDeclaration>
213
199
  </py.SourceFile>
214
200
  );
215
201
 
216
- const res = toSourceTextMultiple([content]);
217
- const file = res.contents.find(
218
- (f) => f.kind === "file" && f.path === "simple.py",
202
+ expect(toSourceText(content)).toRenderTo(d`
203
+ def hello_world():
204
+ print("Hello, World!")
205
+
206
+
207
+ `);
208
+ });
209
+
210
+ it("nothing before top-level definition", () => {
211
+ const content = (
212
+ <py.SourceFile path="test.py">
213
+ <py.FunctionDeclaration name="hello">pass</py.FunctionDeclaration>
214
+ </py.SourceFile>
215
+ );
216
+
217
+ expect(toSourceText(content)).toRenderTo(d`
218
+ def hello():
219
+ pass
220
+
221
+
222
+ `);
223
+ });
224
+
225
+ it("nothing before non-definition", () => {
226
+ const content = (
227
+ <py.SourceFile path="test.py">
228
+ <py.VariableDeclaration name="x" initializer={42} />
229
+ </py.SourceFile>
230
+ );
231
+
232
+ expect(toSourceText(content)).toRenderTo("x = 42");
233
+ });
234
+
235
+ it("only doc before definition", () => {
236
+ const moduleDoc = (
237
+ <py.ModuleDoc description={[<Prose>Module description.</Prose>]} />
238
+ );
239
+
240
+ const content = (
241
+ <py.SourceFile path="test.py" doc={moduleDoc}>
242
+ <py.FunctionDeclaration name="hello">pass</py.FunctionDeclaration>
243
+ </py.SourceFile>
244
+ );
245
+
246
+ expect(toSourceText(content)).toRenderTo(d`
247
+ """
248
+ Module description.
249
+ """
250
+
251
+
252
+ def hello():
253
+ pass
254
+
255
+
256
+ `);
257
+ });
258
+
259
+ it("only doc before non-definition", () => {
260
+ const moduleDoc = (
261
+ <py.ModuleDoc description={[<Prose>Module description.</Prose>]} />
262
+ );
263
+
264
+ const content = (
265
+ <py.SourceFile path="test.py" doc={moduleDoc}>
266
+ <py.VariableDeclaration name="x" initializer={42} />
267
+ </py.SourceFile>
268
+ );
269
+
270
+ expect(toSourceText(content)).toRenderTo(d`
271
+ """
272
+ Module description.
273
+ """
274
+
275
+ x = 42`);
276
+ });
277
+
278
+ it("only header before definition", () => {
279
+ const content = (
280
+ <py.SourceFile path="test.py" header="#!/usr/bin/env python3">
281
+ <py.FunctionDeclaration name="hello">pass</py.FunctionDeclaration>
282
+ </py.SourceFile>
283
+ );
284
+
285
+ // 2 blank lines before definition (PEP 8)
286
+ expect(toSourceText(content)).toRenderTo(d`
287
+ #!/usr/bin/env python3
288
+
289
+
290
+ def hello():
291
+ pass
292
+
293
+
294
+ `);
295
+ });
296
+
297
+ it("only header before non-definition", () => {
298
+ const content = (
299
+ <py.SourceFile path="test.py" header="#!/usr/bin/env python3">
300
+ <py.VariableDeclaration name="x" initializer={42} />
301
+ </py.SourceFile>
302
+ );
303
+
304
+ // 1 blank line for non-definition
305
+ expect(toSourceText(content)).toRenderTo(d`
306
+ #!/usr/bin/env python3
307
+
308
+ x = 42`);
309
+ });
310
+
311
+ it("only futureImports before definition", () => {
312
+ const content = (
313
+ <py.SourceFile
314
+ path="test.py"
315
+ futureImports={<py.FutureStatement feature="annotations" />}
316
+ >
317
+ <py.FunctionDeclaration name="hello">pass</py.FunctionDeclaration>
318
+ </py.SourceFile>
319
+ );
320
+
321
+ expect(toSourceText(content)).toRenderTo(d`
322
+ from __future__ import annotations
323
+
324
+
325
+ def hello():
326
+ pass
327
+
328
+
329
+ `);
330
+ });
331
+
332
+ it("only futureImports before non-definition", () => {
333
+ const content = (
334
+ <py.SourceFile
335
+ path="test.py"
336
+ futureImports={<py.FutureStatement feature="annotations" />}
337
+ >
338
+ <py.VariableDeclaration name="x" initializer={42} />
339
+ </py.SourceFile>
340
+ );
341
+
342
+ expect(toSourceText(content)).toRenderTo(d`
343
+ from __future__ import annotations
344
+
345
+ x = 42`);
346
+ });
347
+
348
+ it("only imports before definition", () => {
349
+ const content = (
350
+ <py.SourceFile path="test.py">
351
+ <py.DataclassDeclaration name="User">
352
+ <py.VariableDeclaration name="name" type="str" />
353
+ </py.DataclassDeclaration>
354
+ </py.SourceFile>
355
+ );
356
+
357
+ expect(toSourceText(content, { externals: [dataclassesModule] }))
358
+ .toRenderTo(d`
359
+ from dataclasses import dataclass
360
+
361
+
362
+ @dataclass
363
+ class User:
364
+ name: str = None
365
+
366
+
367
+ `);
368
+ });
369
+
370
+ it("only imports before non-definition", () => {
371
+ const content = (
372
+ <py.SourceFile path="test.py">
373
+ <py.VariableDeclaration
374
+ name="x"
375
+ initializer={<py.Reference refkey={dataclassesModule["."].dataclass} />}
376
+ />
377
+ </py.SourceFile>
378
+ );
379
+
380
+ expect(toSourceText(content, { externals: [dataclassesModule] }))
381
+ .toRenderTo(d`
382
+ from dataclasses import dataclass
383
+
384
+ x = dataclass`);
385
+ });
386
+
387
+ it("doc + futureImports before definition", () => {
388
+ const moduleDoc = (
389
+ <py.ModuleDoc description={[<Prose>Module description.</Prose>]} />
390
+ );
391
+
392
+ const content = (
393
+ <py.SourceFile
394
+ path="test.py"
395
+ doc={moduleDoc}
396
+ futureImports={<py.FutureStatement feature="annotations" />}
397
+ >
398
+ <py.FunctionDeclaration name="hello">pass</py.FunctionDeclaration>
399
+ </py.SourceFile>
400
+ );
401
+
402
+ expect(toSourceText(content)).toRenderTo(d`
403
+ """
404
+ Module description.
405
+ """
406
+
407
+ from __future__ import annotations
408
+
409
+
410
+ def hello():
411
+ pass
412
+
413
+
414
+ `);
415
+ });
416
+
417
+ it("doc + futureImports before non-definition", () => {
418
+ const moduleDoc = (
419
+ <py.ModuleDoc description={[<Prose>Module description.</Prose>]} />
420
+ );
421
+
422
+ const content = (
423
+ <py.SourceFile
424
+ path="test.py"
425
+ doc={moduleDoc}
426
+ futureImports={<py.FutureStatement feature="annotations" />}
427
+ >
428
+ <py.VariableDeclaration name="x" initializer={42} />
429
+ </py.SourceFile>
430
+ );
431
+
432
+ expect(toSourceText(content)).toRenderTo(d`
433
+ """
434
+ Module description.
435
+ """
436
+
437
+ from __future__ import annotations
438
+
439
+ x = 42`);
440
+ });
441
+
442
+ it("doc + imports before definition", () => {
443
+ const moduleDoc = (
444
+ <py.ModuleDoc description={[<Prose>Module description.</Prose>]} />
445
+ );
446
+
447
+ const content = (
448
+ <py.SourceFile path="test.py" doc={moduleDoc}>
449
+ <py.DataclassDeclaration name="User">
450
+ <py.VariableDeclaration name="name" type="str" />
451
+ </py.DataclassDeclaration>
452
+ </py.SourceFile>
453
+ );
454
+
455
+ expect(toSourceText(content, { externals: [dataclassesModule] }))
456
+ .toRenderTo(d`
457
+ """
458
+ Module description.
459
+ """
460
+
461
+ from dataclasses import dataclass
462
+
463
+
464
+ @dataclass
465
+ class User:
466
+ name: str = None
467
+
468
+
469
+ `);
470
+ });
471
+
472
+ it("doc + imports before non-definition", () => {
473
+ const moduleDoc = (
474
+ <py.ModuleDoc description={[<Prose>Module description.</Prose>]} />
475
+ );
476
+
477
+ const content = (
478
+ <py.SourceFile path="test.py" doc={moduleDoc}>
479
+ <py.VariableDeclaration
480
+ name="x"
481
+ initializer={<py.Reference refkey={dataclassesModule["."].dataclass} />}
482
+ />
483
+ </py.SourceFile>
484
+ );
485
+
486
+ expect(toSourceText(content, { externals: [dataclassesModule] }))
487
+ .toRenderTo(d`
488
+ """
489
+ Module description.
490
+ """
491
+
492
+ from dataclasses import dataclass
493
+
494
+ x = dataclass`);
495
+ });
496
+
497
+ it("futureImports + imports before definition", () => {
498
+ const content = (
499
+ <py.SourceFile
500
+ path="test.py"
501
+ futureImports={<py.FutureStatement feature="annotations" />}
502
+ >
503
+ <py.DataclassDeclaration name="User">
504
+ <py.VariableDeclaration name="name" type="str" />
505
+ </py.DataclassDeclaration>
506
+ </py.SourceFile>
507
+ );
508
+
509
+ expect(toSourceText(content, { externals: [dataclassesModule] }))
510
+ .toRenderTo(d`
511
+ from __future__ import annotations
512
+
513
+ from dataclasses import dataclass
514
+
515
+
516
+ @dataclass
517
+ class User:
518
+ name: str = None
519
+
520
+
521
+ `);
522
+ });
523
+
524
+ it("futureImports + imports before non-definition", () => {
525
+ const content = (
526
+ <py.SourceFile
527
+ path="test.py"
528
+ futureImports={<py.FutureStatement feature="annotations" />}
529
+ >
530
+ <py.VariableDeclaration
531
+ name="x"
532
+ initializer={<py.Reference refkey={dataclassesModule["."].dataclass} />}
533
+ />
534
+ </py.SourceFile>
535
+ );
536
+
537
+ expect(toSourceText(content, { externals: [dataclassesModule] }))
538
+ .toRenderTo(d`
539
+ from __future__ import annotations
540
+
541
+ from dataclasses import dataclass
542
+
543
+ x = dataclass`);
544
+ });
545
+
546
+ it("doc + futureImports + imports before definition", () => {
547
+ const moduleDoc = (
548
+ <py.ModuleDoc description={[<Prose>Module description.</Prose>]} />
549
+ );
550
+
551
+ const content = (
552
+ <py.SourceFile
553
+ path="test.py"
554
+ doc={moduleDoc}
555
+ futureImports={<py.FutureStatement feature="annotations" />}
556
+ >
557
+ <py.DataclassDeclaration name="User">
558
+ <py.VariableDeclaration name="name" type="str" />
559
+ </py.DataclassDeclaration>
560
+ </py.SourceFile>
561
+ );
562
+
563
+ expect(toSourceText(content, { externals: [dataclassesModule] }))
564
+ .toRenderTo(d`
565
+ """
566
+ Module description.
567
+ """
568
+
569
+ from __future__ import annotations
570
+
571
+ from dataclasses import dataclass
572
+
573
+
574
+ @dataclass
575
+ class User:
576
+ name: str = None
577
+
578
+
579
+ `);
580
+ });
581
+
582
+ it("doc + futureImports + imports before non-definition", () => {
583
+ const moduleDoc = (
584
+ <py.ModuleDoc description={[<Prose>Module description.</Prose>]} />
585
+ );
586
+
587
+ const content = (
588
+ <py.SourceFile
589
+ path="test.py"
590
+ doc={moduleDoc}
591
+ futureImports={<py.FutureStatement feature="annotations" />}
592
+ >
593
+ <py.VariableDeclaration
594
+ name="x"
595
+ initializer={<py.Reference refkey={dataclassesModule["."].dataclass} />}
596
+ />
597
+ </py.SourceFile>
598
+ );
599
+
600
+ expect(toSourceText(content, { externals: [dataclassesModule] }))
601
+ .toRenderTo(d`
602
+ """
603
+ Module description.
604
+ """
605
+
606
+ from __future__ import annotations
607
+
608
+ from dataclasses import dataclass
609
+
610
+ x = dataclass`);
611
+ });
612
+
613
+ it("only doc in file (no children)", () => {
614
+ const moduleDoc = (
615
+ <py.ModuleDoc description={[<Prose>Module description.</Prose>]} />
616
+ );
617
+
618
+ const content = <py.SourceFile path="test.py" doc={moduleDoc} />;
619
+
620
+ expect(toSourceText(content)).toRenderTo(d`
621
+ """
622
+ Module description.
623
+ """
624
+
625
+
626
+ `);
627
+ });
628
+
629
+ it("only header in file (no children)", () => {
630
+ const content = (
631
+ <py.SourceFile path="test.py" header="#!/usr/bin/env python3" />
632
+ );
633
+
634
+ expect(toSourceText(content)).toRenderTo(d`
635
+ #!/usr/bin/env python3
636
+
637
+
638
+ `);
639
+ });
640
+
641
+ it("only futureImports in file (no children)", () => {
642
+ const content = (
643
+ <py.SourceFile
644
+ path="test.py"
645
+ futureImports={<py.FutureStatement feature="annotations" />}
646
+ />
647
+ );
648
+
649
+ expect(toSourceText(content)).toRenderTo(d`
650
+ from __future__ import annotations
651
+
652
+ `);
653
+ });
654
+
655
+ it("doc + futureImports in file (no children)", () => {
656
+ const moduleDoc = (
657
+ <py.ModuleDoc description={[<Prose>Module description.</Prose>]} />
658
+ );
659
+
660
+ const content = (
661
+ <py.SourceFile
662
+ path="test.py"
663
+ doc={moduleDoc}
664
+ futureImports={<py.FutureStatement feature="annotations" />}
665
+ />
666
+ );
667
+
668
+ expect(toSourceText(content)).toRenderTo(d`
669
+ """
670
+ Module description.
671
+ """
672
+
673
+ from __future__ import annotations
674
+
675
+ `);
676
+ });
677
+
678
+ // headerComment tests
679
+ it("only headerComment before definition", () => {
680
+ const content = (
681
+ <py.SourceFile path="test.py" headerComment="Copyright 2024 My Company">
682
+ <py.FunctionDeclaration name="hello">pass</py.FunctionDeclaration>
683
+ </py.SourceFile>
684
+ );
685
+
686
+ // 2 blank lines before definition (PEP 8)
687
+ expect(toSourceText(content)).toRenderTo(d`
688
+ # Copyright 2024 My Company
689
+
690
+
691
+ def hello():
692
+ pass
693
+
694
+
695
+ `);
696
+ });
697
+
698
+ it("only headerComment before non-definition", () => {
699
+ const content = (
700
+ <py.SourceFile path="test.py" headerComment="Copyright 2024 My Company">
701
+ <py.VariableDeclaration name="x" initializer={42} />
702
+ </py.SourceFile>
219
703
  );
220
- expect(file).toBeDefined();
221
704
 
222
- assertFileContents(res, {
223
- "simple.py": d`
224
- def hello_world():
225
- print("Hello, World!")
705
+ expect(toSourceText(content)).toRenderTo(d`
706
+ # Copyright 2024 My Company
707
+
708
+ x = 42`);
709
+ });
710
+
711
+ it("headerComment + doc before definition", () => {
712
+ const moduleDoc = (
713
+ <py.ModuleDoc description={[<Prose>Module description.</Prose>]} />
714
+ );
715
+
716
+ const content = (
717
+ <py.SourceFile
718
+ path="test.py"
719
+ headerComment="Copyright 2024 My Company"
720
+ doc={moduleDoc}
721
+ >
722
+ <py.FunctionDeclaration name="hello">pass</py.FunctionDeclaration>
723
+ </py.SourceFile>
724
+ );
725
+
726
+ // headerComment and doc adjacent, then PEP 8 spacing before definition
727
+ expect(toSourceText(content)).toRenderTo(d`
728
+ # Copyright 2024 My Company
729
+ """
730
+ Module description.
731
+ """
732
+
733
+
734
+ def hello():
735
+ pass
736
+
737
+
738
+ `);
739
+ });
740
+
741
+ it("headerComment + futureImports before definition", () => {
742
+ const content = (
743
+ <py.SourceFile
744
+ path="test.py"
745
+ headerComment="Copyright 2024 My Company"
746
+ futureImports={<py.FutureStatement feature="annotations" />}
747
+ >
748
+ <py.FunctionDeclaration name="hello">pass</py.FunctionDeclaration>
749
+ </py.SourceFile>
750
+ );
751
+
752
+ // headerComment and futureImports adjacent, then PEP 8 spacing
753
+ expect(toSourceText(content)).toRenderTo(d`
754
+ # Copyright 2024 My Company
755
+
756
+ from __future__ import annotations
757
+
758
+
759
+ def hello():
760
+ pass
761
+
762
+
763
+ `);
764
+ });
765
+
766
+ it("headerComment + imports before definition", () => {
767
+ const content = (
768
+ <py.SourceFile path="test.py" headerComment="Copyright 2024 My Company">
769
+ <py.DataclassDeclaration name="User">
770
+ <py.VariableDeclaration name="name" type="str" />
771
+ </py.DataclassDeclaration>
772
+ </py.SourceFile>
773
+ );
774
+
775
+ expect(toSourceText(content, { externals: [dataclassesModule] }))
776
+ .toRenderTo(d`
777
+ # Copyright 2024 My Company
778
+
779
+ from dataclasses import dataclass
780
+
781
+
782
+ @dataclass
783
+ class User:
784
+ name: str = None
785
+
786
+
787
+ `);
788
+ });
789
+
790
+ it("headerComment + doc + futureImports before definition", () => {
791
+ const moduleDoc = (
792
+ <py.ModuleDoc description={[<Prose>Module description.</Prose>]} />
793
+ );
794
+
795
+ const content = (
796
+ <py.SourceFile
797
+ path="test.py"
798
+ headerComment="Copyright 2024 My Company"
799
+ doc={moduleDoc}
800
+ futureImports={<py.FutureStatement feature="annotations" />}
801
+ >
802
+ <py.FunctionDeclaration name="hello">pass</py.FunctionDeclaration>
803
+ </py.SourceFile>
804
+ );
805
+
806
+ expect(toSourceText(content)).toRenderTo(d`
807
+ # Copyright 2024 My Company
808
+ """
809
+ Module description.
810
+ """
811
+
812
+ from __future__ import annotations
813
+
814
+
815
+ def hello():
816
+ pass
817
+
818
+
819
+ `);
820
+ });
821
+
822
+ it("headerComment + doc + futureImports + imports before definition", () => {
823
+ const moduleDoc = (
824
+ <py.ModuleDoc description={[<Prose>Module description.</Prose>]} />
825
+ );
826
+
827
+ const content = (
828
+ <py.SourceFile
829
+ path="test.py"
830
+ headerComment="Copyright 2024 My Company"
831
+ doc={moduleDoc}
832
+ futureImports={<py.FutureStatement feature="annotations" />}
833
+ >
834
+ <py.DataclassDeclaration name="User">
835
+ <py.VariableDeclaration name="name" type="str" />
836
+ </py.DataclassDeclaration>
837
+ </py.SourceFile>
838
+ );
839
+
840
+ expect(toSourceText(content, { externals: [dataclassesModule] }))
841
+ .toRenderTo(d`
842
+ # Copyright 2024 My Company
843
+ """
844
+ Module description.
845
+ """
846
+
847
+ from __future__ import annotations
848
+
849
+ from dataclasses import dataclass
850
+
851
+
852
+ @dataclass
853
+ class User:
854
+ name: str = None
855
+
856
+
857
+ `);
858
+ });
859
+
860
+ it("only headerComment in file (no children)", () => {
861
+ const content = (
862
+ <py.SourceFile path="test.py" headerComment="Copyright 2024 My Company" />
863
+ );
864
+
865
+ expect(toSourceText(content)).toRenderTo(d`
866
+ # Copyright 2024 My Company
867
+
868
+ `);
869
+ });
870
+
871
+ it("header + headerComment before definition", () => {
872
+ const content = (
873
+ <py.SourceFile
874
+ path="test.py"
875
+ header="#!/usr/bin/env python3"
876
+ headerComment="Copyright 2024 My Company"
877
+ >
878
+ <py.FunctionDeclaration name="hello">pass</py.FunctionDeclaration>
879
+ </py.SourceFile>
880
+ );
881
+
882
+ // 2 blank lines before definition (PEP 8)
883
+ expect(toSourceText(content)).toRenderTo(d`
884
+ #!/usr/bin/env python3
885
+ # Copyright 2024 My Company
886
+
887
+
888
+ def hello():
889
+ pass
890
+
891
+
892
+ `);
893
+ });
894
+
895
+ it("header + headerComment before class definition", () => {
896
+ const content = (
897
+ <py.SourceFile
898
+ path="test.py"
899
+ header="#!/usr/bin/env python3"
900
+ headerComment="Copyright 2024 My Company"
901
+ >
902
+ <py.ClassDeclaration name="MyClass">pass</py.ClassDeclaration>
903
+ </py.SourceFile>
904
+ );
905
+
906
+ // 2 blank lines before definition (PEP 8)
907
+ expect(toSourceText(content)).toRenderTo(d`
908
+ #!/usr/bin/env python3
909
+ # Copyright 2024 My Company
910
+
911
+
912
+ class MyClass:
913
+ pass
914
+
915
+
916
+ `);
917
+ });
918
+
919
+ it("header + headerComment before non-definition", () => {
920
+ const content = (
921
+ <py.SourceFile
922
+ path="test.py"
923
+ header="#!/usr/bin/env python3"
924
+ headerComment="Copyright 2024 My Company"
925
+ >
926
+ <py.VariableDeclaration name="x" initializer={42} />
927
+ </py.SourceFile>
928
+ );
226
929
 
930
+ // 1 blank line for non-definition
931
+ expect(toSourceText(content)).toRenderTo(d`
932
+ #!/usr/bin/env python3
933
+ # Copyright 2024 My Company
227
934
 
228
- `,
229
- });
935
+ x = 42`);
230
936
  });