sagemath / sage

Main repository of SageMath
https://www.sagemath.org
Other
1.47k stars 487 forks source link

Remove Python access from Parent._element_constructor #23881

Closed jdemeyer closed 7 years ago

jdemeyer commented 7 years ago

In order to avoid confusion between _element_constructor_ (a public interface) and _element_constructor (a private attribute), we change the latter from cdef public to cdef. The same for _element_init_pass_parent. This way, those attributes can no longer be accessed from Python.

Depends on #23882 Depends on #23884 Depends on #23894 Depends on #23899 Depends on #23905 Depends on #23907 Depends on #23914 Depends on #24033

CC: @simon-king-jena @koffie

Component: coercion

Author: Jeroen Demeyer, Simon King

Branch: 41614a7

Reviewer: Travis Scrimshaw

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

simon-king-jena commented 7 years ago

Replying to @jdemeyer:

In order to avoid confusion between _element_constructor_ (a public interface) and _element_constructor (a private attribute), we rename the latter.

I am not sure whether _element_constructor is needed at all. Since _element_constructor_ has underscores, it is private to a certain extent, but still public enough to be a hook for customisation. That's why I believe a default implementation (which is allowed to override) of _element_constructor_ should be a replacement of the current _element_constructor.

jdemeyer commented 7 years ago
comment:2

Replying to @simon-king-jena:

I am not sure whether _element_constructor is needed at all.

I never claimed that it is. Please see my last comments on #23880.

simon-king-jena commented 7 years ago
comment:3

Replying to @jdemeyer:

Replying to @simon-king-jena:

I am not sure whether _element_constructor is needed at all.

I never claimed that it is. Please see my last comments on #23880.

So, your suggestion is to rename _element_constructor into __construct_element in order to learn how things work? This could indeed have some benefits: Making it a very private argument, we would see errors if something tries to call it outside of sage.structure.parents (due to name mangling). This would indicate how much work it would be to avoid the indirection and directly replace the internal element constructor by the hook.

jdemeyer commented 7 years ago
comment:4

Replying to @simon-king-jena:

Making it a very private argument, we would see errors if something tries to call it outside of sage.structure.parents (due to name mangling).

Note that a Cython cdef classes doesn't do mangling, only Python classes do. So the split would be on the Cython/Python border.

simon-king-jena commented 7 years ago
comment:5

I guess the following is what we have to change:

$ grep "_element_constructor " -r src/sage
src/sage/combinat/subword.py:        if self._element_constructor is tuple:
src/sage/combinat/sf/new_kschur.py:    _element_constructor = _element_constructor_
src/sage/combinat/sf/new_kschur.py:    _element_constructor = _element_constructor_
src/sage/combinat/sf/new_kschur.py:    _element_constructor = _element_constructor_
src/sage/combinat/sf/new_kschur.py:    _element_constructor = _element_constructor_
src/sage/combinat/sf/k_dual.py:    _element_constructor = _element_constructor_
src/sage/categories/homset.py:        if self._element_constructor is None:
src/sage/categories/sets_cat.py:        # Todo: simplify the _element_constructor definition logic
src/sage/structure/coerce_maps.pyx:        if (<Parent>codomain)._element_constructor is None:
src/sage/structure/parent_base.pyx:    if p._element_constructor is not None:
src/sage/structure/parent.pyx:            self._element_constructor = element_constructor
src/sage/structure/parent.pyx:            sage: k = GF(5); k._element_constructor # indirect doctest
src/sage/structure/parent.pyx:        self._element_constructor = _element_constructor_
src/sage/structure/parent.pyx:        if self._element_constructor is None:
src/sage/structure/parent.pyx:                self._element_constructor = self._element_constructor_
src/sage/structure/parent.pyx:        self._element_constructor = element_constructor
src/sage/structure/parent_gens.pyx:    if p._element_constructor is not None:
src/sage/structure/parent_gens.pyx:        if self._element_constructor is not None:
src/sage/structure/parent_gens.pyx:        if self._element_constructor is not None:
src/sage/structure/parent_gens.pyx:        if self._element_constructor is not None:
src/sage/structure/parent_old.pyx:    if p._element_constructor is not None:
src/sage/structure/parent_old.pyx:        if self._element_constructor is not None:
src/sage/structure/parent_old.pyx:        if self._element_constructor is not None:
src/sage/structure/parent_old.pyx:        if self._element_constructor is None:
src/sage/structure/parent_old.pyx:        if self._element_constructor is None:
src/sage/structure/parent_old.pyx:        if self._element_constructor is None:
src/sage/structure/parent_old.pyx:        if self._element_constructor is None:
src/sage/structure/parent_old.pyx:                self._element_constructor = self._element_constructor_

and

