Package: gist-import
gist-importGitHub Gist are handy snippets, which are meant to be copy-pasted into one's code... but what if you could import them?Installationpipinstallgist-importUsageThe main class isGistImporter. This allows one to import cleanly a gist into a project.
Namely it retrieves the gist and executes it with any given additional named arguments
in a local context thus not polluting the global namespace (see background).
If required it does a special import of the gist's imports (<3.8) thanks toget_imports_in_codeblock.
The variables in that namespace are then available as items.fromgist_importimportGistImportergi=GistImporter('24d9a319d05773ae219dd678a3aa11be')Safeguard=gi['Safeguard']Additionally, a module can be made:fromgist_importimportGistImporterfromtypesimportModuleTypegi=GistImporter('24d9a319d05773ae219dd678a3aa11be')safeguard:ModuleType=gi.to_module('safeguard')# which can be used elsewhere...fromsafeguardimportSafeguardIn the case of a codeblock that fails because of, say a terminal line, a warning saying so will be printed.fromgist_importimportGistImportergi=GistImporter('👾👾👾',foo=1,bar=2)# a gist with a syntax error on the last line will not run...clean_code=gi.codeblock.split('\n')[:-1]# skip last linegi=GistImporter.from_code_block(clean_code,foo=1,bar=2)baz=gi['baz']The functionget_imports_in_codeblockis a helper function that finds any imports and return the modules as
values of a dictionary.There are four entry points:Gist id (regular initialisation)codeblock as discussed viafrom_code_blocktext file url viafrom_urlGitHub page url viafrom_githubBackgroundGitHub Gistare snippets that aren't part of regular GitHub,
they are intended to be used in blogs etc. to show code-hightlighting by embedding the gist.
If one were to use in it Python, one should copy-paste it or do something convoluted.Say the gist is nice and isolated, with all the correct imports,
then this works fine.importrequestsresponse=requests.get('https://gist.github.com/matteoferla/d0daee35fe6f598bc720ce0eeebbac97/raw/6f7ba15dde86f1066629af61e0724dbe6a62cceb/transmute_FindMCS_parameters.py')response.raise_for_status()exec(response.text)transmute_FindMCS_parameters()..but things get messy quickly. As seen in the comment in this Gist:https://gist.github.com/matteoferla/24d9a319d05773ae219dd678a3aa11beAs a placeholder for theresponse.textin the following examples a string is used.The following works:faux_gist:str='greet = lambda who: print(f"Hello{who}")'# pretend this is the gist from `response.text`exec(faux_gist)greet('World')But as soon as one moves away from the global namespace issues happen.
This would be needed were one to want to wrap the gist execution in a function
to avoid global namespace pollution.This will fail:defnonglobal(faux_gist:str):exec(faux_gist)returngreet# NameError: name 'greet' is not defined# assign to a variable with a different namesalute=nonglobal('greet = lambda who: print(f"Hello{who}")')salute('Mars')But this will pollute the global namespace:defnonglobal(faux_gist:str):exec(faux_gist,globals())returngreet# NameError: name 'greet' is not defined# assign to a variable with a different namesalute=nonglobal('greet = lambda who: print(f"Hello{who}")')salute('Mars')assert'greet'notinglobals()# AssertionError:Asglobals()returns the actual global namespace, not a copy.
If a copy is passed the copy will have the new variable and the namespace will not be polluted.defnonglobal(faux_gist:str):faux_globals={**globals(),**locals()}exec(faux_gist,faux_globals)returnfaux_globals['beware']# assign to a variable with a different nameenguard=nonglobal('import warnings; beware = lambda who: warnings.warn(f"Beware{who}")')enguard('Mars')assert'beware'notinglobals()This used to not work in Python 3.7 due to the import being lost.
Hence the functionget_imports_in_codeblockwhich returns a dictionary of string to module
of the imports in the codeblock —star imports included.