knit-graphs 0.0.10__py3-none-any.whl → 0.0.12__py3-none-any.whl

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.
docs/source/conf.py CHANGED
@@ -1,11 +1,11 @@
1
1
  """
2
- Configuration file for the Sphinx documentation builder.
3
- =============================================================================
4
- SPHINX DOCUMENTATION CONFIGURATION
5
- =============================================================================
6
- This file configures how Sphinx generates documentation from your Python code.
7
- For the full list of built-in configuration values, see:
8
- https://www.sphinx-doc.org/en/master/usage/configuration.html
2
+ Configuration file for the Sphinx documentation builder.
3
+ =============================================================================
4
+ SPHINX DOCUMENTATION CONFIGURATION
5
+ =============================================================================
6
+ This file configures how Sphinx generates documentation from your Python code.
7
+ For the full list of built-in configuration values, see:
8
+ https://www.sphinx-doc.org/en/master/usage/configuration.html
9
9
  """
10
10
 
11
11
  import os
@@ -19,17 +19,17 @@ from importlib.metadata import PackageNotFoundError, version
19
19
  # Add the project root and source directory to Python path so Sphinx can import your modules
20
20
 
21
21
  # Path to your source code (adjust if not using src/ layout)
22
- sys.path.insert(0, os.path.abspath('..')) # Project root
23
- sys.path.insert(0, os.path.abspath('../src')) # Source directory
24
- sys.path.insert(0, os.path.abspath('.')) # Docs directory
22
+ sys.path.insert(0, os.path.abspath("..")) # Project root
23
+ sys.path.insert(0, os.path.abspath("../src")) # Source directory
24
+ sys.path.insert(0, os.path.abspath(".")) # Docs directory
25
25
 
26
26
  # =============================================================================
27
27
  # PROJECT INFORMATION
28
28
  # =============================================================================
29
29
 
30
- project = 'knit-graphs'
31
- copyright = f'{datetime.now().year}, Megan Hofmann'
32
- author = 'Megan Hofmann'
30
+ project = "knit-graphs"
31
+ copyright = f"{datetime.now().year}, Megan Hofmann"
32
+ author = "Megan Hofmann"
33
33
 
34
34
  try:
35
35
  # Get version from installed package metadata
@@ -49,19 +49,17 @@ release = version
49
49
  # Extensions to enable (these add functionality to Sphinx)
50
50
  extensions = [
51
51
  # Core Sphinx extensions
52
- 'sphinx.ext.autodoc', # Generate docs from docstrings
53
- 'sphinx.ext.autosummary', # Generate summary tables automatically
54
- 'sphinx.ext.viewcode', # Add [source] links to documentation
55
- 'sphinx.ext.napoleon', # Support Google/NumPy docstring styles
56
- 'sphinx.ext.intersphinx', # Link to other project docs (e.g., Python docs)
57
- 'sphinx.ext.githubpages', # Publish to GitHub Pages (.nojekyll file)
58
- 'sphinx.ext.todo', # Support TODO items in docs
59
- 'sphinx.ext.coverage', # Check documentation coverage
60
- 'sphinx.ext.doctest', # Test code snippets in documentation
61
-
52
+ "sphinx.ext.autodoc", # Generate docs from docstrings
53
+ "sphinx.ext.autosummary", # Generate summary tables automatically
54
+ "sphinx.ext.viewcode", # Add [source] links to documentation
55
+ "sphinx.ext.napoleon", # Support Google/NumPy docstring styles
56
+ "sphinx.ext.intersphinx", # Link to other project docs (e.g., Python docs)
57
+ "sphinx.ext.githubpages", # Publish to GitHub Pages (.nojekyll file)
58
+ "sphinx.ext.todo", # Support TODO items in docs
59
+ "sphinx.ext.coverage", # Check documentation coverage
60
+ "sphinx.ext.doctest", # Test code snippets in documentation
62
61
  # Third-party extensions (these need to be installed)
63
- 'sphinx_autodoc_typehints', # Better type hint rendering
64
- 'myst_parser', # Support for Markdown files (optional)
62
+ "myst_parser", # Support for Markdown files (optional)
65
63
  ]
66
64
 
67
65
  # =============================================================================