$ grep "_element_constructor(" -r src/sage
src/sage/sets/cartesian_product.py:            ``self._element_constructor(elements)``.
src/sage/rings/padics/CA_template.pxi:        self._zero = R._element_constructor(R, 0)
src/sage/rings/padics/CA_template.pxi:        self._zero = R._element_constructor(R, 0)
src/sage/rings/padics/FM_template.pxi:        self._zero = R._element_constructor(R, 0)
src/sage/rings/padics/FM_template.pxi:        self._zero = R._element_constructor(R, 0)
src/sage/rings/padics/FP_template.pxi:        self._zero = <FPElement?>R._element_constructor(R, 0)
src/sage/rings/padics/FP_template.pxi:        self._zero = R._element_constructor(R, 0)
src/sage/rings/padics/FP_template.pxi:        self._zero = R._element_constructor(R, 0)
src/sage/rings/padics/CR_template.pxi:        self._zero = <CRElement?>R._element_constructor(R, 0)
src/sage/rings/padics/CR_template.pxi:        self._zero = R._element_constructor(R, 0)
src/sage/rings/padics/CR_template.pxi:        self._zero = R._element_constructor(R, 0)
src/sage/rings/finite_rings/hom_prime_finite_field.pyx:            return self._codomain._element_constructor(x)
src/sage/rings/finite_rings/hom_prime_finite_field.pyx:        return self._codomain._element_constructor(x)
src/sage/rings/polynomial/polynomial_element.pyx:            return self.__class__(P,[a], check=False) #P._element_constructor(a, check=False)
src/sage/combinat/integer_lists/lists.py:            sage: L._element_constructor([1,2,3])
src/sage/geometry/linear_expression.py:            sage: L._element_constructor([1, 2, 3], 4)
src/sage/geometry/linear_expression.py:            sage: L._element_constructor(vector(ZZ, [1, 2, 3]), 4)
src/sage/geometry/linear_expression.py:            sage: L._element_constructor(4)
src/sage/geometry/linear_expression.py:            sage: L._element_constructor(vector([4, 1, 2, 3]))
src/sage/geometry/linear_expression.py:            sage: L._element_constructor(m)
src/sage/geometry/hyperplane_arrangement/arrangement.py:            sage: L._element_constructor(polytopes.hypercube(2))
src/sage/categories/morphism.pyx:            return C._element_constructor(C, x, *args, **kwds)
src/sage/categories/morphism.pyx:            return C._element_constructor(x, *args, **kwds)
src/sage/modular/dirichlet.py:        self._set_element_constructor()
src/sage/structure/coerce_maps.pyx:            return C._element_constructor(C, x)
src/sage/structure/coerce_maps.pyx:                    return C._element_constructor(C, x)
src/sage/structure/coerce_maps.pyx:                    return C._element_constructor(C, x, **kwds)
src/sage/structure/coerce_maps.pyx:                    return C._element_constructor(C, x, *args)
src/sage/structure/coerce_maps.pyx:                    return C._element_constructor(C, x, *args, **kwds)
src/sage/structure/coerce_maps.pyx:            return C._element_constructor(x)
src/sage/structure/coerce_maps.pyx:                    return C._element_constructor(x)
src/sage/structure/coerce_maps.pyx:                    return C._element_constructor(x, **kwds)
src/sage/structure/coerce_maps.pyx:                    return C._element_constructor(x, *args)
src/sage/structure/coerce_maps.pyx:                    return C._element_constructor(x, *args, **kwds)
src/sage/structure/coerce_maps.pyx:        return C._element_constructor(self._call_(x), *args, **kwds)
src/sage/structure/parent.pyx:            self._set_element_constructor()
src/sage/structure/parent.pyx:    def _set_element_constructor(self):
src/sage/structure/parent_old.pyx:        self._set_element_constructor()
simon-king-jena commented 7 years ago
comment:6

Replying to @simon-king-jena:

$ grep "_element_constructor " -r src/sage
...
src/sage/combinat/sf/new_kschur.py:    _element_constructor = _element_constructor_
src/sage/combinat/sf/new_kschur.py:    _element_constructor = _element_constructor_
src/sage/combinat/sf/new_kschur.py:    _element_constructor = _element_constructor_
src/sage/combinat/sf/new_kschur.py:    _element_constructor = _element_constructor_
src/sage/combinat/sf/k_dual.py:    _element_constructor = _element_constructor_

The above is strange. Shouldn't Parent._set_element_constructor assign _element_constructor_ to _element_constructor anyway? So, why do it on class level?

$ grep "_element_constructor(" -r src/sage
...
src/sage/rings/padics/CA_template.pxi:        self._zero = R._element_constructor(R, 0)
src/sage/rings/padics/CA_template.pxi:        self._zero = R._element_constructor(R, 0)
src/sage/rings/padics/FM_template.pxi:        self._zero = R._element_constructor(R, 0)
src/sage/rings/padics/FM_template.pxi:        self._zero = R._element_constructor(R, 0)
src/sage/rings/padics/FP_template.pxi:        self._zero = <FPElement?>R._element_constructor(R, 0)
src/sage/rings/padics/FP_template.pxi:        self._zero = R._element_constructor(R, 0)
src/sage/rings/padics/FP_template.pxi:        self._zero = R._element_constructor(R, 0)
src/sage/rings/padics/CR_template.pxi:        self._zero = <CRElement?>R._element_constructor(R, 0)
src/sage/rings/padics/CR_template.pxi:        self._zero = R._element_constructor(R, 0)
src/sage/rings/padics/CR_template.pxi:        self._zero = R._element_constructor(R, 0)

