Coverage for src/prosemark/adapters/binder_scaffold.py: 100%

34 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-09-24 18:08 +0000

1# Copyright (c) 2024 Prosemark Contributors 

2# This software is licensed under the MIT License 

3 

4"""Binder scaffold generator for new prosemark projects. 

5 

6This module provides functionality to generate initial _binder.md files 

7for new prosemark projects with proper structure, format, and example content. 

8""" 

9 

10from contextlib import suppress 

11from pathlib import Path 

12 

13from prosemark.exceptions import FileSystemError, ProsemarkFileExistsError 

14 

15 

16def generate_binder_scaffold(target_path: Path, *, create_dirs: bool = False) -> None: 

17 """Generate a scaffold _binder.md file for a new prosemark project. 

18 

19 Creates a _binder.md file with proper structure including managed block markers, 

20 example content, and helpful documentation for users. The file includes HTML 

21 comment markers that define the managed section for prosemark to maintain 

22 automatically. 

23 

24 Args: 

25 target_path: Directory where the _binder.md file should be created 

26 create_dirs: If True, create parent directories if they don't exist 

27 

28 Raises: 

29 ProsemarkFileExistsError: If _binder.md already exists in the target directory 

30 FileSystemError: If the file cannot be created due to I/O errors, 

31 permission issues, or missing parent directories 

32 

33 Examples: 

34 >>> # Basic usage 

35 >>> generate_binder_scaffold(Path('/path/to/project')) 

36 

37 >>> # With directory creation 

38 >>> generate_binder_scaffold(Path('/path/to/new/project'), create_dirs=True) 

39 

40 """ 

41 # Validate and prepare target directory 

42 if create_dirs: 

43 try: 

44 target_path.mkdir(parents=True, exist_ok=True) 

45 except OSError as exc: 

46 msg = 'Cannot create target directory' 

47 raise FileSystemError(msg, str(target_path)) from exc 

48 elif not target_path.exists(): 

49 msg = 'Target directory does not exist' 

50 raise FileSystemError(msg, str(target_path)) 

51 elif not target_path.is_dir(): 

52 msg = 'Target path is not a directory' 

53 raise FileSystemError(msg, str(target_path)) 

54 

55 # Check for existing binder file 

56 binder_file = target_path / '_binder.md' 

57 try: 

58 if binder_file.exists(): 

59 msg = 'Binder file already exists' 

60 raise ProsemarkFileExistsError(msg, str(binder_file)) 

61 except (OSError, PermissionError) as exc: 

62 # If we can't even check if the file exists due to permissions, we can't write either 

63 msg = 'Cannot access target directory' 

64 raise FileSystemError(msg, str(target_path)) from exc 

65 

66 # Generate scaffold content 

67 content = _generate_scaffold_content() 

68 

69 # Write the file atomically 

70 try: 

71 # Write to temporary file first, then rename for atomicity 

72 temp_file = binder_file.with_suffix('.tmp') 

73 temp_file.write_text(content, encoding='utf-8') 

74 temp_file.rename(binder_file) 

75 except OSError as exc: 

76 # Clean up temporary file if it exists 

77 if temp_file.exists(): # pragma: no cover 

78 with suppress(OSError): 

79 temp_file.unlink() 

80 

81 msg = 'Cannot write binder file' 

82 raise FileSystemError(msg, str(binder_file)) from exc 

83 

84 

85def _generate_scaffold_content() -> str: 

86 """Generate the content for the scaffold _binder.md file. 

87 

88 Creates the complete content including documentation, managed block 

89 markers, and example content that demonstrates proper binder format. 

90 

91 Returns: 

92 Complete content string for the _binder.md file 

93 

94 """ 

95 return """# Binder 

96 

97Welcome to your new prosemark project! This file serves as your project outline and table of contents. 

98 

99You can write notes, introductions, and other content anywhere in this file. 

100Only the section between the special markers below is managed by prosemark. 

101 

102## Binder (managed by Prosemark) 

103<!-- pmk:begin-binder --> 

104- [Sample Chapter](01234567.md) 

105- [New Placeholder]() 

106<!-- pmk:end-binder --> 

107 

108The managed section above will be automatically updated as you add, move, and remove nodes. 

109"""