1515 "CodeGen" ,
1616]
1717
18- T = TypeVar ('T' )
18+ T = TypeVar ("T" )
1919Signature = Union [ProblemSignature , InteractiveProblemSignature ]
2020Code = List [str ]
2121
@@ -24,32 +24,34 @@ class CodeGen(abc.ABC):
2424 @property
2525 @abc .abstractmethod
2626 def language (self ) -> str :
27- r """Name of the language to generate."""
27+ """Name of the language to generate."""
2828 raise NotImplementedError
2929
3030 @property
3131 def extra_files (self ) -> Dict [str , str ]:
32- r"""Extra files that will be written verbatim under the project folder. The returned dictionary maps file names
33- to raw code.
32+ """
33+ Extra files that will be written verbatim under the project folder. The returned
34+ dictionary maps file names to raw code.
3435 """
3536 return {}
3637
3738 @property
3839 @abc .abstractmethod
3940 def code_extension (self ) -> str :
40- r """The file extension for code files."""
41+ """The file extension for code files."""
4142 raise NotImplementedError
4243
4344 @property
4445 @abc .abstractmethod
4546 def line_comment_symbol (self ) -> str :
46- r """The symbol for starting a line comment."""
47+ """The symbol for starting a line comment."""
4748 raise NotImplementedError
4849
4950 @property
5051 @abc .abstractmethod
5152 def template_code (self ) -> str :
52- r"""The template code for each problem. Should include section markers for:
53+ """
54+ The template code for each problem. Should include section markers for:
5355
5456 - ``SOLUTION CLASS``: the section to insert the solution class.
5557 - ``SUBMIT``: the section to submit to LeetCode.
@@ -64,48 +66,68 @@ def template_code(self) -> str:
6466
6567 @property
6668 def user_template_code (self ) -> str :
67- r"""User-defined templates for convenience. These will be included in the submission."""
69+ """
70+ User-defined templates for convenience. These will be included in the
71+ submission.
72+ """
6873 return ""
6974
7075 @classmethod
7176 def write_and_backup (cls , path : str , contents : str ) -> None :
72- r"""Check if there is already a file at the given path, create a backup if there is, and then write contents to
73- the file.
77+ """
78+ Check if there is already a file at the given path, create a backup if there is,
79+ and then write contents to the file.
7480 """
7581 if os .path .exists (path ):
7682 with open (path , "r" ) as f :
7783 original_contents = f .read ()
7884 if original_contents != contents :
7985 # Only create backup if contents differ.
8086 creation_time = os .path .getctime (path )
81- timestamp = datetime .fromtimestamp (creation_time ).strftime ("%Y%m%d_%H%M%S" )
87+ timestamp = datetime .fromtimestamp (creation_time ).strftime (
88+ "%Y%m%d_%H%M%S"
89+ )
8290
8391 file_name , file_ext = os .path .splitext (path )
8492 dest_path = f"{ file_name } _{ timestamp } { file_ext } "
8593 shutil .move (path , dest_path )
86- log (f"File '{ path } ' is modified, backup created at '{ dest_path } '" , "warning" )
94+ log (
95+ f"File '{ path } ' is modified, backup created at '{ dest_path } '" ,
96+ level = "warning" ,
97+ )
8798 with open (path , "w" ) as f :
8899 f .write (contents )
89100
90- def replace_section (self , code : Code , replacements : Dict [str , Code ], * , ignore_errors : bool = False ) -> Code :
91- r"""Replace a section of template with actual code. Sections are often marked with line comments.
101+ def replace_section (
102+ self , code : Code , replacements : Dict [str , Code ], * , ignore_errors : bool = False
103+ ) -> Code :
104+ """
105+ Replace a section of template with actual code. Sections are often marked with
106+ line comments.
92107
93108 :param code: The code as a list of strings, one per line.
94109 :param replacements: A dictionary mapping section names to replacement code.
95- :param ignore_errors: A :exc:`ValueError` will be thrown for sections that are not found, unless this argument
96- is ``True``. Defaults to ``False``.
110+ :param ignore_errors: A :exc:`ValueError` will be thrown for sections that are
111+ not found, unless this argument is ``True``. Defaults to
112+ ``False``.
97113 :return: The updated code.
98114 """
99115 for section_name , section_code in replacements .items ():
100116 try :
101- start_line = code .index (f"{ self .line_comment_symbol } BEGIN { section_name } " )
102- end_line = code .index (f"{ self .line_comment_symbol } END { section_name } " , start_line + 1 )
117+ start_line = code .index (
118+ f"{ self .line_comment_symbol } BEGIN { section_name } "
119+ )
120+ end_line = code .index (
121+ f"{ self .line_comment_symbol } END { section_name } " , start_line + 1
122+ )
103123 # exclude the line comments
104- code = code [:start_line ] + section_code + code [(end_line + 1 ):]
124+ code = code [:start_line ] + section_code + code [(end_line + 1 ) :]
105125 except ValueError :
106126 if not ignore_errors :
107- raise ValueError (f"Section '{ section_name } ' not found in template code for { self .language } "
108- f"({ self .__class__ !r} )" )
127+ raise ValueError (
128+ f"Section { section_name !r} not found in template code for"
129+ f" { self .language } ({ self .__class__ !r} )"
130+ )
109131 return code
110132
111133 @classmethod
@@ -118,23 +140,30 @@ def list_join(cls, list_xs: Iterable[List[T]], sep: List[T]) -> List[T]:
118140 return ret
119141
120142 @abc .abstractmethod
121- def generate_code (self , problem : Problem , signature : Signature ) -> Tuple [Code , Code ]:
122- r"""Generate code given the signature. Code consists of two parts:
143+ def generate_code (
144+ self , problem : Problem , signature : Signature
145+ ) -> Tuple [Code , Code ]:
146+ """
147+ Generate code given the signature. Code consists of two parts:
123148
124149 - Code for the solution class. This is basically the template as-is.
125- - Code for testing the solution. This includes test functions for each example, and also the main function where
126- the test functions are called and results are compared.
150+ - Code for testing the solution. This includes test functions for each example,
151+ and also the main function where the test functions are called and results are
152+ compared.
127153
128154 :param problem: The crawled raw description of the problem.
129155 :param signature: The parsed signature of the problem.
130- :return: A tuple of two lists of strings, corresponding to code for the solution class, and code for testing.
156+ :return: A tuple of two lists of strings, corresponding to code for the solution
157+ class, and code for testing.
131158 """
132159 raise NotImplementedError
133160
134- def generate_additional_files (self , project_path : str , problems : List [Problem ],
135- signatures : List [Signature ]) -> None :
136- r"""Generate additional files that the project requires, besides those in :attr:`EXTRA_FILES` that are written
137- verbatim.
161+ def generate_additional_files (
162+ self , project_path : str , problems : List [Problem ], signatures : List [Signature ]
163+ ) -> None :
164+ """
165+ Generate additional files that the project requires, besides those in
166+ :attr:`EXTRA_FILES` that are written verbatim.
138167
139168 :param project_path: Path to the project folder.
140169 :param problems: List of problem descriptions to generate code for.
@@ -143,7 +172,9 @@ def generate_additional_files(self, project_path: str, problems: List[Problem],
143172 pass
144173
145174 def get_problem_file_name (self , idx : int , problem : Problem ) -> str :
146- r"""Generate the code file name for a problem. By default, names are uppercase letters starting from "A".
175+ """
176+ Generate the code file name for a problem. By default, names are uppercase
177+ letters starting from "A".
147178
148179 :param idx: Zero-based index of the problem.
149180 :param problem: The description of the problem.
@@ -152,28 +183,35 @@ def get_problem_file_name(self, idx: int, problem: Problem) -> str:
152183 return f"{ chr (ord ('A' ) + idx )} { self .code_extension } "
153184
154185 def format_statement (self , problem : Problem ) -> List [str ]:
155- r"""Convert the problem statement into code (as comments).
186+ """
187+ Convert the problem statement into code (as comments).
156188
157189 :param problem: The problem description.
158190 :return: Code for the problem statement.
159191 """
160192 statement = []
161193 max_length = 80 - (len (self .line_comment_symbol ) + 1 )
162194 for line in problem .statement .strip ().split ("\n " ):
163- comments = [f"{ self .line_comment_symbol } { line [i :(i + max_length )]} "
164- for i in range (0 , len (line ), max_length )]
195+ comments = [
196+ f"{ self .line_comment_symbol } { line [i :(i + max_length )]} "
197+ for i in range (0 , len (line ), max_length )
198+ ]
165199 statement .extend (comments )
166200 return statement
167201
168- def create_project (self , project_path : str , problems : List [Problem ], site : str , debug : bool = False ) -> None :
169- r"""Create the folder for the project and generate code and supporting files.
202+ def create_project (
203+ self , project_path : str , problems : List [Problem ], site : str , debug : bool = False
204+ ) -> None :
205+ """
206+ Create the folder for the project and generate code and supporting files.
170207
171208 :param project_path: Path to the project folder.
172209 :param problems: List of problem descriptions to generate code for.
173- :param site: The LeetCode site where problems are crawled. Different sites may have slightly different syntax
174- (or language-dependent markings).
175- :param debug: If ``True``, exceptions will not be caught. This is probably only useful when the ``--debug``
176- flag is set, in which case the Python debugger is hooked to handle exceptions.
210+ :param site: The LeetCode site where problems are crawled. Different sites may
211+ have slightly different syntax (or language-dependent markings).
212+ :param debug: If ``True``, exceptions will not be caught. This is probably only
213+ useful when the ``--debug`` flag is set, in which case the Python
214+ debugger is hooked to handle exceptions.
177215 """
178216 if not os .path .exists (project_path ):
179217 os .makedirs (project_path )
@@ -186,21 +224,33 @@ def create_project(self, project_path: str, problems: List[Problem], site: str,
186224 try :
187225 problem_signature = parse_problem (problem , site )
188226 signatures .append (problem_signature )
189- solution_code , test_code = self .generate_code (problem , problem_signature )
190- problem_code = self .replace_section (template , {
191- "SOLUTION CLASS" : solution_code ,
192- "TEST" : test_code ,
193- })
227+ solution_code , test_code = self .generate_code (
228+ problem , problem_signature
229+ )
230+ problem_code = self .replace_section (
231+ template ,
232+ {
233+ "SOLUTION CLASS" : solution_code ,
234+ "TEST" : test_code ,
235+ },
236+ )
194237 if problem .statement != "" :
195238 statement = self .format_statement (problem )
196- problem_code = self .replace_section (problem_code , {"STATEMENT" : statement }, ignore_errors = True )
197- code_path = os .path .join (project_path , self .get_problem_file_name (idx , problem ))
239+ problem_code = self .replace_section (
240+ problem_code , {"STATEMENT" : statement }, ignore_errors = True
241+ )
242+ code_path = os .path .join (
243+ project_path , self .get_problem_file_name (idx , problem )
244+ )
198245 self .write_and_backup (code_path , "\n " .join (problem_code ) + "\n " )
199246 except Exception :
200247 if debug :
201248 raise
202249 traceback .print_exc ()
203- log (f"Exception occurred while processing \" { problem .name } \" " , "error" )
250+ log (
251+ f"Exception occurred while processing { problem .name !r} " ,
252+ level = "error" ,
253+ )
204254
205255 for tmpl_name , tmpl_code in self .extra_files .items ():
206256 with open (os .path .join (project_path , tmpl_name ), "w" ) as f :
0 commit comments