I read all (most) of the documentation a few times, but couldn’t see clearly how all works out or what translates to what.
I have never built a python module before using nbdev, so there is a knowledge gap I need to fill.
So here are my thoughts / experiences / lessons to self.
Running the notebook exports Python modules because of this cell.
Yes and no.
If you run that cell in the notebook, the corresponding module (and all other modules in that project) will be recreated, BUT only if the notebook file has changed. And with changed I mean ‘you change it and save the file’.
Do changes
Save file
run the cell
.py module file is recreated
I guess it applies
if the file hasn't changed from last time:
do nothing
else:
recreate modules
If you want to see the notebook and the corresponding module_name.py side by side in Jupyter.
I don’t believe that’s correct. You do indeed have to save the notebook for the export to see changes, but there’s no conditional of the type you mentioned.
The issue arises because the @patch decorator expects to work with a callable object (like a function or method), and combining it directly with @property results in a property object, which causes the TypeError.
To work around this, you can use the patch_property from the fastcore library instead. This is designed to handle properties specifically.
As of 2024-06-06
UserWarning: patch_property is deprecated and will be removed;
use patch(as_prop=True) instead
but GPT put me on the right track.
The @patch is really handy when you want to add some functionality to a class, but you leverage partially in the class to create it.
So you can
Create class_A (missing new_func)
Create new_func ( leveraging on existing class_A )
@patch class_A which now will use new_func somehow
Very clean, without using mock implementations. Simply beautiful.
patch_to(cls, as_prop=False, cls_method=False):
"Decorator: add `f` to `cls`"
patch(f=None, *, as_prop=False, cls_method=False):
"Decorator: add `f` to the first parameter's class (based on f's type annotations)"
In other words, @patch or patch gets the class name from the type annotation and then calls @patch_to | patch_to with it. They do the same.
When using fastai’s nbdev, the choice between #|export and #|exporti for cells containing only imports depends on how you want those imports to be handled in the generated module. Here’s the explanation:
Export Options
#|export
Using #|export for import cells will include those imports in the generated Python module. This means that when someone imports your module, they will also implicitly import the libraries you’ve specified.
#|exporti
The #|exporti directive, where ‘i’ stands for ‘import’, is specifically designed for handling import statements. When you use #|exporti, the imports are included in the generated module but are not re-exported.
Recommendation
For cells that only contain import statements, it’s generally recommended to use #|exporti rather than #|export. Here’s why:
Cleaner API: Using #|exporti keeps your module’s public API cleaner by not re-exporting third-party libraries.
Explicit imports: It encourages users of your module to explicitly import the libraries they need, which can lead to more maintainable and understandable code.
Avoiding conflicts: It prevents potential naming conflicts that could arise from re-exporting common library names.
Performance: In some cases, it can lead to slightly faster import times for your module, as it doesn’t need to import and re-export unnecessary libraries.
By using #|exporti for your import cells, you ensure that the necessary imports are available for your module’s internal use without affecting the module’s public interface.