Here we could probably use the default implementation of zero().

src/sage/combinat/integer_lists/lists.py:            sage: L._element_constructor([1,2,3])
src/sage/geometry/linear_expression.py:            sage: L._element_constructor([1, 2, 3], 4)
src/sage/geometry/linear_expression.py:            sage: L._element_constructor(vector(ZZ, [1, 2, 3]), 4)
src/sage/geometry/linear_expression.py:            sage: L._element_constructor(4)
src/sage/geometry/linear_expression.py:            sage: L._element_constructor(vector([4, 1, 2, 3]))
src/sage/geometry/linear_expression.py:            sage: L._element_constructor(m)
src/sage/geometry/hyperplane_arrangement/arrangement.py:            sage: L._element_constructor(polytopes.hypercube(2))

An internal attribute such as _element_constructor shouldn't be mentioned in the docs.

simon-king-jena commented 7 years ago
comment:7

Replying to @simon-king-jena:

src/sage/combinat/subword.py:        if self._element_constructor is tuple:

Why should it be a tuple?? Very strange.

simon-king-jena commented 7 years ago
comment:8

Replying to @simon-king-jena:

Replying to @simon-king-jena:

src/sage/combinat/subword.py:        if self._element_constructor is tuple:

Why should it be a tuple?? Very strange.

I see: It is not a tuple, but it is the class .

That's probably a tricky case: The elements are tuples, so, the elements are not instances of sage.structure.element.Element, and in particular it is one of the cases where we do not want to pass the parent. It should be possible to customize _element_constructor_ in that case, but there would be a slow-down, compared with calling the tuple constructor directly (without the additional step of a method that calls the tuple constructor).

mantepse commented 7 years ago
comment:9

Replying to @simon-king-jena:

Replying to @simon-king-jena:

src/sage/combinat/subword.py:        if self._element_constructor is tuple:

Why should it be a tuple?? Very strange.

looking at the last few lines of https://github.com/sagemath/sagetrac-mirror/commit/a2915642f7f5357e0d374aaf007680c263cd164c maybe that should have been self._build

simon-king-jena commented 7 years ago
comment:10

Replying to @mantepse:

looking at the last few lines of https://github.com/sagemath/sagetrac-mirror/commit/a2915642f7f5357e0d374aaf007680c263cd164c maybe that should have been self._build

Probably you are right. And the fact that it doesn't fail indicates that it isn't sufficiently tested.

mantepse commented 7 years ago
comment:11

Replying to @simon-king-jena:

Probably you are right. And the fact that it doesn't fail indicates that it isn't sufficiently tested.

I just checked, it's just a speedup of a factor 4 - I guess there is no performance testing in sage:

sage: S = Subwords([randint(1,10) for i in range(100)], 3, element_constructor=tuple)
sage: %timeit len([x for x in S.__iter__()])
1 loop, best of 3: 322 ms per loop
sage: %timeit len([x for x in S.__iter_new__()])
10 loops, best of 3: 85.7 ms per loop

sage: A = [x for x in S.__iter_new__()]
sage: B = [x for x in S.__iter__()]
sage: A == B
True
simon-king-jena commented 7 years ago
comment:12

Replying to @mantepse:

sage: A = [x for x in S.__iter_new__()]

What is __iter_new__?

mantepse commented 7 years ago
comment:13

Replying to @simon-king-jena:

Replying to @mantepse:

sage: A = [x for x in S.__iter_new__()]

What is __iter_new__?

That's the new (correct) implementation - I always put in old and new in parallel for the comparison. That's #23882 now.

simon-king-jena commented 7 years ago

Dependencies: #23882

simon-king-jena commented 7 years ago
comment:15

On top of #23882, I propose to first remove _element_constructor from the docs, before we rename it in the code.

mantepse commented 7 years ago
comment:16

I think the following grep is also needed:

grep -r --color -nH "_element_constructor$"  *
categories/sets_cat.py:964:                sage: A._element_constructor
categories/sets_cat.py:970:                sage: B._element_constructor
combinat/integer_lists/lists.py:173:        a = self._element_constructor
combinat/integer_lists/lists.py:174:        b = other._element_constructor
combinat/integer_lists/lists.py:281:            sage: L._element_constructor
structure/parent.pxd:13:    cdef public _element_constructor
structure/parent.pyx:776:        d['_element_constructor'] = self._element_constructor
structure/parent.pyx:1869:                element_constructor = (<Parent>S)._element_constructor
mantepse commented 7 years ago
comment:17

and another one:

