2121import logging
2222import os
2323from collections import defaultdict
24+ from functools import total_ordering
2425from itertools import tee , zip_longest
2526from pathlib import PurePath
2627from typing import (
5152 ListingNode ,
5253 ListingSource ,
5354)
54- from docc .plugins .python import PythonBuilder
55+ from docc .plugins .python import PythonBuilder , PythonDiscover
56+ from docc .plugins .python .cst import PythonSource
5557from docc .plugins .references import Definition , Reference
5658from docc .settings import PluginSettings
5759from docc .source import Source
@@ -76,24 +78,65 @@ def pairwise(iterable: Iterable[G]) -> Iterable[Tuple[G, G]]:
7678 return zip (a , b , strict = False )
7779
7880
81+ @total_ordering
82+ @dataclasses .dataclass (frozen = True )
83+ class _EthereumSortKey :
84+ sort : int
85+ path : PurePath
86+
87+ def __lt__ (self , other : object ) -> bool :
88+ if isinstance (other , _EthereumSortKey ):
89+ return (self .sort , self .path ) < (other .sort , other .path )
90+ elif isinstance (other , PurePath ):
91+ return self .path < other
92+ return NotImplemented
93+
94+
95+ class _EthereumPythonSource (PythonSource ):
96+ _sort : Final [int ]
97+
98+ def __init__ (
99+ self ,
100+ root_path : PurePath ,
101+ relative_path : PurePath ,
102+ absolute_path : PurePath ,
103+ sort : int ,
104+ ) -> None :
105+ super ().__init__ (root_path , relative_path , absolute_path )
106+ self ._sort = sort
107+
108+ @override
109+ def listing_order_key (
110+ self ,
111+ ) -> Tuple [bool , _EthereumSortKey , None ]:
112+ return (
113+ self .index_dir is None ,
114+ _EthereumSortKey (self ._sort , self .output_path ),
115+ None ,
116+ )
117+
118+
79119class _EthereumListingSource (ListingSource ):
80120 _sort : Final [int ]
81121
82122 def __init__ (
83123 self ,
84124 relative_path : PurePath ,
85125 output_path : PurePath ,
86- sources : Set [Source ],
87126 sort : int ,
88127 ) -> None :
89- super ().__init__ (relative_path , output_path , sources )
128+ super ().__init__ (relative_path , output_path )
90129 self ._sort = sort
91130
92131 @override
93132 def listing_order_key (
94133 self ,
95- ) -> Tuple [bool , Tuple [int , PurePath ], None ]:
96- return (self .is_leaf , (self ._sort , self .output_path ), None )
134+ ) -> Tuple [bool , _EthereumSortKey , None ]:
135+ return (
136+ self .index_dir is None ,
137+ _EthereumSortKey (self ._sort , self .output_path ),
138+ None ,
139+ )
97140
98141
99142def _find_forks (config : PluginSettings ) -> List [Hardfork ]:
@@ -105,47 +148,85 @@ def _diff_path(before: Hardfork, after: Hardfork) -> PurePath:
105148 return PurePath ("diffs" ) / before .short_name / after .short_name
106149
107150
108- class EthereumListingDiscover (ListingDiscover ):
109- """
110- Changes the sort order of directory listings so they render in hard fork
111- chronological order.
112- """
113-
114- fork_order : List [PurePath ]
115- diff_order : List [PurePath ]
151+ class _ForkOrder :
152+ forks : List [PurePath ]
153+ diffs : List [PurePath ]
116154
117155 def __init__ (self , config : PluginSettings ) -> None :
118- super ().__init__ (config )
119156 forks = _find_forks (config )
120- self .fork_order = [
157+ self .forks = [
121158 config .unresolve_path (PurePath (f .path ))
122159 for f in forks
123160 if f .path is not None
124161 ]
125- self .diff_order = [_diff_path (b , a ).parent for b , a in pairwise (forks )]
162+ self .diffs = [_diff_path (b , a ).parent for b , a in pairwise (forks )]
126163
127- def _fork_index (self , parent : PurePath ) -> Optional [int ]:
128- for idx , fork in enumerate (self .fork_order ):
164+ def index (self , parent : PurePath ) -> Optional [int ]:
165+ for idx , fork in enumerate (self .forks ):
129166 if parent .is_relative_to (fork ):
130167 return idx
131168
132- for idx , diff in enumerate (self .diff_order ):
169+ for idx , diff in enumerate (self .diffs ):
133170 if parent .is_relative_to (diff ):
134171 return idx
135172
136173 return None
137174
175+
176+ class EthereumListingDiscover (ListingDiscover ):
177+ """
178+ Changes the sort order of directory listings so they render in hard fork
179+ chronological order.
180+ """
181+
182+ _fork_order : _ForkOrder
183+
184+ def __init__ (self , config : PluginSettings ) -> None :
185+ super ().__init__ (config )
186+ self ._fork_order = _ForkOrder (config )
187+
138188 @override
139189 def _listing_source (
140190 self , source : Source , parent : PurePath
141191 ) -> ListingSource :
142- index = self ._fork_index (parent )
192+ index = self ._fork_order . index (parent )
143193 if index is None :
144194 return super ()._listing_source (source , parent )
145195 return _EthereumListingSource (
146196 parent ,
147197 parent / "index" ,
148- set (),
198+ - index , # Reverse chronological order
199+ )
200+
201+
202+ class EthereumPythonDiscover (PythonDiscover ):
203+ """
204+ Changes the sort order of python files so they render in hard fork
205+ chronological order.
206+ """
207+
208+ _fork_order : _ForkOrder
209+
210+ def __init__ (self , config : PluginSettings ) -> None :
211+ super ().__init__ (config )
212+ self ._fork_order = _ForkOrder (config )
213+
214+ @override
215+ def _python_source (
216+ self ,
217+ root_path : PurePath ,
218+ relative_path : PurePath ,
219+ absolute_path : PurePath ,
220+ ) -> PythonSource :
221+ index = self ._fork_order .index (relative_path )
222+ if index is None :
223+ return super ()._python_source (
224+ root_path , relative_path , absolute_path
225+ )
226+ return _EthereumPythonSource (
227+ root_path ,
228+ relative_path ,
229+ absolute_path ,
149230 - index , # Reverse chronological order
150231 )
151232
@@ -214,6 +295,9 @@ def discover(self, known: FrozenSet[T]) -> Iterator[Source]:
214295
215296 assert before_source or after_source
216297
298+ if path .name == "__init__.py" :
299+ path = path .with_name ("index" )
300+
217301 output_path = _diff_path (before , after ) / path
218302
219303 yield DiffSource (
@@ -282,6 +366,19 @@ def output_path(self) -> PurePath:
282366 """
283367 return self ._output_path
284368
369+ @property
370+ def index_dir (self ) -> Optional [PurePath ]:
371+ """
372+ For index sources, the directory the source indexes. For other sources,
373+ `None`.
374+
375+ For example, for an output path of `./foo/index`, this should return
376+ `./foo`.
377+ """
378+ if self .output_path .name == "index" :
379+ return self .output_path .parent
380+ return None
381+
285382
286383class AfterNode (Node ):
287384 """
0 commit comments