@@ -70,19 +68,19 @@ extensions = [
70
68
 
71
69
  # File extensions that Sphinx will process
72
70
  source_suffix = {
73
- '.rst': None, # RestructuredText (default)
74
- '.md': 'myst_parser', # Markdown (requires myst_parser extension)
71
+ ".rst": None, # RestructuredText (default)
72
+ ".md": "myst_parser", # Markdown (requires myst_parser extension)
75
73
  }
76
74
 
77
75
  # The master toctree document (main page)
78
- master_doc = 'index'
76
+ master_doc = "index"
79
77
 
80
78
  # Files and directories to exclude from processing
81
79
  exclude_patterns = [
82
- '_build', # Build output directory
83
- 'Thumbs.db', # Windows thumbnail cache
84
- '.DS_Store', # macOS metadata
85
- '**.ipynb_checkpoints', # Jupyter notebook checkpoints
80
+ "_build", # Build output directory
81
+ "Thumbs.db", # Windows thumbnail cache
82
+ ".DS_Store", # macOS metadata
83
+ "**.ipynb_checkpoints", # Jupyter notebook checkpoints
86
84
  ]
87
85
 
88
86
  # =============================================================================
@@ -90,31 +88,29 @@ exclude_patterns = [
90
88
  # =============================================================================
91
89
 
92
90
  # The theme to use for HTML pages
93
- html_theme = 'sphinx_rtd_theme' # Read the Docs theme (clean, professional)
91
+ html_theme = "sphinx_rtd_theme" # Read the Docs theme (clean, professional)
94
92
 
95
93
  # Directories containing static files (CSS, JS, images)
96
- html_static_path = ['_static']
94
+ html_static_path = ["_static"]
97
95
 
98
96
 
99
97
  # Theme-specific options
100
98
  html_theme_options = {
101
- 'logo_only': False, # Show project name with logo
102
- 'display_version': True, # Show version in sidebar
103
- 'prev_next_buttons_location': 'bottom', # Navigation button placement
104
- 'style_external_links': True, # Style external links differently
105
- 'vcs_pageview_mode': '', # Version control integration
106
- 'style_nav_header_background': '#2980B9', # Header background color
107
-
99
+ "logo_only": False, # Show project name with logo
100
+ "prev_next_buttons_location": "bottom", # Navigation button placement
101
+ "style_external_links": True, # Style external links differently
102
+ "vcs_pageview_mode": "", # Version control integration
103
+ "style_nav_header_background": "#2980B9", # Header background color
108
104
  # Table of contents options
109
- 'collapse_navigation': True, # Collapse subsections in nav
110
- 'sticky_navigation': True, # Keep navigation visible on scroll
111
- 'navigation_depth': 4, # Maximum navigation depth
112
- 'includehidden': True, # Include hidden toctrees
113
- 'titles_only': False, # Show subsection titles in nav
105
+ "collapse_navigation": True, # Collapse subsections in nav
106
+ "sticky_navigation": True, # Keep navigation visible on scroll
107
+ "navigation_depth": 4, # Maximum navigation depth
108
+ "includehidden": True, # Include hidden toctrees
109
+ "titles_only": False, # Show subsection titles in nav
114
110
  }
115
111
 
116
112
  # Additional HTML options
117
- html_title = f'{project} Documentation' # Browser window title
113
+ html_title = f"{project} Documentation" # Browser window title
118
114
  html_short_title = project # Short title for navigation
119
115
 
120
116
 
@@ -136,20 +132,20 @@ html_short_title = project # Short title for navigation
136
132
 
137
133
  # Default options for all autodoc directives
138
134
  autodoc_default_options = {
139
- 'members': True, # Include all members
140
- 'member-order': 'bysource', # Order members as they appear in source
141
- 'special-members': '__init__', # Include __init__ methods
142
- 'undoc-members': True, # Include members without docstrings
143
- 'exclude-members': '__weakref__', # Exclude certain members
144
- 'show-inheritance': True, # Show class inheritance
145
- 'inherited-members': True, # Include inherited methods
135
+ "members": True, # Include all members
136
+ "member-order": "bysource", # Order members as they appear in source
137
+ "special-members": "__init__", # Include __init__ methods
138
+ "undoc-members": True, # Include members without docstrings
139
+ "exclude-members": "__weakref__", # Exclude certain members
140
+ "show-inheritance": True, # Show class inheritance
141
+ "inherited-members": True, # Include inherited methods
146
142
  }
147
143
 
148
144
  # How to display class signatures
149
145
  autodoc_class_signature = "mixed" # Show __init__ parameters with class
150
146
 
151
147
  # Order of members in documentation
152
- autodoc_member_order = 'bysource' # bysource, alphabetical, or groupwise
148
+ autodoc_member_order = "bysource" # bysource, alphabetical, or groupwise
153
149
 
154
150
  # Mock imports for modules that might not be available during doc building
155
151
  # Add any modules that cause import errors during doc building
@@ -159,6 +155,10 @@ autodoc_mock_imports = [
159
155
  # 'some_optional_dependency',
160
156
  ]
161
157
 
158
+ # Enable type hints in signatures
159
+ autodoc_typehints = "signature" # Show type hints in function signatures
160
+ autodoc_typehints_description_target = "documented" # Where to show type info
161
+
162
162
  # =============================================================================
163
163
  # AUTOSUMMARY CONFIGURATION
164
164
  # =============================================================================
@@ -193,13 +193,13 @@ napoleon_attr_annotations = True # Include attribute annotations
193
193
  # Links to external documentation
194
194
 
195
195
  intersphinx_mapping = {
196
- 'python': ('https://docs.python.org/3', None),
197
- 'numpy': ('https://numpy.org/doc/stable/', None),
198
- 'pandas': ('https://pandas.pydata.org/pandas-docs/stable/', None),
199
- 'matplotlib': ('https://matplotlib.org/stable/', None),
200
- 'scipy': ('https://docs.scipy.org/doc/scipy/', None),
201
- 'sklearn': ('https://scikit-learn.org/stable/', None),
202
- 'typing': ('https://typing.readthedocs.io/en/latest/', None),
196
+ "python": ("https://docs.python.org/3", None),
197
+ "numpy": ("https://numpy.org/doc/stable/", None),
198
+ "pandas": ("https://pandas.pydata.org/pandas-docs/stable/", None),
199
+ "matplotlib": ("https://matplotlib.org/stable/", None),
200
+ "scipy": ("https://docs.scipy.org/doc/scipy/", None),
201
+ "sklearn": ("https://scikit-learn.org/stable/", None),
202
+ "typing": ("https://typing.readthedocs.io/en/latest/", None),
203
203
  }
204
204
 
205
205
  # =============================================================================
@@ -207,10 +207,8 @@ intersphinx_mapping = {
207
207
  # =============================================================================
208
208
  # Controls how type hints are displayed in documentation
209
209
 
210
- typehints_fully_qualified = False # Use short names for types
210
+ # Note: These settings work with sphinx.ext.autodoc built-in type hint support
211
211
  always_document_param_types = True # Always show parameter types
212
- typehints_document_rtype = True # Document return types
213
- typehints_use_rtype = True # Use :rtype: directive for return types
214
212
 
215
213
  # =============================================================================
216
214
  # TODO EXTENSION CONFIGURATION
@@ -245,16 +243,17 @@ add_module_names = False # Don't prepend module names to functions
245
243
  show_authors = False # Don't show author info by default
246
244
 
247
245
  # Syntax highlighting style
248
- pygments_style = 'sphinx' # Code highlighting theme
246
+ pygments_style = "sphinx" # Code highlighting theme
249
247
 
250
248
  # Language for content that doesn't specify a language
251
- language = 'en'
249
+ language = "en"
252
250
 
253
251
 
254
252
  # =============================================================================
255
253
  # CUSTOM FUNCTIONS AND SETUP
256
254
  # =============================================================================
257
255
 
256
+
258
257
  def autodoc_skip_member(app, what, name, obj, skip, options):
259
258
  """
260
259
  Custom function to control which members are included in documentation.
@@ -288,15 +287,16 @@ def setup(app):
288
287
  app: The Sphinx application instance
289
288
  """
290
289
  # Connect custom functions to Sphinx events
291
- app.connect('autodoc-skip-member', autodoc_skip_member)
290
+ app.connect("autodoc-skip-member", autodoc_skip_member)
292
291
 
293
292
  # Return extension metadata
294
293
  return {
295
- 'version': version,
296
- 'parallel_read_safe': True,
297
- 'parallel_write_safe': True,
294
+ "version": version,
295
+ "parallel_read_safe": True,
296
+ "parallel_write_safe": True,
298
297
  }
299
298
 
299
+
300
300
  # =============================================================================
301
301
  # CHECKLIST FOR SPHINX SETUP
302
302
  # =============================================================================
docs/source/index.rst CHANGED
@@ -3,10 +3,6 @@ knit-graphs
3
3
 
4
4
  A graph representation of knitted structures where each loop is a node and edges represent yarn and stitch relationships.
5
5
 
6
- .. image:: https://img.shields.io/github/workflow/status/mhofmann-Khoury/knit-graphs/CI
7
- :target: https://github.com/mhofmann-Khoury/knit-graphs/actions
8
- :alt: Build Status
9
-
10
6
  .. image:: https://img.shields.io/pypi/v/knit-graphs
11
7
  :target: https://pypi.org/project/knit-graphs/
12
8
  :alt: PyPI Version
knit_graphs/Course.py CHANGED
@@ -2,35 +2,48 @@
2
2
 
3
3
  This module contains the Course class which represents a horizontal row of loops in a knitting pattern.
4
4
  """
5
+
5
6
  from __future__ import annotations
6
7
 
7
- from typing import TYPE_CHECKING, Iterator, cast
8
+ from collections.abc import Iterator, Sequence
9
+ from typing import TYPE_CHECKING, Generic, TypeVar, overload
8
10
 
9
11
  from knit_graphs.Loop import Loop
10
12
 
11
13
  if TYPE_CHECKING:
12
14
  from knit_graphs.Knit_Graph import Knit_Graph
13
15
 
16
+ LoopT = TypeVar("LoopT", bound=Loop)
17
+
14
18
 
15
- class Course:
19
+ class Course(Sequence[LoopT], Generic[LoopT]):
16
20
  """Course object for organizing loops into knitting rows.
17
21
 
18
22
  A Course represents a horizontal row of loops in a knitting pattern.
19
23
  It maintains an ordered list of loops and provides methods for analyzing the structure and relationships between courses in the knitted fabric.
20
24
  """
21
25
 
22
- def __init__(self, knit_graph: Knit_Graph) -> None:
26
+ def __init__(self, course_number: int, knit_graph: Knit_Graph[LoopT]) -> None:
23
27
  """Initialize an empty course associated with a knit graph.
24
28
 
25
29
  Args:
26
30
  knit_graph (Knit_Graph): The knit graph that this course belongs to.
27
31
  """
28
- self._knit_graph: Knit_Graph = knit_graph
29
- self._loops_in_order: list[Loop] = []
30
- self._loop_set: set[Loop] = set()
32
+ self._course_number: int = course_number
33
+ self._knit_graph: Knit_Graph[LoopT] = knit_graph
34
+ self._loops_in_order: list[LoopT] = []
35
+ self._loop_set: set[LoopT] = set()
36
+
37
+ @property
38
+ def course_number(self) -> int:
39
+ """
40
+ Returns:
41
+ int: The course number of the course.
42
+ """
43
+ return self._course_number
31
44
 
32
45
  @property
33
- def loops_in_order(self) -> list[Loop]:
46
+ def loops_in_order(self) -> list[LoopT]:
34
47
  """
35
48
  Returns:
36
49
  list[Loop]: The list of loops in this course.
@@ -38,22 +51,34 @@ class Course:
38
51
  return self._loops_in_order
39
52
 
40
53
  @property
41
- def knit_graph(self) -> Knit_Graph:
54
+ def loop_ids_in_course(self) -> list[int]:
55
+ """
56
+ Returns:
57
+ list[int]: The loop ids in the course in the order the occur in the course.
58
+ """
59
+ return [l.loop_id for l in self.loops_in_order]
60
+
61
+ @property
62
+ def knit_graph(self) -> Knit_Graph[LoopT]:
42
63
  """
43
64
  Returns:
44
65
  Knit_Graph: The knit graph that this course belongs to.
45
66
  """
46
67
  return self._knit_graph
47
68
 
48
- def add_loop(self, loop: Loop, index: int | None = None) -> None:
69
+ def add_loop(self, loop: LoopT, index: int | None = None) -> None:
49
70
  """Add a loop to the course at the specified index or at the end.
50
71
 
51
72
  Args:
52
73
  loop (Loop): The loop to add to this course.
53
74
  index (int | None, optional): The index position to insert the loop at. If None, appends to the end.
75
+
76
+ Raises:
77
+ ValueError: If the loop is a parent to any loops already in the course.
54
78
  """
55
79
  for parent_loop in loop.parent_loops:
56
- assert parent_loop not in self, f"{loop} has parent {parent_loop}, cannot be added to same course"
80
+ if parent_loop in self:
81
+ raise ValueError(f"{loop} has parent {parent_loop}, cannot be added to same course")
57
82
  self._loop_set.add(loop)
58
83
  if index is None:
59
84
  self.loops_in_order.append(loop)
@@ -66,7 +91,7 @@ class Course:
66
91
  Returns:
67
92
  bool: True if the course has at least one yarn over (loop with no parent loops) to start new wales.
68
93
  """
69
- return any(not loop.has_parent_loops() for loop in self)
94
+ return any(not loop.has_parent_loops for loop in self)
70
95
 
71
96
  def has_decrease(self) -> bool:
72
97
  """Check if this course contains any decrease stitches that merge wales.
@@ -76,18 +101,7 @@ class Course:
76
101
  """
77
102
  return any(len(loop.parent_loops) > 1 for loop in self)
78
103
 
79
- def __getitem__(self, index: int | slice) -> Loop | list[Loop]:
80
- """Get loop(s) at the specified index or slice.
81
-
82
- Args:
83
- index (int | slice): The index or slice to retrieve from the course.
84
-
85
- Returns:
86
- Loop | list[Loop]: The loop at the specified index, or list of loops for a slice.
87
- """
88
- return self.loops_in_order[index]
89
-
90
- def in_round_with(self, next_course: Course) -> bool:
104
+ def in_round_with(self, next_course: Course[LoopT]) -> bool:
91
105
  """Check if the next course connects to this course in a circular pattern.
92
106
 
93
107
  This method determines if the courses are connected in the round (circular knitting) by checking if the next course starts at the beginning of this course.
@@ -98,14 +112,14 @@ class Course:
98
112
  Returns:
99
113
  bool: True if the next course starts at the beginning of this course, indicating circular knitting.
100
114
  """
101
- next_start: Loop = cast(Loop, next_course[0])
115
+ next_start = next_course[0]
102
116
  i = 1
103
- while not next_start.has_parent_loops():
104
- next_start = cast(Loop, next_course[i])
117
+ while not next_start.has_parent_loops:
118
+ next_start = next_course[i]
105
119
  i += 1
106
120
  return self[0] in next_start.parent_loops
107
121
 
108
- def in_row_with(self, next_course: Course) -> bool:
122
+ def in_row_with(self, next_course: Course[LoopT]) -> bool:
109
123
  """Check if the next course connects to this course in a flat/row pattern.
110
124
 
111
125
  This method determines if the courses are connected in flat knitting (back and forth) by checking if the next course starts at the end of this course.
@@ -116,39 +130,48 @@ class Course:
116
130
  Returns:
117
131
  bool: True if the next course starts at the end of this course, indicating flat/row knitting.
118
132
  """
119
- next_start: Loop = cast(Loop, next_course[0])
133
+ next_start: LoopT = next_course[0]
120
134
  i = 1
121
- while not next_start.has_parent_loops():
122
- next_start = cast(Loop, next_course[i])
135
+ while not next_start.has_parent_loops:
136
+ next_start = next_course[i]
123
137
  i += 1
124
138
  return self[-1] in next_start.parent_loops
125
139
 
126
- def __contains__(self, loop: Loop) -> bool:
140
+ def __contains__(self, loop: object) -> bool:
127
141
  """Check if a loop is contained in this course.
128
142
 
129
143
  Args:
130
- loop (Loop): The loop to check for membership in this course.
144
+ loop (LoopT): The loop to check for membership in this course.
131
145
 
132
146
  Returns:
133
147
  bool: True if the loop is in this course, False otherwise.
134
148
  """
135
149
  return loop in self._loop_set
136
150
 
137
- def __iter__(self) -> Iterator[Loop]:
138
- """Iterate over loops in this course in order.
151
+ @overload
152
+ def __getitem__(self, index: int) -> LoopT: ...
153
+
154
+ @overload
155
+ def __getitem__(self, index: slice) -> list[LoopT]: ...
156
+
157
+ def __getitem__(self, index: int | slice) -> LoopT | list[LoopT]:
158
+ """Get loop(s) at the specified index or slice.
159
+
160
+ Args:
161
+ index (int | slice): The index or slice to retrieve from the course.
139
162
 
140
163
  Returns:
141
- Iterator[Loop]: An iterator over the loops in this course in their natural order.
164
+ Loop | list[Loop]: The loop at the specified index, or list of loops for a slice.
142
165
  """
143
- return iter(self.loops_in_order)
166
+ return self.loops_in_order[index]
144
167
 
145
- def __reversed__(self) -> Iterator[Loop]:
146
- """Iterate over loops in this course in reverse order.
168
+ def __iter__(self) -> Iterator[LoopT]:
169
+ """Iterate over loops in this course in order.
147
170
 
148
171
  Returns:
149
- Iterator[Loop]: An iterator over the loops in this course in reverse order.
172
+ Iterator[Loop]: An iterator over the loops in this course in their natural order.
150
173
  """
151
- return reversed(self.loops_in_order)
174
+ return iter(self.loops_in_order)
152
175
 
153
176
  def __len__(self) -> int:
154
177
  """Get the number of loops in this course.
@@ -164,7 +187,7 @@ class Course:
164
187
  Returns:
165
188
  str: String representation showing the ordered list of loops.
166
189
  """
167
- return str(self.loops_in_order)
190
+ return f"Course {self.course_number}: {self.loops_in_order}"
168
191
 
169
192
  def __repr__(self) -> str:
170
193
  """Get string representation of this course for debugging.