\n", 515 | " | year | \n", 516 | "le | \n", 517 | "
---|---|---|
1 | \n", 522 | "1900 | \n", 523 | "47.3 | \n", 524 | "
2 | \n", 527 | "1901 | \n", 528 | "49.1 | \n", 529 | "
3 | \n", 532 | "1902 | \n", 533 | "51.5 | \n", 534 | "
4 | \n", 537 | "1903 | \n", 538 | "50.5 | \n", 539 | "
5 | \n", 542 | "1904 | \n", 543 | "47.6 | \n", 544 | "
6 | \n", 547 | "1905 | \n", 548 | "48.7 | \n", 549 | "
7 | \n", 552 | "1906 | \n", 553 | "48.7 | \n", 554 | "
8 | \n", 557 | "1907 | \n", 558 | "47.6 | \n", 559 | "
9 | \n", 562 | "1908 | \n", 563 | "51.1 | \n", 564 | "
10 | \n", 567 | "1909 | \n", 568 | "52.1 | \n", 569 | "
11 | \n", 572 | "1910 | \n", 573 | "50 | \n", 574 | "
12 | \n", 577 | "1911 | \n", 578 | "52.6 | \n", 579 | "
13 | \n", 582 | "1912 | \n", 583 | "53.5 | \n", 584 | "
14 | \n", 587 | "1913 | \n", 588 | "52.5 | \n", 589 | "
15 | \n", 592 | "1914 | \n", 593 | "54.2 | \n", 594 | "
16 | \n", 597 | "1915 | \n", 598 | "54.5 | \n", 599 | "
17 | \n", 602 | "1916 | \n", 603 | "51.7 | \n", 604 | "
18 | \n", 607 | "1917 | \n", 608 | "50.9 | \n", 609 | "
19 | \n", 612 | "1918 | \n", 613 | "39.1 | \n", 614 | "
20 | \n", 617 | "1919 | \n", 618 | "54.7 | \n", 619 | "
21 | \n", 622 | "1920 | \n", 623 | "54.1 | \n", 624 | "
22 | \n", 627 | "1921 | \n", 628 | "60.8 | \n", 629 | "
23 | \n", 632 | "1922 | \n", 633 | "59.6 | \n", 634 | "
24 | \n", 637 | "1923 | \n", 638 | "57.2 | \n", 639 | "
25 | \n", 642 | "1924 | \n", 643 | "59.7 | \n", 644 | "
26 | \n", 647 | "1925 | \n", 648 | "59 | \n", 649 | "
27 | \n", 652 | "1926 | \n", 653 | "56.7 | \n", 654 | "
28 | \n", 657 | "1927 | \n", 658 | "60.4 | \n", 659 | "
29 | \n", 662 | "1928 | \n", 663 | "56.8 | \n", 664 | "
30 | \n", 667 | "1929 | \n", 668 | "57.1 | \n", 669 | "
31 | \n", 672 | "1930 | \n", 673 | "59.7 | \n", 674 | "
32 | \n", 677 | "1931 | \n", 678 | "61.1 | \n", 679 | "
33 | \n", 682 | "1932 | \n", 683 | "62.1 | \n", 684 | "
34 | \n", 687 | "1933 | \n", 688 | "63.3 | \n", 689 | "
35 | \n", 692 | "1934 | \n", 693 | "61.1 | \n", 694 | "
36 | \n", 697 | "1935 | \n", 698 | "61.7 | \n", 699 | "
37 | \n", 702 | "1936 | \n", 703 | "58.5 | \n", 704 | "
38 | \n", 707 | "1937 | \n", 708 | "60 | \n", 709 | "
39 | \n", 712 | "1938 | \n", 713 | "63.5 | \n", 714 | "
40 | \n", 717 | "1939 | \n", 718 | "63.7 | \n", 719 | "
41 | \n", 722 | "1940 | \n", 723 | "62.9 | \n", 724 | "
[\s,]+.*?)?\Z', flags=re.DOTALL + re.MULTILINE)
52 |
53 | # Format: magic_name: help_content
54 | available_magics = {
55 | '%browse': '{} [-h] [varlist] [if] [in] [, nolabel noformat]',
56 | '%head': '{} [-h] [N] [varlist] [if] [, nolabel noformat]',
57 | '%tail': '{} [-h] [N] [varlist] [if] [, nolabel noformat]',
58 | '%frbrowse': '{} [-h] framename: [varlist] [if] [in] [, nolabel noformat]',
59 | '%frhead': '{} [-h] framename: [N] [varlist] [if] [, nolabel noformat]',
60 | '%frtail': '{} [-h] framename: [N] [varlist] [if] [, nolabel noformat]',
61 | '%locals': '',
62 | '%delimit': '',
63 | '%help': '{} [-h] command_or_topic_name',
64 | '%set': '{} [-h] key = value',
65 | '%%set': '{} [-h]\nkey1 = value1\n[key2 = value2]\n[...]',
66 | '%status': '',
67 | '%%quietly': '',
68 | '%%noecho': '',
69 | '%%echo': '',
70 | }
71 |
72 | abbrev_dict = _construct_abbrev_dict()
73 |
74 | csshelp_default = resource_filename(
75 | 'nbstata', 'css/_StataKernelHelpDefault.css'
76 | )
77 |
78 | def magic_quietly(self, code, kernel, cell):
79 | """Suppress all display for the current cell."""
80 | cell.quietly = True
81 | return code
82 |
83 | def magic_noecho(self, code, kernel, cell):
84 | """Suppress echo for the current cell."""
85 | cell.noecho = True
86 | cell.echo = False
87 | return code
88 |
89 | def magic_echo(self, code, kernel, cell):
90 | """Suppress echo for the current cell."""
91 | cell.noecho = False
92 | cell.echo = True
93 | return code
94 |
95 | def magic_delimit(self, code, kernel, cell):
96 | delim = ';' if kernel.stata_session.sc_delimiter else 'cr'
97 | print_kernel(f'Current Stata command delimiter: {delim}', kernel)
98 | return ''
99 |
100 | def magic_status(self, code, kernel, cell):
101 | kernel.nbstata_config.display_status()
102 | return ''
103 |
104 | # %% ../nbs/09_magics.ipynb 8
105 | @patch_to(StataMagics)
106 | def _unabbrev_magic_name(self, raw_name):
107 | last_percent = raw_name.rfind('%')
108 | percent_part = raw_name[:last_percent+1]
109 | raw_name_part = raw_name[last_percent+1:]
110 | if raw_name_part in self.abbrev_dict:
111 | name_part = self.abbrev_dict[raw_name_part]
112 | else:
113 | name_part = raw_name_part
114 | return percent_part + name_part
115 |
116 | # %% ../nbs/09_magics.ipynb 11
117 | def _parse_magic_name_code(match):
118 | v = match.groupdict()
119 | for k in v:
120 | v[k] = v[k] if v[k] is not None else ''
121 | name = v['magic'].strip()
122 | code = v['code'].strip()
123 | return name, code
124 |
125 | # %% ../nbs/09_magics.ipynb 12
126 | @patch_to(StataMagics)
127 | def _parse_code_for_magic(self, code):
128 | match = self.magic_regex.match(code.strip())
129 | if match:
130 | raw_name, mcode = _parse_magic_name_code(match)
131 | name = self._unabbrev_magic_name(raw_name)
132 | if name in {'%quietly', '%noecho', '%echo'}:
133 | print_red(
134 | f"Warning: The correct syntax for a cell magic is '%{name}', not '{name}'. "
135 | "In v1.0, nbstata may trigger an error instead of just a warning."
136 | )
137 | name = '%' + name
138 | elif name == "%set" and len(code.splitlines()) > 1:
139 | print_red(
140 | f"Warning: The correct syntax for the multi-line 'set' magic is '%{name}', not '{name}'. "
141 | "In v1.0, nbstata may trigger an error instead of just a warning."
142 | )
143 | name = '%' + name
144 | elif name == "%%set" and len(code.splitlines()) == 1:
145 | print_red(
146 | f"Warning: The correct syntax for the single-line 'set' magic is '%set', not '{name}'. "
147 | "In v1.0, nbstata may trigger an error instead of just a warning."
148 | )
149 | name = '%set'
150 | elif name not in self.available_magics:
151 | raise ValueError(f"Unknown magic {name}.")
152 | return name, mcode
153 | else:
154 | return None, code
155 |
156 | # %% ../nbs/09_magics.ipynb 19
157 | @patch_to(StataMagics)
158 | def _do_magic(self, name, code, kernel, cell):
159 | if code.startswith('-h') or code.startswith('--help') or (name == "%help" and (not code or code.isspace())):
160 | print_kernel(self.available_magics[name].format(name), kernel)
161 | return ''
162 | else:
163 | return getattr(self, "magic_" + name.lstrip('%'))(code, kernel, cell)
164 |
165 | # %% ../nbs/09_magics.ipynb 21
166 | @patch_to(StataMagics)
167 | def magic(self, code, kernel, cell):
168 | try:
169 | if kernel.nbstata_config.browse_auto_height and not kernel.ipydatagrid_height_set:
170 | browse.set_ipydatagrid_height()
171 | kernel.ipydatagrid_height_set = True
172 | name, code = self._parse_code_for_magic(code)
173 | except ValueError as e:
174 | print_kernel(str(e), kernel)
175 | else:
176 | if name:
177 | code = self._do_magic(name, code, kernel, cell)
178 | return code
179 |
180 | # %% ../nbs/09_magics.ipynb 22
181 | def _formatted_local_list(local_dict):
182 | std_len = 14
183 | str_reps = []
184 | for n in local_dict:
185 | if len(n) <= std_len:
186 | str_reps.append(f"{n}:{' '*(std_len-len(n))} {local_dict[n]}")
187 | else:
188 | str_reps.append(f"{n}:\n{' '*std_len} {local_dict[n]}")
189 | return "\n".join(str_reps)
190 |
191 | # %% ../nbs/09_magics.ipynb 25
192 | @patch_to(StataMagics)
193 | def magic_locals(self, code, kernel, cell):
194 | local_dict = kernel.stata_session.get_local_dict()
195 | print_kernel(_formatted_local_list(local_dict), kernel)
196 | return ''
197 |
198 | # %% ../nbs/09_magics.ipynb 27
199 | def _get_new_settings(code):
200 | parser = configparser.ConfigParser(
201 | empty_lines_in_values=False,
202 | comment_prefixes=('*','//', '/*'), # '/*': to not cause error when commenting out for Stata purposes only
203 | inline_comment_prefixes=('//',),
204 | )
205 | parser.read_string("[set]\n" + code.strip(), source="set")
206 | return dict(parser.items('set'))
207 |
208 | # %% ../nbs/09_magics.ipynb 31
209 | def _clean_error_message(err_str):
210 | return (err_str
211 | .replace("While reading from 'set'", "")
212 | .replace(" in section 'set' already exists", " already set above")
213 | .replace("Source contains ", "")
214 | .replace(" 'set'\n", "\n")
215 | )
216 |
217 | # %% ../nbs/09_magics.ipynb 32
218 | def _process_new_settings(settings, kernel):
219 | kernel.nbstata_config.update(settings)
220 | kernel.nbstata_config.update_graph_config()
221 |
222 | # %% ../nbs/09_magics.ipynb 33
223 | @patch_to(StataMagics)
224 | def magic_set(self, code, kernel, cell):
225 | try:
226 | settings = _get_new_settings(code)
227 | except configparser.Error as err:
228 | print_red(f"set error:\n {_clean_error_message(str(err))}")
229 | else:
230 | _process_new_settings(settings, kernel)
231 | warn_re_unclosed_comment_block_if_needed(code)
232 |
233 | # %% ../nbs/09_magics.ipynb 38
234 | @patch_to(StataMagics)
235 | def magic_browse(self, code, kernel, cell):
236 | """Display data interactively."""
237 | try:
238 | expanded_code = macro_expand(code)
239 | params = browse.browse_df_params(
240 | expanded_code, obs_count(), kernel.nbstata_config.env['missing'],
241 | )
242 | sformat = params[-1]
243 | df = browse.get_df(*params)
244 | browse.display_df_as_ipydatagrid(df, kernel.nbstata_config.browse_auto_height)
245 | except Exception as e:
246 | print_kernel(f"browse failed.\r\n{e}", kernel)
247 | return ''
248 |
249 | # %% ../nbs/09_magics.ipynb 39
250 | class Frame():
251 | """Class for generating Stata select_var for getAsDict"""
252 | def __init__(self, framename):
253 | self.original_framename = get_global('c(frame)')
254 | self.framename = framename
255 |
256 | def __enter__(self):
257 | import sfi
258 | try:
259 | frame = sfi.Frame.connect(self.framename)
260 | except sfi.FrameError:
261 | raise ValueError(f"frame {self.framename} not found")
262 | else:
263 | frame.changeToCWF()
264 |
265 | def __exit__(self, exc_type, exc_value, exc_tb):
266 | import sfi
267 | orig_frame = sfi.Frame.connect(self.original_framename)
268 | orig_frame.changeToCWF()
269 |
270 | # %% ../nbs/09_magics.ipynb 40
271 | def _parse_frame_prefix(code):
272 | pattern = re.compile(
273 | r'\A(?P\w+)[ \t]*(?:\:[ \t]*(?P.*?))?\Z', flags=re.DOTALL)
274 | match = pattern.match(code)
275 | if not match:
276 | raise ValueError("invalid syntax: missing framename or colon?")
277 | v = match.groupdict()
278 | for k in v:
279 | v[k] = v[k] if v[k] is not None else ''
280 | framename = v['frame'].strip()
281 | main_code = v['code'].strip()
282 | return framename, main_code
283 |
284 | # %% ../nbs/09_magics.ipynb 42
285 | @patch_to(StataMagics)
286 | def magic_frbrowse(self, code, kernel, cell):
287 | """Display frame interactively."""
288 | try:
289 | framename, main_code = _parse_frame_prefix(code)
290 | with Frame(framename):
291 | self.magic_browse(main_code, kernel, cell)
292 | except Exception as e:
293 | print_kernel(f"frbrowse failed.\r\n{e}", kernel)
294 | return ''
295 |
296 | # %% ../nbs/09_magics.ipynb 46
297 | def _get_html_data(df):
298 | html = df.convert_dtypes().to_html(notebook=True)
299 | return {'text/html': html}
300 |
301 | # %% ../nbs/09_magics.ipynb 47
302 | @patch_to(StataMagics)
303 | def _headtail_html(self, df, kernel):
304 | content = {
305 | 'data': _get_html_data(df),
306 | 'metadata': {},
307 | }
308 | kernel.send_response(kernel.iopub_socket, 'display_data', content)
309 |
310 | # %% ../nbs/09_magics.ipynb 48
311 | @patch_to(StataMagics)
312 | def _magic_headtail(self, code, kernel, cell, tail=False):
313 | try:
314 | expanded_code = macro_expand(code)
315 | df = browse.headtail_get_df(*browse.headtail_df_params(
316 | expanded_code, obs_count(), kernel.nbstata_config.env['missing'], tail=tail
317 | ))
318 | self._headtail_html(df, kernel)
319 | except Exception as e:
320 | print_kernel(f"{'tail' if tail else 'head'} failed.\r\n{e}", kernel)
321 | return ''
322 |
323 | # %% ../nbs/09_magics.ipynb 49
324 | @patch_to(StataMagics)
325 | def magic_head(self, code, kernel, cell):
326 | """Display data in a nicely-formatted table."""
327 | return self._magic_headtail(code, kernel, cell, tail=False)
328 |
329 | # %% ../nbs/09_magics.ipynb 50
330 | @patch_to(StataMagics)
331 | def magic_frhead(self, code, kernel, cell):
332 | """Display data in a nicely-formatted table."""
333 | return self._magic_frheadtail(code, kernel, cell, tail=False)
334 |
335 | # %% ../nbs/09_magics.ipynb 51
336 | @patch_to(StataMagics)
337 | def magic_tail(self, code, kernel, cell):
338 | """Display data in a nicely-formatted table."""
339 | return self._magic_headtail(code, kernel, cell, tail=True)
340 |
341 | # %% ../nbs/09_magics.ipynb 52
342 | @patch_to(StataMagics)
343 | def magic_frtail(self, code, kernel, cell):
344 | """Display data in a nicely-formatted table."""
345 | return self._magic_frheadtail(code, kernel, cell, tail=True)
346 |
347 | # %% ../nbs/09_magics.ipynb 53
348 | @patch_to(StataMagics)
349 | def _magic_frheadtail(self, code, kernel, cell, tail):
350 | """Display frame interactively."""
351 | try:
352 | framename, main_code = _parse_frame_prefix(code)
353 | with Frame(framename):
354 | self._magic_headtail(main_code, kernel, cell, tail)
355 | except Exception as e:
356 | print_kernel(f"{'tail' if tail else 'head'} failed.\r\n{e}", kernel)
357 | return ''
358 |
359 | # %% ../nbs/09_magics.ipynb 55
360 | @patch_to(StataMagics)
361 | def _get_help_html(self, code):
362 | html_base = "https://www.stata.com"
363 | html_help = urllib.parse.urljoin(html_base, "help.cgi?{}")
364 | url_safe_code = urllib.parse.quote(code)
365 | headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"}
366 | request = urllib.request.Request(html_help.format(url_safe_code), headers=headers)
367 | reply = urllib.request.urlopen(request)
368 | html = reply.read().decode("utf-8")
369 |
370 | # Remove excessive extra lines (Note css: "white-space: pre-wrap")
371 | edited_html = html.replace("\n", "
")
372 | soup = bs(edited_html, 'html.parser')
373 |
374 | # Set root for links to https://www.stata.com
375 | for a in soup.find_all('a', href=True):
376 | href = a.get('href')
377 | match = re.search(r'{}(.*?)#'.format(code), href)
378 | if match:
379 | hrelative = href.find('#')
380 | a['href'] = href[hrelative:]
381 | elif not href.startswith('http'):
382 | link = a['href']
383 | match = re.search(r'/help.cgi\?(.+)$', link)
384 | # URL encode bad characters like %
385 | if match:
386 | link = '/help.cgi?'
387 | link += urllib.parse.quote_plus(match.group(1))
388 | a['href'] = urllib.parse.urljoin(html_base, link)
389 | a['target'] = '_blank'
390 |
391 | # Remove header 'Stata 15 help for ...'
392 | stata_header = soup.find('h2')
393 | if stata_header:
394 | stata_header.decompose()
395 |
396 | # Remove Stata help menu
397 | soup.find('div', id='menu').decompose()
398 |
399 | # Remove Copyright notice
400 | copyright = soup.find(string=re.compile(r".*Copyright.*", flags=re.DOTALL))
401 | copyright.find_parent("table").decompose()
402 |
403 | # Remove last hrule
404 | soup.find_all('hr')[-1].decompose()
405 |
406 | # Remove last br
407 | soup.find_all('br')[-1].decompose()
408 |
409 | # Remove last empty paragraph, empty space
410 | empty_paragraphs = soup.find_all('p', string="")
411 | if str(empty_paragraphs[-1]) == "
":
412 | empty_paragraphs[-1].decompose()
413 |
414 | # Set all the backgrounds to transparent
415 | for color in ['#ffffff', '#FFFFFF']:
416 | for bg in ['bgcolor', 'background', 'background-color']:
417 | for tag in soup.find_all(attrs={bg: color}):
418 | if tag.get(bg):
419 | tag[bg] = 'transparent'
420 |
421 | # Set html
422 | css = soup.find('style', {'type': 'text/css'})
423 | with open(self.csshelp_default, 'r') as default:
424 | css.string = default.read()
425 |
426 | return str(soup)
427 |
428 | # %% ../nbs/09_magics.ipynb 56
429 | @patch_to(StataMagics)
430 | def magic_help(self, code, kernel, cell):
431 | """Show help file from stata.com/help.cgi?\{\}"""
432 | try:
433 | html_help = self._get_help_html(code)
434 | except Exception as e: # original: (urllib.error.HTTPError, urllib.error.URLError)
435 | msg = "Failed to fetch HTML help.\r\n{0}"
436 | print_kernel(msg.format(e), kernel)
437 | else:
438 | fallback = 'This front-end cannot display HTML help.'
439 | resp = {
440 | 'data': {
441 | 'text/html': html_help,
442 | 'text/plain': fallback},
443 | 'metadata': {}}
444 | kernel.send_response(kernel.iopub_socket, 'display_data', resp)
445 | return ''
446 |
--------------------------------------------------------------------------------
/nbstata/misc_utils.py:
--------------------------------------------------------------------------------
1 | """General helper functions with no Jupyter or pystata dependence"""
2 |
3 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_misc_utils.ipynb.
4 |
5 | # %% auto 0
6 | __all__ = ['Timer', 'print_red']
7 |
8 | # %% ../nbs/00_misc_utils.ipynb 3
9 | import time
10 |
11 | # %% ../nbs/00_misc_utils.ipynb 4
12 | class Timer():
13 | text = "Elapsed time: {:0.4f} seconds"
14 | logger = print
15 | _start_time = None
16 |
17 | def start(self):
18 | self._start_time = time.perf_counter()
19 |
20 | def stop(self):
21 | elapsed_time = time.perf_counter() - self._start_time
22 | self._start_time = None
23 | if self.logger:
24 | self.logger(self.text.format(elapsed_time))
25 |
26 | def __enter__(self):
27 | self.start()
28 | return self
29 |
30 | def __exit__(self, *exc_info):
31 | self.stop()
32 |
33 | # %% ../nbs/00_misc_utils.ipynb 6
34 | def print_red(text):
35 | print(f"\x1b[31m{text}\x1b[0m")
36 |
--------------------------------------------------------------------------------
/nbstata/noecho.py:
--------------------------------------------------------------------------------
1 | """For running multi-line Stata code without echoing the commands"""
2 |
3 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/05_noecho.ipynb.
4 |
5 | # %% auto 0
6 | __all__ = ['parse_sreturn', 'run_as_program_w_locals', 'run_non_prog_noecho', 'run_prog_noecho', 'run_noecho']
7 |
8 | # %% ../nbs/05_noecho.ipynb 4
9 | from .code_utils import break_out_prog_blocks, valid_single_line_code, local_def_in, preserve_restore_in
10 | from .stata import run_direct, set_local, run_single, get_global
11 | from . import stata_more as sm
12 | from textwrap import dedent
13 | import re
14 |
15 | # %% ../nbs/05_noecho.ipynb 7
16 | def _run_as_program_w_locals_sreturned(std_code):
17 | sreturn_code = dedent("""\
18 |
19 | mata : st_local("temp_nbstata_all_locals", invtokens(st_dir("local", "macro", "*")'))
20 | foreach lname in `temp_nbstata_all_locals' {
21 | sreturn local `lname' "``lname''"
22 | }
23 | """)
24 | store_new_locals_code = ("sreturn clear\n"
25 | + std_code
26 | + sreturn_code)
27 | sm.run_as_program(store_new_locals_code, "sclass")
28 |
29 | # %% ../nbs/05_noecho.ipynb 11
30 | parse_sreturn = re.compile(
31 | r'^\s*?(?:\ss\((?P\w+)\) : )', flags=re.MULTILINE
32 | ).findall
33 |
34 | # %% ../nbs/05_noecho.ipynb 13
35 | def _local_names_from_sreturn(sreturn_output):
36 | matches = parse_sreturn(sreturn_output)
37 | return matches
38 |
39 | # %% ../nbs/05_noecho.ipynb 15
40 | def _after_local_dict():
41 | sreturn_output = sm.diverted_stata_output_quicker("sreturn list")
42 | _local_names = _local_names_from_sreturn(sreturn_output)
43 | return {name: get_global(f"s({name})") for name in _local_names}
44 |
45 | # %% ../nbs/05_noecho.ipynb 20
46 | def _restore_locals_and_clear_sreturn():
47 | for lname, value in _after_local_dict().items():
48 | set_local(lname, value)
49 | run_single("sreturn clear")
50 |
51 | # %% ../nbs/05_noecho.ipynb 22
52 | def run_as_program_w_locals(std_code, local_dict=None):
53 | if local_dict is None:
54 | local_dict = sm.get_local_dict()
55 | locals_code = sm.locals_code_from_dict(local_dict)
56 | if not local_def_in(std_code):
57 | sm.run_as_program(f"""{locals_code}\n{std_code}""")
58 | else:
59 | _run_as_program_w_locals_sreturned(f"""{locals_code}\n{std_code}""")
60 | _restore_locals_and_clear_sreturn()
61 |
62 | # %% ../nbs/05_noecho.ipynb 25
63 | def run_non_prog_noecho(std_non_prog_code, run_as_prog=run_as_program_w_locals):
64 | if len(std_non_prog_code.splitlines()) <= 1: # to keep it simple when we can
65 | run_direct(valid_single_line_code(std_non_prog_code),
66 | quietly=False, inline=True, echo=False)
67 | elif preserve_restore_in(std_non_prog_code):
68 | print("(Note: Below code run with echo to enable preserve/restore functionality.)")
69 | run_direct(std_non_prog_code,
70 | quietly=False, inline=True, echo=False)
71 | else:
72 | run_as_prog(std_non_prog_code)
73 |
74 | # %% ../nbs/05_noecho.ipynb 32
75 | def run_prog_noecho(std_prog_code):
76 | if std_prog_code.splitlines()[0] in {'mata', 'mata:'}: # b/c 'quietly' blocks mata output
77 | run_direct(std_prog_code, quietly=False, inline=True, echo=False)
78 | else:
79 | run_direct(std_prog_code, quietly=True, inline=True, echo=False)
80 |
81 | # %% ../nbs/05_noecho.ipynb 38
82 | def run_noecho(code, sc_delimiter=False, run_as_prog=run_as_program_w_locals):
83 | """After `break_out_prog_blocks`, run each prog and non-prog block noecho"""
84 | for block in break_out_prog_blocks(code, sc_delimiter):
85 | if block['is_prog']:
86 | run_prog_noecho(block['std_code'])
87 | else:
88 | run_non_prog_noecho(block['std_code'], run_as_prog=run_as_prog)
89 |
--------------------------------------------------------------------------------
/nbstata/pandas.py:
--------------------------------------------------------------------------------
1 | """Stata-to-pandas utilities, used in `nbstata.browse`"""
2 |
3 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/06_pandas.ipynb.
4 |
5 | # %% auto 0
6 | __all__ = ['better_dataframe_from_stata', 'better_pdataframe_from_data', 'better_pdataframe_from_frame']
7 |
8 | # %% ../nbs/06_pandas.ipynb 3
9 | from .stata import stata_formatted
10 | from .stata_more import IndexVar
11 |
12 | # %% ../nbs/06_pandas.ipynb 6
13 | def _better_dataframe(hdl, var, obs, selectvar, valuelabel, missingval):
14 | import pandas as pd
15 | with IndexVar() as idx_var:
16 | data = hdl.getAsDict(var, obs, selectvar, valuelabel, missingval)
17 | if not data:
18 | return pd.DataFrame()
19 |
20 | if idx_var in data:
21 | idx = data.pop(idx_var)
22 | else:
23 | temp_var = [idx_var, selectvar] if selectvar else idx_var
24 | idx = hdl.getAsDict(temp_var, obs, selectvar, valuelabel, missingval).pop(idx_var)
25 | idx = pd.array(idx, dtype='int64')
26 |
27 | return pd.DataFrame(data=data, index=idx)
28 |
29 | # %% ../nbs/06_pandas.ipynb 17
30 | def _simple_dataframe_from_stata(stfr, var, valuelabel, missingval):
31 | from pystata import stata
32 | if stfr is None:
33 | df = stata.pdataframe_from_data(var=var, valuelabel=valuelabel, missingval=missingval)
34 | else:
35 | df = stata.pdataframe_from_frame(stfr, var=var, valuelabel=valuelabel, missingval=missingval)
36 | df.index += 1
37 | return df
38 |
39 | # %% ../nbs/06_pandas.ipynb 20
40 | def better_dataframe_from_stata(stfr, var, obs, selectvar, valuelabel, missingval, sformat):
41 | from numpy import nan
42 | import pandas as pd
43 | import sfi
44 | hdl = sfi.Data if stfr is None else sfi.Frame.connect(stfr)
45 | custom_index_not_needed = obs is None and not selectvar
46 | if custom_index_not_needed:
47 | df = _simple_dataframe_from_stata(stfr, var, valuelabel, missingval)
48 | else:
49 | if hdl.getObsTotal() <= 0:
50 | return pd.DataFrame()
51 | df = _better_dataframe(hdl, var, obs, selectvar, valuelabel, missingval)
52 | if sformat:
53 | for v in list(df.columns):
54 | if hdl.isVarTypeString(v) or (valuelabel and missingval==nan
55 | and not pd.api.types.is_numeric_dtype(df[v])):
56 | continue
57 | v_format = hdl.getVarFormat(v)
58 | if missingval != nan and not pd.api.types.is_numeric_dtype(df[v]):
59 | def format_value(x):
60 | return stata_formatted(x, v_format).lstrip() if type(x)!=str else x
61 | else:
62 | def format_value(x):
63 | return stata_formatted(x, v_format).lstrip()
64 | df[v] = df[v].apply(format_value)
65 | return df
66 |
67 | # %% ../nbs/06_pandas.ipynb 21
68 | def better_pdataframe_from_data(var=None, obs=None, selectvar=None, valuelabel=False, missingval=None, sformat=False):
69 | from numpy import nan
70 | if missingval is None:
71 | missingval = nan
72 | return better_dataframe_from_stata(None, var, obs, selectvar, valuelabel, missingval, sformat)
73 |
74 | # %% ../nbs/06_pandas.ipynb 22
75 | def better_pdataframe_from_frame(stfr, var=None, obs=None, selectvar=None, valuelabel=False, missingval=None, sformat=False):
76 | from numpy import nan
77 | if missingval is None:
78 | missingval = nan
79 | return better_dataframe_from_stata(stfr, var, obs, selectvar, valuelabel, missingval, sformat)
80 |
--------------------------------------------------------------------------------
/nbstata/stata.py:
--------------------------------------------------------------------------------
1 | """Simple wrappers for `pystata`/`sfi` functionality"""
2 |
3 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/02_stata.ipynb.
4 |
5 | # %% auto 0
6 | __all__ = ['get_local', 'set_local', 'get_global', 'get_scalar', 'stata_formatted', 'variable_names', 'drop_var', 'obs_count',
7 | 'pwd', 'macro_expand', 'run_direct', 'run_single']
8 |
9 | # %% ../nbs/02_stata.ipynb 5
10 | from .misc_utils import print_red
11 | from contextlib import redirect_stdout
12 | from io import StringIO
13 |
14 | # %% ../nbs/02_stata.ipynb 9
15 | def get_local(name):
16 | import sfi
17 | return sfi.Macro.getLocal(name)
18 |
19 | # %% ../nbs/02_stata.ipynb 11
20 | def set_local(name, value):
21 | import sfi
22 | return sfi.Macro.setLocal(name, value)
23 |
24 | # %% ../nbs/02_stata.ipynb 13
25 | def get_global(name):
26 | import sfi
27 | return sfi.Macro.getGlobal(name)
28 |
29 | # %% ../nbs/02_stata.ipynb 15
30 | def get_scalar(name):
31 | import sfi
32 | return sfi.Scalar.getValue(name)
33 |
34 | # %% ../nbs/02_stata.ipynb 17
35 | def stata_formatted(value, s_format):
36 | import sfi
37 | return sfi.SFIToolkit.formatValue(value, s_format)
38 |
39 | # %% ../nbs/02_stata.ipynb 19
40 | def variable_names():
41 | from sfi import Data
42 | return [Data.getVarName(i) for i in range(Data.getVarCount())]
43 |
44 | # %% ../nbs/02_stata.ipynb 23
45 | def drop_var(name):
46 | import sfi
47 | sfi.Data.dropVar(name)
48 |
49 | # %% ../nbs/02_stata.ipynb 26
50 | def obs_count():
51 | """Count the number of observations"""
52 | import sfi
53 | return sfi.Data.getObsTotal()
54 |
55 | # %% ../nbs/02_stata.ipynb 29
56 | def pwd():
57 | from sfi import SFIToolkit
58 | return SFIToolkit.getWorkingDir()
59 |
60 | # %% ../nbs/02_stata.ipynb 32
61 | def macro_expand(s):
62 | from sfi import SFIToolkit
63 | return SFIToolkit.macroExpand(s)
64 |
65 | # %% ../nbs/02_stata.ipynb 35
66 | def run_direct(cmds, quietly=False, echo=False, inline=True):
67 | import pystata
68 | return pystata.stata.run(cmds, quietly, echo, inline)
69 |
70 | # %% ../nbs/02_stata.ipynb 43
71 | def run_single(cmd, echo=False):
72 | import sfi
73 | try:
74 | sfi.SFIToolkit.stata(cmd, echo)
75 | except Exception as e:
76 | with redirect_stdout(StringIO()) as diverted:
77 | sfi.SFIToolkit.stata("", echo)
78 | raise SyntaxError(diverted.getvalue())
79 |
--------------------------------------------------------------------------------
/nbstata/stata_more.py:
--------------------------------------------------------------------------------
1 | """Helper functions that expand on `pystata`/`sfi` functionality"""
2 |
3 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/03_stata_more.ipynb.
4 |
5 | # %% auto 0
6 | __all__ = ['run_direct_cleaned', 'run_sfi', 'SelectVar', 'IndexVar', 'run_as_program', 'diverted_stata_output',
7 | 'diverted_stata_output_quicker', 'var_from_varlist', 'local_names', 'get_local_dict',
8 | 'locals_code_from_dict', 'user_expression']
9 |
10 | # %% ../nbs/03_stata_more.ipynb 4
11 | from .misc_utils import print_red
12 | from .stata import run_direct, run_single, get_local, set_local, drop_var, stata_formatted
13 | from textwrap import dedent
14 | import functools
15 | from contextlib import redirect_stdout
16 | from io import StringIO
17 | import re
18 |
19 | # %% ../nbs/03_stata_more.ipynb 8
20 | def run_direct_cleaned(cmds, quietly=False, echo=False, inline=True):
21 | if quietly:
22 | with redirect_stdout(StringIO()) as diverted: # to prevent blank line output, as with `program define`
23 | out = run_direct(cmds, quietly, echo, inline)
24 | prints = diverted.getvalue()
25 | for line in prints.splitlines():
26 | if line.strip():
27 | print(line)
28 | return out
29 | elif len(cmds.splitlines()) > 1:
30 | with redirect_stdout(StringIO()) as diverted:
31 | run_direct(cmds, quietly, echo, inline)
32 | output_lines = diverted.getvalue().splitlines()
33 | if (len(output_lines) >= 2
34 | and not output_lines[0].strip()
35 | and "\n".join(output_lines[-2:]).strip() == "."):
36 | print("\n".join(output_lines[1:-2]))
37 | else:
38 | print("\n".join(output_lines))
39 | else:
40 | return run_direct(cmds, quietly, echo, inline)
41 |
42 | # %% ../nbs/03_stata_more.ipynb 30
43 | def run_sfi(std_code, echo=False, show_exc_warning=True):
44 | import sfi
45 | cmds = std_code.splitlines()
46 | for i, cmd in enumerate(cmds):
47 | try:
48 | sfi.SFIToolkit.stata(cmd, echo)
49 | except Exception as e:
50 | if show_exc_warning:
51 | print_red(f"run_sfi (sfi.SFIToolkit.stata) error: {repr(e)}")
52 | remaining_code = "\n".join(cmds[i:])
53 | run_direct(remaining_code, echo=echo)
54 | break
55 |
56 | # %% ../nbs/03_stata_more.ipynb 35
57 | class SelectVar():
58 | """Class for generating Stata select_var for getAsDict"""
59 | varname = None
60 |
61 | def __init__(self, stata_if_code):
62 | import sfi
63 | condition = stata_if_code.replace('if ', '', 1).strip()
64 | if condition:
65 | self.varname = sfi.SFIToolkit.getTempName()
66 | cmd = f"quietly gen {self.varname} = cond({condition},1,0)"
67 | run_single(cmd)
68 |
69 | def clear(self):
70 | """Remove temporary select_var from Stata dataset"""
71 | if self.varname:
72 | drop_var(self.varname)
73 |
74 | def __enter__(self):
75 | return self.varname
76 |
77 | def __exit__(self, exc_type, exc_value, exc_tb):
78 | self.clear()
79 |
80 | # %% ../nbs/03_stata_more.ipynb 38
81 | class IndexVar:
82 | """Class for generating Stata index var for use with pandas"""
83 | def __enter__(self):
84 | import sfi
85 | self.idx_var = sfi.SFIToolkit.getTempName()
86 | run_single(f"gen {self.idx_var} = _n")
87 | return self.idx_var
88 |
89 | def __exit__(self, exc_type, exc_value, exc_tb):
90 | drop_var(self.idx_var)
91 |
92 | # %% ../nbs/03_stata_more.ipynb 51
93 | def run_as_program(std_non_prog_code, prog_def_option_code=""):
94 | _program_name = "temp_nbstata_program_name"
95 | _options = f", {prog_def_option_code}" if prog_def_option_code else ""
96 | _program_define_code = (
97 | f"program {_program_name}{_options}\n"
98 | f"{std_non_prog_code}\n"
99 | "end\n"
100 | )
101 | try:
102 | run_direct_cleaned(_program_define_code, quietly=True)
103 | run_direct(_program_name, quietly=False, inline=True, echo=False)
104 | finally:
105 | run_single(f"capture program drop {_program_name}")
106 |
107 | # %% ../nbs/03_stata_more.ipynb 65
108 | def diverted_stata_output(std_code, runner=None):
109 | if runner is None:
110 | runner = functools.partial(run_direct, quietly=False, inline=True, echo=False)
111 | with redirect_stdout(StringIO()) as diverted:
112 | run_as_program("return add\ncapture log off", prog_def_option_code="rclass")
113 | try:
114 | runner(std_code)
115 | finally:
116 | run_as_program("return add\ncapture log on", prog_def_option_code="rclass")
117 | out = diverted.getvalue()
118 | return out
119 |
120 | # %% ../nbs/03_stata_more.ipynb 71
121 | def diverted_stata_output_quicker(std_non_prog_code):
122 | with redirect_stdout(StringIO()) as diverted:
123 | code = f"return add\ncapture log off\n{std_non_prog_code}\ncapture log on"""
124 | try:
125 | run_as_program(code, prog_def_option_code="rclass")
126 | except SystemError as e:
127 | run_as_program("return add\ncapture log on", prog_def_option_code="rclass")
128 | raise(e)
129 | out = diverted.getvalue()
130 | return out
131 |
132 | # %% ../nbs/03_stata_more.ipynb 76
133 | def var_from_varlist(varlist, stfr=None):
134 | if stfr:
135 | var_code = varlist.strip()
136 | else:
137 | _program_name = "temp_nbstata_varlist_name"
138 | run_direct_cleaned((
139 | f"program define {_program_name}\n"
140 | """ syntax [varlist(default=none)]
141 | foreach var in `varlist' {
142 | disp "`var'"
143 | }
144 | end
145 | """), quietly=True)
146 | try:
147 | var_code = diverted_stata_output_quicker(f"""\
148 | {_program_name} {varlist}
149 | program drop {_program_name}
150 | """).strip()
151 | except Exception as e:
152 | run_sfi(f"capture program drop {_program_name}")
153 | raise(e)
154 | return [c.strip() for c in var_code.split() if c] if var_code else None
155 |
156 | # %% ../nbs/03_stata_more.ipynb 86
157 | def local_names():
158 | run_single("""\
159 | mata : st_local("temp_nbstata_all_locals", invtokens(st_dir("local", "macro", "*")'))""")
160 | out = get_local('temp_nbstata_all_locals')
161 | set_local('temp_nbstata_all_locals', "")
162 | return out.split()
163 |
164 | # %% ../nbs/03_stata_more.ipynb 90
165 | def get_local_dict(_local_names=None):
166 | if _local_names is None:
167 | _local_names = local_names()
168 | return {n: get_local(n) for n in _local_names}
169 |
170 | # %% ../nbs/03_stata_more.ipynb 92
171 | def locals_code_from_dict(preexisting_local_dict):
172 | local_defs = (f"""local {name} `"{preexisting_local_dict[name]}"'"""
173 | for name in preexisting_local_dict)
174 | return "\n".join(local_defs)
175 |
176 | # %% ../nbs/03_stata_more.ipynb 98
177 | def user_expression(input_str):
178 | run_single("tempname output")
179 | try:
180 | run_single(f"local `output': display {input_str}")
181 | except SyntaxError as e:
182 | combined_message = f"{str(e)}\nInvalid Stata '[%fmt] [=]exp' display expression: {input_str}"
183 | raise SyntaxError(combined_message)
184 | return get_local(get_local("output"))
185 |
--------------------------------------------------------------------------------
/nbstata/stata_session.py:
--------------------------------------------------------------------------------
1 | """A class for representing a Stata session"""
2 |
3 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/08_stata_session.ipynb.
4 |
5 | # %% auto 0
6 | __all__ = ['StataSession', 'warn_re_unclosed_comment_block_if_needed']
7 |
8 | # %% ../nbs/08_stata_session.ipynb 4
9 | from .misc_utils import print_red
10 | from .stata import run_direct, get_local, get_scalar
11 | from .stata_more import diverted_stata_output_quicker, local_names, run_sfi
12 | from .stata_more import get_local_dict as _get_local_dict
13 | from nbstata.code_utils import (
14 | valid_single_line_code,
15 | ending_sc_delimiter,
16 | ends_in_comment_block,
17 | ending_code_version,
18 | )
19 | from .noecho import run_as_program_w_locals, run_noecho
20 | from fastcore.basics import patch_to
21 | from textwrap import dedent
22 | import re
23 |
24 | # %% ../nbs/08_stata_session.ipynb 6
25 | class StataSession():
26 | def __init__(self):
27 | """"""
28 | self.sc_delimiter = False
29 | self.code_version = None
30 | self.stata_version = None
31 | self.clear_suggestions()
32 | self._compile_re()
33 |
34 | def clear_suggestions(self):
35 | self.suggestions = None
36 |
37 | def _compile_re(self):
38 | self.matchall = re.compile(
39 | r"\A.*?"
40 | r"^%varlist%(?P.*?)"
41 | r"%globals%(?P.*?)"
42 | #r"%locals%(?P.*?)"
43 | r"%scalars%(?P.*?)"
44 | r"%matrices%(?P.*?)%end%", #"(\Z|---+\s*end)",
45 | flags=re.DOTALL + re.MULTILINE).match
46 |
47 | # Varlist-style matching; applies to most
48 | self.varlist = re.compile(r"(?:\s+)(\S+)", flags=re.MULTILINE)
49 |
50 | # file-style matching
51 | self.filelist = re.compile(r"[\r\n]{1,2}", flags=re.MULTILINE)
52 |
53 | # Clean line-breaks.
54 | self.varclean = re.compile(
55 | r"(?=\s*)[\r\n]{1,2}?^>\s", flags=re.MULTILINE).sub
56 |
57 | # # Match output from mata mata desc
58 | # self.matadesc = re.compile(
59 | # r"(\A.*?---+|---+[\r\n]*\Z)", flags=re.MULTILINE + re.DOTALL)
60 |
61 | # self.matalist = re.compile(
62 | # r"(?:.*?)\s(\S+)\s*$", flags=re.MULTILINE + re.DOTALL)
63 |
64 | # self.mataclean = re.compile(r"\W.*?(\b|$)")
65 | # self.matasearch = re.compile(r"(?P\w.*?(?=\W|\b|$))").search
66 |
67 | # %% ../nbs/08_stata_session.ipynb 7
68 | @patch_to(StataSession)
69 | def refresh_suggestions(self):
70 | self.suggestions = self.get_suggestions()
71 |
72 | # %% ../nbs/08_stata_session.ipynb 8
73 | @patch_to(StataSession)
74 | def _completions(self):
75 | return diverted_stata_output_quicker(dedent("""\
76 | local _temp_completions_while_local_ = 1
77 | while `_temp_completions_while_local_' {
78 | set more off
79 | set trace off
80 | if `"`varlist'"' != "" {
81 | local _temp_completions_varlist_loc_ `"`varlist'"'
82 | }
83 | syntax [varlist]
84 | disp "%varlist%"
85 | disp `"`varlist'"'
86 | macro drop _varlist __temp_completions_while_local_
87 | if `"`_temp_completions_varlist_loc_'"' != "" {
88 | local varlist `"`_temp_completions_varlist_loc_'"'
89 | macro drop __temp_completions_varlist_loc_
90 | }
91 | disp "%globals%"
92 | disp `"`:all globals'"'
93 | *disp "%locals%"
94 | *mata : invtokens(st_dir("local", "macro", "*")')
95 | disp "%scalars%"
96 | disp `"`:all scalars'"'
97 | disp "%matrices%"
98 | disp `"`:all matrices'"'
99 | disp "%end%"
100 | local _temp_completions_while_local_ = 0
101 | }
102 | macro drop _temp_completions_while_local_
103 | """))
104 |
105 | # %% ../nbs/08_stata_session.ipynb 11
106 | @patch_to(StataSession)
107 | def _get_locals(self):
108 | return self.suggestions['locals'] if self.suggestions else local_names()
109 |
110 | # %% ../nbs/08_stata_session.ipynb 15
111 | @patch_to(StataSession)
112 | def get_suggestions(self):
113 | match = self.matchall(self._completions())
114 | suggestions = match.groupdict()
115 | # suggestions['mata'] = self._parse_mata_desc(suggestions['mata'])
116 | # suggestions['programs'] = self._parse_programs_desc(
117 | # suggestions['programs'])
118 | for k, v in suggestions.items():
119 | suggestions[k] = self.varlist.findall(self.varclean('', v))
120 | suggestions['locals'] = self._get_locals()
121 | return suggestions
122 |
123 | # %% ../nbs/08_stata_session.ipynb 19
124 | @patch_to(StataSession)
125 | def get_local_dict(self):
126 | return _get_local_dict(self._get_locals())
127 |
128 | # %% ../nbs/08_stata_session.ipynb 21
129 | @patch_to(StataSession)
130 | def _run_as_program_w_locals(self, std_code):
131 | """After `break_out_prog_blocks`, run noecho, inserting locals when needed"""
132 | return run_as_program_w_locals(std_code, local_dict=self.get_local_dict())
133 |
134 | # %% ../nbs/08_stata_session.ipynb 26
135 | def _run_simple(code, quietly=False, echo=False, sc_delimiter=False):
136 | if sc_delimiter:
137 | code = "#delimit;\n" + code
138 | if len(code.splitlines()) == 1:
139 | code = valid_single_line_code(code)
140 | run_direct(code, quietly=quietly, inline=not quietly, echo=echo)
141 |
142 | # %% ../nbs/08_stata_session.ipynb 29
143 | _final_delimiter_warning = (
144 | "Warning: Code cell (with #delimit; in effect) does not end in ';'. "
145 | "Exported .do script may behave differently from notebook. "
146 | "In v1.0, nbstata may trigger an error instead of just a warning."
147 | )
148 |
149 | # %% ../nbs/08_stata_session.ipynb 30
150 | @patch_to(StataSession)
151 | def _update_ending_delimiter(self, code):
152 | self.sc_delimiter = ending_sc_delimiter(code, self.sc_delimiter)
153 | _final_character = code.strip()[-1]
154 | _code_missing_final_delimiter = (self.sc_delimiter
155 | and _final_character != ';')
156 | if _code_missing_final_delimiter:
157 | print_red(_final_delimiter_warning)
158 |
159 | # %% ../nbs/08_stata_session.ipynb 32
160 | def warn_re_unclosed_comment_block_if_needed(code):
161 | if ends_in_comment_block(code):
162 | print_red("Warning: Code cell ends in a comment block without a "
163 | "closing '*/'. Exported .do script may behave differently "
164 | "from notebook. In v1.0, nbstata may trigger an error "
165 | "instead of just a warning."
166 | )
167 |
168 | # %% ../nbs/08_stata_session.ipynb 35
169 | @patch_to(StataSession)
170 | def _post_run_hook(self, code):
171 | self.clear_suggestions()
172 | if self.stata_version is None:
173 | self.stata_version = f"{get_scalar('c(stata_version)'):0.2f}"
174 | self.code_version = ending_code_version(code, self.sc_delimiter, self.code_version, self.stata_version)
175 | self._update_ending_delimiter(code) # after updating code_version (based on starting sc_delimiter)
176 | warn_re_unclosed_comment_block_if_needed(code)
177 |
178 | # %% ../nbs/08_stata_session.ipynb 36
179 | @patch_to(StataSession)
180 | def dispatch_run(self, code, quietly=False, echo=False, noecho=False):
181 | if self.code_version:
182 | version_prefix = "capture version " + self.code_version + (";" if self.sc_delimiter else "\n")
183 | code = version_prefix + code
184 | if noecho and not quietly:
185 | run_noecho(code, self.sc_delimiter, run_as_prog=self._run_as_program_w_locals)
186 | else:
187 | _run_simple(code, quietly, echo, self.sc_delimiter)
188 | self._post_run_hook(code)
189 |
--------------------------------------------------------------------------------
/settings.ini:
--------------------------------------------------------------------------------
1 | [DEFAULT]
2 | repo = nbstata
3 | lib_name = nbstata
4 | version = 0.8.2
5 | min_python = 3.9
6 | license = gpl3
7 | doc_path = _docs
8 | lib_path = nbstata
9 | nbs_path = nbs
10 | recursive = True
11 | tst_flags = notest
12 | put_version_in_init = True
13 | branch = master
14 | custom_sidebar = True
15 | doc_host = https://hugetim.github.io
16 | doc_baseurl = /nbstata
17 | git_url = https://github.com/hugetim/nbstata
18 | title = nbstata
19 | audience = Science/Research
20 | author = Tim Huegerich
21 | author_email = hugetim@gmail.com
22 | copyright = 2022 onwards, Tim Huegerich
23 | description = Jupyter kernel for Stata built on pystata
24 | keywords = nbdev jupyter notebook python stata
25 | language = English
26 | status = 3
27 | user = hugetim
28 | requirements = jupyter-client ipython ipykernel packaging pandas numpy beautifulsoup4 fastcore pygments>=2.8 ipydatagrid<1.3
29 | black_formatting = False
30 | readme_nb = index.ipynb
31 | allowed_metadata_keys =
32 | allowed_cell_metadata_keys =
33 | jupyter_hooks = True
34 | clean_ids = True
35 | clear_all = False
36 | cell_number = True
37 | skip_procs =
38 |
39 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from pkg_resources import parse_version
2 | from configparser import ConfigParser
3 | import setuptools
4 | assert parse_version(setuptools.__version__)>=parse_version('36.2')
5 |
6 | # note: all settings are in settings.ini; edit there, not here
7 | config = ConfigParser(delimiters=['='])
8 | config.read('settings.ini')
9 | cfg = config['DEFAULT']
10 |
11 | cfg_keys = 'version description keywords author author_email'.split()
12 | expected = cfg_keys + "lib_name user branch license status min_python audience language".split()
13 | for o in expected: assert o in cfg, "missing expected setting: {}".format(o)
14 | setup_cfg = {o:cfg[o] for o in cfg_keys}
15 |
16 | licenses = {
17 | 'apache2': ('Apache Software License 2.0','OSI Approved :: Apache Software License'),
18 | 'mit': ('MIT License', 'OSI Approved :: MIT License'),
19 | 'gpl2': ('GNU General Public License v2', 'OSI Approved :: GNU General Public License v2 (GPLv2)'),
20 | 'gpl3': ('GNU General Public License v3', 'OSI Approved :: GNU General Public License v3 (GPLv3)'),
21 | 'bsd3': ('BSD License', 'OSI Approved :: BSD License'),
22 | }
23 | statuses = [ '1 - Planning', '2 - Pre-Alpha', '3 - Alpha',
24 | '4 - Beta', '5 - Production/Stable', '6 - Mature', '7 - Inactive' ]
25 | py_versions = '3.6 3.7 3.8 3.9 3.10'.split()
26 |
27 | requirements = cfg.get('requirements','').split()
28 | if cfg.get('pip_requirements'): requirements += cfg.get('pip_requirements','').split()
29 | min_python = cfg['min_python']
30 | lic = licenses.get(cfg['license'].lower(), (cfg['license'], None))
31 | dev_requirements = (cfg.get('dev_requirements') or '').split()
32 |
33 | setuptools.setup(
34 | name = cfg['lib_name'],
35 | license = lic[0],
36 | classifiers = [
37 | 'Development Status :: ' + statuses[int(cfg['status'])],
38 | 'Intended Audience :: ' + cfg['audience'].title(),
39 | 'Natural Language :: ' + cfg['language'].title(),
40 | ] + ['Programming Language :: Python :: '+o for o in py_versions[py_versions.index(min_python):]] + (['License :: ' + lic[1] ] if lic[1] else []),
41 | url = cfg['git_url'],
42 | packages = setuptools.find_packages(),
43 | include_package_data = True,
44 | install_requires = requirements,
45 | extras_require={ 'dev': dev_requirements },
46 | setup_requires = ['wheel'], # https://stackoverflow.com/questions/34819221/why-is-python-setup-py-saying-invalid-command-bdist-wheel-on-travis-ci#comment99762029_54833684
47 | dependency_links = cfg.get('dep_links','').split(),
48 | python_requires = '>=' + cfg['min_python'],
49 | long_description = open('README.md').read(),
50 | long_description_content_type = 'text/markdown',
51 | zip_safe = False,
52 | entry_points = {
53 | 'console_scripts': cfg.get('console_scripts','').split(),
54 | 'nbdev': [f'{cfg.get("lib_path")}={cfg.get("lib_path")}._modidx:d']
55 | },
56 | **setup_cfg)
57 |
--------------------------------------------------------------------------------