grep -r --color -nH "_element_constructor ="  *
combinat/sf/k_dual.py:923:    _element_constructor = _element_constructor_
combinat/sf/new_kschur.py:1021:    _element_constructor = _element_constructor_
combinat/sf/new_kschur.py:1279:    _element_constructor = _element_constructor_
combinat/sf/new_kschur.py:1395:    _element_constructor = _element_constructor_
combinat/sf/new_kschur.py:1472:    _element_constructor = _element_constructor_
structure/parent_old.pyx:397:                self._element_constructor = self._element_constructor_
structure/parent.pyx:316:            self._element_constructor = element_constructor
structure/parent.pyx:607:        self._element_constructor = _element_constructor_
structure/parent.pyx:914:                self._element_constructor = self._element_constructor_
structure/parent.pyx:1452:        self._element_constructor = element_constructor
simon-king-jena commented 7 years ago
comment:18

The documentation already is a lot worse than I thought:

$ grep "_element_constructor" -r src/sage | grep "sage\:"
src/sage/schemes/toric/variety.py:            sage: H._element_constructor_(1)
src/sage/schemes/toric/divisor.py:            sage: TDiv._element_constructor_([ (1,P2.gen(2)) ])
src/sage/schemes/toric/divisor.py:            sage: Cl._element_constructor_(D)
src/sage/schemes/toric/homset.py:            sage: hom_set(fm)     # calls hom_set._element_constructor_()
src/sage/schemes/product_projective/homset.py:            sage: P(QQ)._element_constructor_([4, 2, 2, 0])
src/sage/schemes/generic/homset.py:            sage: H._element_constructor_(f)
src/sage/schemes/generic/homset.py:            sage: F_points._element_constructor_([4,2*a])
src/sage/schemes/projective/projective_homset.py:            sage: X._element_constructor_([0,1,0])
src/sage/schemes/affine/affine_homset.py:            sage: H._element_constructor_(ring_hom)
src/sage/numerical/linear_tensor.py:            sage: LT._element_constructor_(123)
src/sage/numerical/linear_functions.pyx:            sage: LF._element_constructor_(123)
src/sage/numerical/linear_functions.pyx:            sage: LC._element_constructor_(1, 2)
src/sage/numerical/linear_tensor_constraints.py:            sage: LTC._element_constructor_(1, 2, True)
src/sage/sets/disjoint_union_enumerated_sets.py:            sage: U._element_constructor_
src/sage/sets/disjoint_union_enumerated_sets.py:            sage: U._element_constructor_
src/sage/sets/disjoint_union_enumerated_sets.py:            sage: p = X._element_constructor_((0, []))  # indirect doctest
src/sage/sets/non_negative_integers.py:            sage: NN._element_constructor_(42)
src/sage/sets/non_negative_integers.py:            sage: NN._element_constructor_(-5)
src/sage/sets/non_negative_integers.py:            sage: NN._element_constructor_(x)
src/sage/groups/affine_gps/euclidean_group.py:            sage: E3._element_constructor_check(A, b)
src/sage/groups/affine_gps/euclidean_group.py:            sage: E3._element_constructor_check(A, b)
src/sage/groups/affine_gps/affine_group.py:            sage: Aff3._element_constructor_check(A, b)
src/sage/groups/affine_gps/affine_group.py:            sage: Aff3._element_constructor_check(A, b)
src/sage/rings/fraction_field.py:            sage: F._element_constructor_(1)
src/sage/rings/fraction_field.py:            sage: F._element_constructor_(F.gen(0)/F.gen(1))
src/sage/rings/fraction_field.py:            sage: F._element_constructor_('1 + x/y')
src/sage/rings/fraction_field.py:            sage: F._element_constructor_(x/y)
src/sage/rings/fraction_field.py:            sage: K._element_constructor_(2, 'x+y')
src/sage/rings/fraction_field.py:            sage: K._element_constructor_(1, 'z')
src/sage/rings/finite_rings/residue_field.pyx:            sage: ResidueField_generic._element_constructor_(F, i)
src/sage/rings/finite_rings/residue_field.pyx:            sage: ResidueField_generic._element_constructor_(F, GF(13)(8))
src/sage/rings/polynomial/plural.pyx:            sage: P._element_constructor_(1/2)
src/sage/rings/polynomial/plural.pyx:            sage: P._element_constructor_(x*y)
src/sage/rings/polynomial/plural.pyx:            sage: P._element_constructor_(y*x)
src/sage/rings/polynomial/plural.pyx:            sage: P._element_constructor_(1)
src/sage/rings/polynomial/plural.pyx:            sage: P._element_constructor_(0)
src/sage/rings/multi_power_series_ring.py:            sage: M._element_constructor_(m)
src/sage/rings/multi_power_series_ring.py:            sage: M._element_constructor_(p)
src/sage/rings/multi_power_series_ring.py:            sage: M._element_constructor_(p).parent()
src/sage/rings/function_field/function_field.py:            sage: L._element_constructor_(L.polynomial_ring().gen())
src/sage/rings/function_field/function_field.py:            sage: a = K._element_constructor_(K.maximal_order().gen()); a
src/sage/rings/function_field/function_field_order.py:            sage: K.maximal_order()._element_constructor_(x)
src/sage/rings/function_field/function_field_order.py:            sage: O._element_constructor_(y)
src/sage/rings/function_field/function_field_order.py:            sage: O._element_constructor_(1/y)
src/sage/combinat/rooted_tree.py:            sage: B._element_constructor_([])
src/sage/combinat/root_system/weyl_characters.py:            sage: A2._element_constructor_([2,1,0])
src/sage/combinat/root_system/weyl_characters.py:            sage: A2._element_constructor_([2,1,0])
src/sage/combinat/root_system/associahedron.py:            sage: parent._element_constructor_(['A',2])
src/sage/combinat/finite_class.py:            sage: F._element_constructor_(1)
src/sage/combinat/partition_tuple.py:            sage: PartitionTuples._element_constructor_(PartitionTuples(), [[2,1],[3,2],[1,1,1]])
src/sage/combinat/integer_lists/lists.py:            sage: L._element_constructor_nocheck([1,2,3])
src/sage/combinat/integer_lists/lists.py:            sage: L._element_constructor
src/sage/combinat/integer_lists/lists.py:            sage: L._element_constructor([1,2,3])
src/sage/combinat/binary_tree.py:            sage: B._element_constructor_([])
src/sage/combinat/binary_tree.py:            sage: FB._element_constructor_([])
src/sage/combinat/schubert_polynomial.py:            sage: X._element_constructor_([2,1,3])
src/sage/combinat/schubert_polynomial.py:            sage: X._element_constructor_(Permutation([2,1,3]))
src/sage/combinat/schubert_polynomial.py:            sage: X._element_constructor_([1,2,1])
src/sage/combinat/sf/classical.py:            sage: s._element_constructor_(McdJ(s[2,1]))
src/sage/combinat/posets/linear_extensions.py:            sage: x = L._element_constructor_([1,2,4,3]); x
src/sage/combinat/posets/linear_extensions.py:            sage: L._element_constructor_([4,3,2,1])
src/sage/combinat/posets/linear_extensions.py:            sage: L._element_constructor_([4,3,2,1],check=False)
src/sage/geometry/triangulation/point_configuration.py:            sage: p._element_constructor_([ (0,1,2), (2,3,0) ])
src/sage/geometry/linear_expression.py:            sage: L._element_constructor([1, 2, 3], 4)
src/sage/geometry/linear_expression.py:            sage: L._element_constructor(vector(ZZ, [1, 2, 3]), 4)
src/sage/geometry/linear_expression.py:            sage: L._element_constructor(4)
src/sage/geometry/linear_expression.py:            sage: L._element_constructor(vector([4, 1, 2, 3]))
src/sage/geometry/linear_expression.py:            sage: L._element_constructor(m)
src/sage/geometry/polyhedron/parent.py:            sage: P._element_constructor_([[(0, 0, 0), (1, 0, 0), (0, 1, 0), (0,0,1)], [], []], None)
src/sage/geometry/hyperplane_arrangement/hyperplane.py:        sage: ambient._element_constructor_(x+y-1)   
src/sage/geometry/hyperplane_arrangement/arrangement.py:            sage: L._element_constructor_(x, y)
src/sage/geometry/hyperplane_arrangement/arrangement.py:            sage: L._element_constructor_([x, y])
src/sage/geometry/hyperplane_arrangement/arrangement.py:            sage: L._element_constructor_([0, 1, 0], [0, 0, 1])
src/sage/geometry/hyperplane_arrangement/arrangement.py:            sage: L._element_constructor_([[0, 1, 0], [0, 0, 1]])
src/sage/geometry/hyperplane_arrangement/arrangement.py:            sage: L._element_constructor(polytopes.hypercube(2))
src/sage/homology/chain_complex.py:            sage: D._element_constructor_(0)
src/sage/tensor/modules/free_module_homset.py:            sage: phi = H._element_constructor_([[2,-1,3], [1,0,-4]], bases=(e,f),
src/sage/tensor/modules/free_module_homset.py:            sage: phi = EM._element_constructor_([[1,2,3],[4,5,6],[7,8,9]],
src/sage/tensor/modules/free_module_homset.py:            sage: phi_a = EM._element_constructor_(a) ; phi_a
src/sage/tensor/modules/ext_pow_free_module.py:            sage: a = A._element_constructor_(0) ; a
src/sage/tensor/modules/ext_pow_free_module.py:            sage: a = A._element_constructor_([], name='a') ; a
src/sage/tensor/modules/ext_pow_free_module.py:            sage: a = A._element_constructor_(0) ; a
src/sage/tensor/modules/ext_pow_free_module.py:            sage: a = A._element_constructor_([2,0,-1], name='a') ; a
src/sage/tensor/modules/ext_pow_free_module.py:            sage: a = A._element_constructor_(0) ; a
src/sage/tensor/modules/ext_pow_free_module.py:            sage: a = A._element_constructor_([], name='a') ; a
src/sage/tensor/modules/finite_rank_free_module.py:            sage: v = M._element_constructor_(comp=[1,0,-2], basis=e, name='v') ; v
src/sage/tensor/modules/finite_rank_free_module.py:            sage: v = M._element_constructor_(0) ; v
src/sage/tensor/modules/finite_rank_free_module.py:            sage: v = M._element_constructor_() ; v
src/sage/tensor/modules/free_module_linear_group.py:            sage: a = GL._element_constructor_(comp=[[1,2],[1,3]], basis=e,
src/sage/tensor/modules/free_module_linear_group.py:            sage: GL._element_constructor_(1)
src/sage/tensor/modules/free_module_linear_group.py:            sage: GL._element_constructor_(1).matrix(e)
src/sage/tensor/modules/free_module_linear_group.py:            sage: a = GL._element_constructor_(phi) ; a
src/sage/tensor/modules/free_module_linear_group.py:            sage: a = GL._element_constructor_(t) ; a
src/sage/tensor/modules/tensor_free_module.py:            sage: T._element_constructor_(0) is T.zero()
src/sage/tensor/modules/tensor_free_module.py:            sage: t = T._element_constructor_(comp=[[2,0],[1/2,-3]], basis=e,
src/sage/categories/sets_cat.py:                sage: S._element_constructor_(17)
src/sage/categories/sets_cat.py:                sage: A._element_constructor
src/sage/categories/sets_cat.py:                sage: B._element_constructor
src/sage/categories/sets_cat.py:                sage: s = S._element_constructor_from_element_class(17); s
src/sage/categories/examples/semigroups.py:            sage: F._element_constructor_('a')
src/sage/categories/examples/semigroups.py:            sage: F._element_constructor_('bad')
src/sage/categories/examples/semigroups.py:            sage: F._element_constructor_('z')
src/sage/categories/examples/semigroups.py:            sage: bad = F._element_constructor_('bad'); bad
src/sage/categories/examples/semigroups.py:            sage: type(S._element_constructor_(17))
src/sage/categories/examples/semigroups.py:            sage: S._element_constructor_(17)
src/sage/categories/examples/semigroups.py:            sage: type(S._element_constructor_(17))
src/sage/categories/examples/sets_cat.py:            sage: P._element_constructor_(13) == 13
src/sage/categories/examples/sets_cat.py:            sage: P._element_constructor_(13).parent()
src/sage/categories/examples/sets_cat.py:            sage: P._element_constructor_(14)
src/sage/categories/examples/sets_cat.py:            sage: P._element_constructor_(14)
src/sage/categories/examples/sets_cat.py:            sage: P._element_constructor_(14)
src/sage/structure/parent.pyx:            sage: k = GF(5); k._element_constructor # indirect doctest
src/sage/structure/set_factories.py:            sage: pol.self_element_constructor_attributes(XYPair)
src/sage/structure/set_factories.py:            sage: pol.facade_element_constructor_attributes(XYPairs())
src/sage/manifolds/differentiable/real_line.py:            sage: I((2,))  # standard used of TopologicalManifoldSubset._element_constructor_
src/sage/algebras/steenrod/steenrod_algebra.py:            sage: A1._element_constructor_(Sq(2))
src/sage/algebras/steenrod/steenrod_algebra.py:            sage: A1._element_constructor_(Sq(4)) # Sq(4) not in A1
src/sage/algebras/free_algebra_quotient.py:            sage: H._element_constructor_(i) is i
src/sage/algebras/free_algebra_quotient.py:            sage: a = H._element_constructor_(1); a
src/sage/algebras/free_algebra_quotient.py:            sage: a = H._element_constructor_([1,2,3,4]); a

Of course, the documentation should explain how to implement _element_constructor_. However, the documentation must not suggest that the method (with or without trailing underscore) should ever be called directly.

jdemeyer commented 7 years ago

Branch: u/jdemeyer/rename_parentelementconstructor____parentconstruct_element

koffie commented 7 years ago

New commits:

e9ce136_element_constructor was an attribute of Subword before it was replaced with _build
93e25fbRename Parent._element_constructor -> Parent.__construct_element
koffie commented 7 years ago

Commit: 93e25fb

simon-king-jena commented 7 years ago

Changed branch from u/jdemeyer/rename_parentelementconstructor____parentconstruct_element to u/SimonKing/rename_parentelementconstructor____parentconstruct_element

simon-king-jena commented 7 years ago
comment:22

Briefly looking at your commit: I find it a good idea that you are using a cdef __construct_element instead of a cdef public _element_constructor, because in that way it is certain that no Python class will illegally use the internal element constructor. I also see that you make pickling backward compatible.

My commit fixes one part of the documentation (in sage.schemes).


New commits:

c95b724Remove ._element_constructor_ from sage.schemes documentation
simon-king-jena commented 7 years ago

Changed commit from 93e25fb to c95b724

simon-king-jena commented 7 years ago

Changed author from Jeroen Demeyer to Jeroen Demeyer, Simon King

jdemeyer commented 7 years ago

Changed dependencies from #23882 to #23882, #23884

simon-king-jena commented 7 years ago
comment:25

So far, the git commits come from Maarten and me, so, I guess the Authors field should be changed accordingly.

On a different ticket, Jeroen said that he likes small tickets. I guess that means I should open a new ticket for cleanup of the documentation.

I suggest, in order to not change things in git that have already been done, to let commit:c95b724 (Remove ._elementconstructor from sage.schemes documentation) stay here and open a new ticket for other documentation issues.

First of all, the failures resulting from Maarten's commit should be addressed.

simon-king-jena commented 7 years ago
comment:26

Replying to @simon-king-jena:

First of all, the failures resulting from Maarten's commit should be addressed.

Nope. Some failures come from padics. So, first #23884 needs to be fixed. That's why I don't like creating many small tickets.

simon-king-jena commented 7 years ago
comment:27

Replying to @simon-king-jena:

Replying to @simon-king-jena:

First of all, the failures resulting from Maarten's commit should be addressed.

Nope. Some failures come from padics. So, first #23884 needs to be fixed. That's why I don't like creating many small tickets.

Anyway, if we are lucky, merging Jeroen's branch from #23884 will fix the issue. I am trying that now.

mantepse commented 7 years ago
comment:28

For the doc of binary trees, it is #23885

mantepse commented 7 years ago
comment:29

For the remainder of combinat, it is #23889. However, I'm not so sure about this anymore: _element_constructor_ is a private method, so it won't show up in the documentation anyway? So, isn't it correct to doctest it directly?

jdemeyer commented 7 years ago
comment:30

Replying to @simon-king-jena:

Some failures come from padics. So, first #23884 needs to be fixed. That's why I don't like creating many small tickets.

I don't really get this. The padic failures need to be fixed anyway, either here or on #23884. Why don't you like to do it on a different ticket?

jdemeyer commented 7 years ago
comment:31

Replying to @simon-king-jena:

That's why I don't like creating many small tickets.

Interestingly, you are the first person I am hearing this from.

Advantages of small tickets:

  1. Small tickets are easier to review because the reviewer only needs to look at a small set of changes.

  2. Different tickets can have different sets of authors/reviewers, each with their own interests/specialities.

  3. Small tickets are less likely to get stuck because of one isolated difficulty. For example: a ticket like "stop using _element_constructor" cannot succeed as long as there is one isolated place where _element_constructor is used non-trivially. A ticket like "stop using _element_constructor in sub-package foo" does not have this problem.

Usually, when working on a big ticket like #23880 (big in the sense that it has a lot of connections to other parts of Sage, it is very non-local), I start coding and then split off separate tickets as I go along.

mantepse commented 7 years ago
comment:32

I'd be genuinly interested: shouldn't doctests of a private method like _element_constructor_ better doctest this method directly? Or should I ask this on sage-devel?

jdemeyer commented 7 years ago
comment:33

Replying to @mantepse:

I'd be genuinly interested: shouldn't doctests of a private method like _element_constructor_ better doctest this method directly? Or should I ask this on sage-devel?

In the end, both should be tested: direct and indirect tests. In most cases, the indirect usage is already tested (many doctests indirectly use _element_constructor_). So, yes, I think that _element_constructor_ should be tested directly.

simon-king-jena commented 7 years ago
comment:34

Replying to @jdemeyer:

Replying to @mantepse:

I'd be genuinly interested: shouldn't doctests of a private method like _element_constructor_ better doctest this method directly? Or should I ask this on sage-devel?

In the end, both should be tested: direct and indirect tests. In most cases, the indirect usage is already tested (many doctests indirectly use _element_constructor_). So, yes, I think that _element_constructor_ should be tested directly.

Seriously??? When I was younger, I occasionally did direct tests (say, for __iter__) and was told to not do that.

I also think it has been discussed on sage-devel a long time ago and the consensus was that the documentation (in particular the "EXAMPLES:" section) has to show how stuff is actually being used. It may be possible to be a bit more explicit in the "TESTS:" section, but still I'd recommend against it.

It is clear from the tutorials that _repr_, _add_, _mul_ and _element_constructor_ (all single underscore!) in Sage implement string representation of parents/elements, addition resp. multiplication of elements, and the call method of parents. Thus, all these methods should be tested indirectly:

sage: P    # indirect doctest
String representation
sage: x+y  # indirect doctest
result of addition
sage: P(x) # indirect doctest
result of conversion
simon-king-jena commented 7 years ago
comment:35

Replying to @simon-king-jena:

I also think it has been discussed on sage-devel a long time ago and the consensus was that the documentation (in particular the "EXAMPLES:" section) has to show how stuff is actually being used. It may be possible to be a bit more explicit in the "TESTS:" section, but still I'd recommend against it.

I tried to find the discussion, but didn't succeed. So, if you prefer, do ask on Sage-devel.

jdemeyer commented 7 years ago
comment:36

Replying to @simon-king-jena:

Seriously??? When I was younger, I occasionally did direct tests (say, for __iter__) and was told to not do that.

I think Python special methods like __iter__ and private methods like _element_constructor_ are a different category. This is especially so for a cdef class, where __iter__ is not a method at all! So whatever you think about __iter__ may not apply to _element_constructor_.

I also think it has been discussed on sage-devel a long time ago and the consensus was that the documentation (in particular the "EXAMPLES:" section) has to show how stuff is actually being used.

Well, _element_constructor_ is not actually used by the end user. So again, this is not a relevant point.

In the end I would say that it doesn't really matter. Maybe add both kinds of tests to be safe?

mantepse commented 7 years ago
comment:37

In the end I would say that it doesn't really matter. Maybe add both kinds of tests to be safe?

Well, currently we are removing these tests. So if we decide here that it's not bad practice to test _element_constructor_ directly, several of the tickets can be closed immediately.

Personally, I think that indirect doctests do not add much value and are hard to write. So, I think we could close all these tickets again - I'd move out the extra stuff from #23885. But I don't care much, comment:34 is convincing, too. Actually, I didn't know that _element_constructor_ always invokes the call method of the parent.

Anyway, let's not waste time on this.

jdemeyer commented 7 years ago

Changed dependencies from #23882, #23884 to #23882, #23884, #23894

tscrim commented 7 years ago
comment:39

Replying to @mantepse:

In the end I would say that it doesn't really matter. Maybe add both kinds of tests to be safe?

Well, currently we are removing these tests. So if we decide here that it's not bad practice to test _element_constructor_ directly, several of the tickets can be closed immediately.

I am a strong -1 on removing these tests. There can be good reasons for calling it directly, such as having non-trivial parsing of inputs before sending it to the element class but wanting to avoid the coercion framework. I also cannot see any problems with a direct call. Since this is being tested directly in the corresponding method, it is not public, and so it won't cause any problems for users.

Personally, I think that indirect doctests do not add much value and are hard to write. So, I think we could close all these tickets again - I'd move out the extra stuff from #23885. But I don't care much, comment:34 is convincing, too.

Do you mean comment:33 or comment:36?

I usually am just lazy and do indirect for _element_constructor_ tests. However, I probably should do direct calls in tests... However, for some of the methods, it is just easier (and can be more readable, such as for algebraic operations) to do the indirect test.

Actually, I didn't know that _element_constructor_ always invokes the call method of the parent.

It is the other way around; the _element_constructor_ is called from Parent.__call__. It usually is the last thing called from the parent and actually constructs the element when doing Foo(bar).

mantepse commented 7 years ago
comment:40

I am a strong -1 on removing these tests.

OK - so please let's ignore these tickets for the moment and concentrate on this one and #23880 instead...

Personally, I think that indirect doctests do not add much value and are hard to write. So, I think we could close all these tickets again - I'd move out the extra stuff from #23885. But I don't care much, comment:34 is convincing, too.

Do you mean comment:33 or comment:36?

No, I meant comment:34. Specifically:

It is clear from the tutorials that _repr_, _add_, _mul_ and _element_constructor_ (all single underscore!) in Sage implement string representation of parents/elements, addition resp. multiplication of elements, and the call method of parents. Thus, all these methods should be tested indirectly:

sage: P    # indirect doctest
String representation
sage: x+y  # indirect doctest
result of addition
sage: P(x) # indirect doctest
result of conversion

Actually, I didn't know that _element_constructor_ always invokes the call method of the parent.

It is the other way around; the _element_constructor_ is called from Parent.__call__. It usually is the last thing called from the parent and actually constructs the element when doing Foo(bar).

Thanks for the clarification!

jdemeyer commented 7 years ago

Changed dependencies from #23882, #23884, #23894 to #23882, #23884, #23894, #23899

jdemeyer commented 7 years ago

Changed dependencies from #23882, #23884, #23894, #23899 to #23882, #23884, #23894, #23899, #23905

jdemeyer commented 7 years ago

Changed dependencies from #23882, #23884, #23894, #23899, #23905 to #23882, #23884, #23894, #23899, #23905, #23907

jdemeyer commented 7 years ago

Changed dependencies from #23882, #23884, #23894, #23899, #23905, #23907 to #23882, #23884, #23894, #23899, #23905, #23907, #23914

jdemeyer commented 7 years ago
comment:45

With all the dependencies on this ticket and #23880, I am no longer convinced that we need this ticket. We could also simply do the change cdef public _element_constructor -> cdef _element_constructor here and nothing else.

tscrim commented 7 years ago
comment:46

I think removing _element_constructor from the public Python API is a good thing to do to prevent anyone else from adding something (although I would say that is highly unlikely).

jdemeyer commented 7 years ago

Description changed:

--- 
+++ 
@@ -1 +1 @@
-In order to avoid confusion between `_element_constructor_` (a public interface) and `_element_constructor` (a private attribute), we rename the latter.
+In order to avoid confusion between `_element_constructor_` (a public interface) and `_element_constructor` (a private attribute), we change the latter from `cdef public` to `cdef`. This way, it can no longer be accessed from Python.
jdemeyer commented 7 years ago

Description changed:

--- 
+++ 
@@ -1 +1 @@
-In order to avoid confusion between `_element_constructor_` (a public interface) and `_element_constructor` (a private attribute), we change the latter from `cdef public` to `cdef`. This way, it can no longer be accessed from Python.
+In order to avoid confusion between `_element_constructor_` (a public interface) and `_element_constructor` (a private attribute), we change the latter from `cdef public` to `cdef`. The same for `_element_init_pass_parent`. This way, those attributes can no longer be accessed from Python.