Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eliminate use of __init__.py files for supplying package docstrings #32508

Closed
mkoeppe opened this issue Sep 12, 2021 · 68 comments
Closed

Eliminate use of __init__.py files for supplying package docstrings #32508

mkoeppe opened this issue Sep 12, 2021 · 68 comments

Comments

@mkoeppe
Copy link
Contributor

mkoeppe commented Sep 12, 2021

... as in src/sage/categories/__init__.py or src/sage/combinat/__init__.py

(part of #32501)

CC: @tscrim @kwankyu @nthiery

Component: refactoring

Author: Kwankyu Lee

Branch: ee4367d

Reviewer: Matthias Koeppe

Issue created by migration from https://trac.sagemath.org/ticket/32508

@mkoeppe mkoeppe added this to the sage-9.5 milestone Sep 12, 2021
@kwankyu
Copy link
Collaborator

kwankyu commented Oct 28, 2021

Branch: u/klee/32508

@kwankyu
Copy link
Collaborator

kwankyu commented Oct 28, 2021

Author: Kwankyu Lee

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Oct 28, 2021

Commit: 5dcb3f5

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Oct 28, 2021

Branch pushed to git repo; I updated commit sha1. New commits:

5dcb3f5Remove docstrings from __init__.py

@kwankyu
Copy link
Collaborator

kwankyu commented Oct 28, 2021

comment:4

Previously

sage: import sage.combinat
sage: sage.combinat.quickref?

worked. Not anymore with the patch. We should

sage: import sage.combinat.quickref
sage: sage.combinat.quickref?

The same with sage.combinat.tutorial

@kwankyu
Copy link
Collaborator

kwankyu commented Oct 29, 2021

comment:5

More seriously, before the patch,

sage: import sage.combinat
sage: help(sage.combinat)

gives a nice ... help. Could we retain this functionality somehow while eliminating the content of sage.combinat.__init__.py?

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Oct 29, 2021

comment:6

I guess with a bit of extra code in src/sage/misc/sagedoc.py, where help is defined, we could do this

@kwankyu
Copy link
Collaborator

kwankyu commented Oct 29, 2021

comment:7

Replying to @mkoeppe:

I guess with a bit of extra code in src/sage/misc/sagedoc.py, where help is defined, we could do this

I cannot guess how. Where should the content go? Do you imagine a file like src/sage/combinat/doc.rst or something?

@kwankyu
Copy link
Collaborator

kwankyu commented Oct 29, 2021

comment:8

src/sage/combinat/__doc__.py would be better name for that.

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Oct 29, 2021

comment:9

+1 on __doc__.

@kwankyu
Copy link
Collaborator

kwankyu commented Oct 30, 2021

comment:10

Replying to @mkoeppe:

+1 on __doc__.

Do you mean __doc__ without .py extension?

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Oct 30, 2021

comment:11

With .py, like you suggested

@kwankyu
Copy link
Collaborator

kwankyu commented Oct 30, 2021

comment:12

Then __doc__.py with a python docstring ("""...""")?

@kwankyu
Copy link
Collaborator

kwankyu commented Oct 30, 2021

comment:13

On the other hand, I wonder if there is a solution(or recommendation) for this problem(package docstring) in python level as this seems a general issue with namespace packages.

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Oct 30, 2021

comment:14

Replying to @kwankyu:

Then __doc__.py with a python docstring ("""...""")?

Yes, that's what I thought

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Oct 30, 2021

comment:15

Replying to @kwankyu:

On the other hand, I wonder if there is a solution(or recommendation) for this problem(package docstring) in python level as this seems a general issue with namespace packages.

I agree, it would be good to find out whether there are existing practices for this in the Python community.

@kwankyu
Copy link
Collaborator

kwankyu commented Nov 1, 2021

comment:16

Replying to @mkoeppe:

Replying to @kwankyu:

Then __doc__.py with a python docstring ("""...""")?

Yes, that's what I thought

One problem with __doc__.py is that it is a fake module for it cannot be imported.

How about this:

src/sage/combinat/__init__/__init__.py

Then the namespace package sage.combinat get to have a subpackage __init__ initialized by __init__.py, which we perhaps may use for "some initialization" as well as for package docstring.

Is this too much or incompatible with the modularization plan?

@kwankyu
Copy link
Collaborator

kwankyu commented Nov 1, 2021

comment:17

Replying to @mkoeppe:

I agree, it would be good to find out whether there are existing practices for this in the Python community.

I googled for it, but found none.

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Nov 1, 2021

comment:18

How about we just use all.py for this purpose?

(Modularized packages such as sagemath-polyhedra (#32432) add files such as all__sagemath_polyhedra.py)

@kwankyu
Copy link
Collaborator

kwankyu commented Nov 1, 2021

comment:19

Replying to @mkoeppe:

How about we just use all.py for this purpose?

(Modularized packages such as sagemath-polyhedra (#32432) add files such as all__sagemath_polyhedra.py)

How does all.py work?

(1) Does it exist by itself and import other all__xxx files into it?

(2) Or is it dynamically created by combining all__xxx files?

Anyway, if it is at src/sage/combinat/all.py, then it cannot be imported. Can it be?

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Nov 1, 2021

comment:20

Yes, all.py exists as a file in Sage. For namespace packages, I am modularizing it by splitting parts of the existing all.py out to all__sagemath_objects.py, all__sagemath_categories.py, all__sagemath_polyhedra etc. When a distribution has another distribution as a dependency, then it imports the dependency's all__... module. For example, sagemath-polyhedra has sagemath-categories as a dependency, so you see the imports: https://github.com/sagemath/sagetrac-mirror/blob/a58ea1f6decbb84102bd341f8b3cc20f3147452b/src/sage/all__sagemath_polyhedra.py&id2=f716a0b366e31bbb546230140489244cfb68390d and https://github.com/sagemath/sagetrac-mirror/blob/a58ea1f6decbb84102bd341f8b3cc20f3147452b/src/sage/rings/all__sagemath_polyhedra.py&id2=f716a0b366e31bbb546230140489244cfb68390d

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Nov 1, 2021

comment:21

Generally, if the all module is present, then it can also be imported.
Likewise, if an all__... module is present, then it can also be imported.

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Nov 1, 2021

comment:22

If sage.misc.sagedoc.help is used on a package P without __doc__ attribute, it could try to import everything in dir(P) whose name begins with all and accumulate the docstrings of the importable modules.

@kwankyu
Copy link
Collaborator

kwankyu commented Nov 1, 2021

comment:23

Replying to @mkoeppe:

Generally, if the all module is present, then it can also be imported.
Likewise, if an all__... module is present, then it can also be imported.

Hmm. if the all module is just below in a namespace package (without __init__.py), I cannot import it. Do I misunderstand something?

For experiment, I created src/sage/namespace/all.py but no src/sage/namespace/__init__.py. I get

sage: import sage.namespace.all
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
<ipython-input-1-f30e99bc2b64> in <module>
----> 1 import sage.namespace.all

ModuleNotFoundError: No module named 'sage.namespace.all'

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Nov 1, 2021

comment:24

The current default build system of the Sage library does not actually understand namespace packages. If you look into the installed Sage library in site-packages, you will see that your file all.py has not been copied there. This will be fixed in #28925.

If you want to test this, you can use ./configure --enable-editable to switch to a different build system (https://wiki.sagemath.org/ReleaseTours/sage-9.3#Editable_.28.22in-place.22.2C_.22develop.22.29_installs_of_the_Sage_library)

@kwankyu
Copy link
Collaborator

kwankyu commented Nov 4, 2021

comment:25

Replying to @mkoeppe:

The current default build system of the Sage library does not actually understand namespace packages. If you look into the installed Sage library in site-packages, you will see that your file all.py has not been copied there. This will be fixed in #28925.

If you want to test this, you can use ./configure --enable-editable to switch to a different build system (https://wiki.sagemath.org/ReleaseTours/sage-9.3#Editable_.28.22in-place.22.2C_.22develop.22.29_installs_of_the_Sage_library)

Thanks. It works.

@tscrim
Copy link
Collaborator

tscrim commented Nov 6, 2021

comment:44

I don't see a reason why all.py should have its own docstring either. I am +1 for using that.

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Nov 6, 2021

comment:45

Replying to @kwankyu:

Replying to @mkoeppe:

This looks great to me. Ready for review?

We are supposed to deal with all __init__ files. No?

Not necessarily. Only the __init__.py files of packages that will become namespace packages. See explanation in #32501 (also related to our previous discussion in #32734 comment:13).

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Nov 6, 2021

comment:46

Re-running the command from the ticket description of #32501 on the present branch:

$ find src/sage -name '__init__.py' | xargs wc -l | grep -v '^ *0'
       2 src/sage/crypto/__init__.py
       3 src/sage/crypto/mq/__init__.py
       3 src/sage/dynamics/__init__.py
       3 src/sage/dynamics/cellular_automata/__init__.py
       1 src/sage/combinat/words/__init__.py
       1 src/sage/combinat/designs/__init__.py
       1 src/sage/combinat/chas/__init__.py
       1 src/sage/combinat/path_tableaux/__init__.py
       1 src/sage/combinat/species/__init__.py
       1 src/sage/combinat/sf/__init__.py
       1 src/sage/combinat/ncsf_qsym/__init__.py
       6 src/sage/combinat/integer_lists/__init__.py
       9 src/sage/combinat/root_system/__init__.py
       3 src/sage/doctest/__init__.py
     847 src/sage/features/__init__.py
      15 src/sage/repl/__init__.py
       4 src/sage/repl/rich_output/__init__.py
      34 src/sage/__init__.py
     358 src/sage/libs/giac/__init__.py
       2 src/sage/libs/ntl/__init__.py
       1 src/sage/libs/gap/__init__.py
     199 src/sage/libs/pari/__init__.py
      55 src/sage/cpython/__init__.py
       2 src/sage/sat/converters/__init__.py
       4 src/sage/sat/solvers/__init__.py
       1 src/sage/modular/quasimodform/__init__.py
       2 src/sage/finance/__init__.py
       2 src/sage/matroids/__init__.py
     114 src/sage/rings/polynomial/pbori/__init__.py
      10 src/sage/modules/with_basis/__init__.py
       2 src/sage/structure/__init__.py
       2 src/sage/media/__init__.py

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Nov 6, 2021

comment:47

To do:

  • src/sage/dynamics/__init__.py - because sage.dynamics.complex_dynamics needs sage.symbolic and everything else does not, see Modularization of sagelib: Break out a separate package sagemath-standard-no-symbolics #32601
  • src/sage/combinat/root_system/__init__.py - because git grep 'cimport' src/sage/combinat/root_system/ reveals from sage.groups.perm_gps.permgroup_element cimport PermutationGroupElement, and git grep 'cimport' src/sage/groups/perm_gps shows dependencies on numerous libraries: gap, flint
  • src/sage/matroids - to play safe given that there's a lot of cimporting

Not necessary to change:

  • src/sage/combinat/words/__init__.py because git grep 'cimport' src/sage/combinat/words does not show compile-time dependencies on libraries, so sage.combinat.words does not have to become a namespace package
  • likewise src/sage/combinat/integer_lists/__init__.py
  • src/sage/sat/... because git grep 'cimport' src/sage/sat/ is empty
  • src/sage/features/, src/sage/libs/..., src/sage/repl because they will definitely not become namespace packages

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Nov 8, 2021

Changed commit from d7085db to b7d2be9

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Nov 8, 2021

Branch pushed to git repo; I updated commit sha1. New commits:

b7d2be9More refactoring of `__init__` files

@kwankyu
Copy link
Collaborator

kwankyu commented Nov 8, 2021

New commits:

b7d2be9More refactoring of `__init__` files

@kwankyu
Copy link
Collaborator

kwankyu commented Nov 8, 2021

comment:50

Re-running the command from the ticket description of #32501 on the present branch:

$ find src/sage -name '__init__.py' | xargs wc -l | grep -v '^ *0'
       2 src/sage/crypto/__init__.py
       3 src/sage/crypto/mq/__init__.py
       3 src/sage/dynamics/cellular_automata/__init__.py
      41 src/sage/combinat/words/__init__.py
       1 src/sage/combinat/designs/__init__.py
       1 src/sage/combinat/chas/__init__.py
       1 src/sage/combinat/path_tableaux/__init__.py
       1 src/sage/combinat/species/__init__.py
       1 src/sage/combinat/sf/__init__.py
       1 src/sage/combinat/ncsf_qsym/__init__.py
       6 src/sage/combinat/integer_lists/__init__.py
       9 src/sage/combinat/root_system/__init__.py
       3 src/sage/doctest/__init__.py
     847 src/sage/features/__init__.py
      15 src/sage/repl/__init__.py
       4 src/sage/repl/rich_output/__init__.py
      34 src/sage/__init__.py
     358 src/sage/libs/giac/__init__.py
       2 src/sage/libs/ntl/__init__.py
       1 src/sage/libs/gap/__init__.py
     199 src/sage/libs/pari/__init__.py
      55 src/sage/cpython/__init__.py
       2 src/sage/sat/converters/__init__.py
       4 src/sage/sat/solvers/__init__.py
       1 src/sage/modular/quasimodform/__init__.py
       2 src/sage/finance/__init__.py
       2 src/sage/matroids/__init__.py
     114 src/sage/rings/polynomial/pbori/__init__.py
      10 src/sage/modules/with_basis/__init__.py
       2 src/sage/structure/__init__.py
       2 src/sage/media/__init__.py
    1727 total

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Nov 8, 2021

Reviewer: Matthias Koeppe

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Nov 8, 2021

comment:52

Looking great but I'm getting a docbuild error:

[dochtml] [combinat ] /Users/mkoeppe/s/sage/sage-rebasing/local/lib/python3.9/site-packages/sage/combinat/crystals/crystals.py:docstring of sage.combinat.crystals.crystals:122: WARNING: undefined label: sage.combinat.crystals
[dochtml] [combinat ] /Users/mkoeppe/s/sage/sage-rebasing/local/lib/python3.9/site-packages/sage/combinat/quickref.py:docstring of sage.combinat.quickref:48: WARNING: undefined label: sage.combinat.root_system
[dochtml] [combinat ] /Users/mkoeppe/s/sage/sage-rebasing/local/lib/python3.9/site-packages/sage/combinat/quickref.py:docstring of sage.combinat.quickref:54: WARNING: undefined label: sage.combinat.crystals
[dochtml] [combinat ] /Users/mkoeppe/s/sage/sage-rebasing/local/lib/python3.9/site-packages/sage/combinat/quickref.py:docstring of sage.combinat.quickref:71: WARNING: undefined label: sage.combinat.posets
[dochtml] [combinat ] /Users/mkoeppe/s/sage/sage-rebasing/local/lib/python3.9/site-packages/sage/combinat/root_system/root_system.py:docstring of sage.combinat.root_system.root_system:3: WARNING: undefined label: sage.combinat.root_system
[dochtml] [combinat ] dumping search index in English (code: en)... done
[dochtml] [combinat ] The HTML pages are in local/share/doc/sage/html/en/reference/combinat.
[dochtml] Error building the documentation.

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Nov 8, 2021

Branch pushed to git repo; I updated commit sha1. New commits:

ee4367dFixes for doctest failures

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Nov 8, 2021

Changed commit from b7d2be9 to ee4367d

@kwankyu
Copy link
Collaborator

kwankyu commented Nov 8, 2021

comment:54

Strange that those errors got slipped in unnoticed...

@mkoeppe
Copy link
Contributor Author

mkoeppe commented Nov 8, 2021

comment:55

It's working now, and the built documentation also looks fine. Thanks!

@nthiery
Copy link
Contributor

nthiery commented Nov 8, 2021

comment:56

Looks good indeed.

Just one thing (that could possibly go in a follow up
ticket): would there be a way to maintain the validity
of cross-references like :ref:sage.combinat ?

First because it's easier to remember, and does not
reveal an implementation detail (that the doc is actually
stored in sage/combinat/all.py).

But also because, thanks to intersphinx links these
cross-references can be used from documents outside
of the Sage documentation, and this is breaking
backward compatibility for them.

Thanks,

@kwankyu
Copy link
Collaborator

kwankyu commented Nov 9, 2021

comment:57

Replying to @nthiery:

Looks good indeed.

Just one thing (that could possibly go in a follow up
ticket): would there be a way to maintain the validity
of cross-references like :ref:sage.combinat ?

If you compare "before" and "after" below, we see that Sphinx recognizes regular package and treat it appropriately. Hence I think we need to wait until Sphinx gets equipped with some mechanism to deal with namespace packages appropriately. It seems that Sphinx does not have such a mechanism yet (as far as I know by googling).


Before the patch: this line

.. toctree::    

    sage/combinat/__init__   

generates

.. nodoctest

.. _sage.combinat:

Combinatorics
=============

.. This file has been autogenerated.


.. automodule:: sage.combinat.__init__
   :members:
   :undoc-members:
   :show-inheritance:   

After the patch: this line

.. toctree::    

    sage/combinat/all  

generates

.. nodoctest

.. _sage.combinat.all:

Combinatorics
=============

.. This file has been autogenerated.


.. automodule:: sage.combinat.all
   :members:
   :undoc-members:
   :show-inheritance:  

@vbraun
Copy link
Member

vbraun commented Nov 12, 2021

Changed branch from public/32508 to ee4367d

@jhpalmieri
Copy link
Member

Changed commit from ee4367d to none

@jhpalmieri
Copy link
Member

comment:59

The addition of

from . import quickref, tutorial

to sage.combinat.all broke the top-level tutorial() command. Maybe that command should be removed anyway, especially since no one has reported its loss, but in any case, right now tutorial() is broken as a command in Sage.

@kwankyu
Copy link
Collaborator

kwankyu commented May 29, 2022

comment:60

Replying to @jhpalmieri:

The addition of

from . import quickref, tutorial

to sage.combinat.all broke the top-level tutorial() command. Maybe that command should be removed anyway, especially since no one has reported its loss, but in any case, right now tutorial() is broken as a command in Sage.

After 7 months, I don't remember why I put that line into all.py. Simply removing that line would solve the problem. Yes? We don't need quickref either in the global name space.

@nthiery
Copy link
Contributor

nthiery commented May 29, 2022

comment:61

Indeed, there is no reason for quickref and tutorial to be in the global namespace.
All we need is to be able to do sage.combinat? sage.combinat.tutorial? and sage.combinat.quickref?

@kwankyu
Copy link
Collaborator

kwankyu commented May 29, 2022

comment:62

See #33933.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants