Skip to content

Commit

Permalink
Monkey patch framework
Browse files Browse the repository at this point in the history
  • Loading branch information
saraedum committed May 31, 2018
1 parent 83ca9a5 commit ab1bb06
Show file tree
Hide file tree
Showing 7 changed files with 377 additions and 16 deletions.
2 changes: 2 additions & 0 deletions henselization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
from . import sage as monkey_sage
import sage.all

from .monkey import *

from recursive_monkey_patch import monkey_patch
monkey_patch(monkey_sage, sage)

Expand Down
3 changes: 3 additions & 0 deletions henselization/monkey/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .trac_25226 import *
from .trac_22983 import *
from .trac_24934 import *
79 changes: 79 additions & 0 deletions henselization/monkey/trac_22983.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-

#*****************************************************************************
# Copyright (C) 2018 Julian Rüth <julian.rueth@fsfe.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# http://www.gnu.org/licenses/
#*****************************************************************************

from .util import AbstractMonkey

class Monkey(AbstractMonkey):
_trac = "https://trac.sagemath.org/ticket/22983"

def _test(self):
from sage.all import QQ, PolynomialRing
R = PolynomialRing(QQ, 'x')
x = R.gen()
if R.quo(x) is not R.quo(x):
raise Exception("#22983 has not been fixed")

def _patch(self):
import sage.rings.polynomial.polynomial_quotient_ring
sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRingFactory = PolynomialQuotientRingFactory
sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing = PolynomialQuotientRingFactory("sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing")
del sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing_generic.__reduce__
del sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing_domain.__reduce__
del sage.rings.polynomial.polynomial_quotient_ring.PolynomialQuotientRing_field.__reduce__

from sage.structure.factory import UniqueFactory
class PolynomialQuotientRingFactory(UniqueFactory):
def create_key(self, ring, polynomial, names=None):
from sage.rings.polynomial.polynomial_ring import PolynomialRing_commutative
if not isinstance(ring, PolynomialRing_commutative):
raise TypeError("ring must be a polynomial ring")
from sage.rings.polynomial.polynomial_element import Polynomial
if not isinstance(polynomial, Polynomial):
raise TypeError("must be a polynomial")
if not polynomial.parent() is ring:
raise TypeError("polynomial must be in ring")

c = polynomial.leading_coefficient()
if not c.is_unit():
raise TypeError("polynomial must have unit leading coefficient")

if names is None:
names = tuple([x + 'bar' for x in ring.variable_names()])
else:
from sage.rings.polynomial.polynomial_quotient_ring import normalize_names
names = normalize_names(ring.ngens(), names)

return ring, polynomial, names

def create_object(self, version, key):
ring, polynomial, names = key

R = ring.base_ring()
from sage.categories.all import IntegralDomains
if R in IntegralDomains():
try:
is_irreducible = polynomial.is_irreducible()
except NotImplementedError: # is_irreducible sometimes not implemented
pass
else:
if is_irreducible:
from sage.categories.all import Fields
if R in Fields():
from sage.rings.polynomial.polynomial_quotient_ring import PolynomialQuotientRing_field
return PolynomialQuotientRing_field(ring, polynomial, names)
else:
from sage.rings.polynomial.polynomial_quotient_ring import PolynomialQuotientRing_domain
return PolynomialQuotientRing_domain(ring, polynomial, names)
from sage.rings.polynomial.polynomial_quotient_ring import PolynomialQuotientRing_generic
return PolynomialQuotientRing_generic(ring, polynomial, names)

Monkey().patch()
129 changes: 129 additions & 0 deletions henselization/monkey/trac_24934.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-

#*****************************************************************************
# Copyright (C) 2018 Julian Rüth <julian.rueth@fsfe.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# http://www.gnu.org/licenses/
#*****************************************************************************

from .util import AbstractMonkey

class Monkey(AbstractMonkey):
_trac = "https://trac.sagemath.org/ticket/24934"

def _test(self):
from sage.all import QuadraticField, loads, dumps
K = QuadraticField(-1, 'i')
if loads(dumps(K.maximal_order())) is not K.maximal_order():
raise Exception("#24934 has not been fixed")

def _patch(self):
import sage.rings.number_field.order
sage.rings.number_field.order.AbsoluteOrderFactory = AbsoluteOrderFactory
sage.rings.number_field.order.absolute_order_from_module_generators = AbsoluteOrderFactory("sage.rings.number_field.order.absolute_order_from_module_generators")
sage.rings.number_field.order.RelativeOrderFactory = RelativeOrderFactory
sage.rings.number_field.order.relative_order_from_ring_generators = RelativeOrderFactory("sage.rings.number_field.order.relative_order_from_ring_generators")
del sage.rings.number_field.order.AbsoluteOrder.__reduce__
del sage.rings.number_field.order.RelativeOrder.__reduce__


from sage.structure.factory import UniqueFactory
class AbsoluteOrderFactory(UniqueFactory):
def create_key_and_extra_args(self, gens, check_integral=True, check_rank=True, check_is_ring=True, is_maximal=None, allow_subfield=False):
if allow_subfield:
raise NotImplementedError("the allow_subfield parameter is not supported yet")
if len(gens) == 0:
raise ValueError("gens must span an order over ZZ")

from sage.all import Sequence
gens = Sequence(gens)

K = gens.universe()
from sage.rings.number_field.order import is_NumberFieldOrder
if is_NumberFieldOrder(K):
K = K.number_field()
gens = frozenset([K(g) for g in gens])
return (K, gens), {"check_integral": check_integral,
"check_rank": check_rank,
"check_is_ring": check_is_ring,
"is_maximal": is_maximal}

def create_object(self, version, key, check_integral, check_rank, check_is_ring, is_maximal):
K, gens = key

from sage.rings.number_field.order import each_is_integral
if check_integral and not each_is_integral(gens):
raise ValueError("each generator must be integral")

K = iter(gens).next().parent()
V, from_V, to_V = K.vector_space()
mod_gens = [to_V(x) for x in gens]

from sage.all import ZZ
ambient = ZZ**V.dimension()
W = ambient.span(mod_gens)

if check_rank:
if W.rank() != K.degree():
raise ValueError("the rank of the span of gens is wrong")

if check_is_ring:
# Is there a faster way?
alg = [to_V(x) for x in monomials(gens, [f.absolute_minpoly().degree() for f in gens])]
if ambient.span(alg) != W:
raise ValueError("the module span of the gens is not closed under multiplication.")

from sage.rings.number_field.order import AbsoluteOrder
return AbsoluteOrder(K, W, check=False, is_maximal=is_maximal) # we have already checked everything

class RelativeOrderFactory(UniqueFactory):
def create_key_and_extra_args(self, gens, check_is_integral=True, check_rank=True, is_maximal = None, allow_subfield=False):
if allow_subfield:
raise NotImplementedError("the allow_subfield parameter is not supported yet")

from sage.all import Sequence
gens = Sequence(gens)

K = gens.universe()
from sage.rings.number_field.order import is_NumberFieldOrder
if is_NumberFieldOrder(K):
K = K.number_field()
gens = frozenset([K(g) for g in gens])

return (K, gens), {"check_is_integral": check_is_integral,
"check_rank": check_rank,
"is_maximal": is_maximal}

def create_object(self, version, key, check_is_integral, check_rank, is_maximal):
K, gens = key

from sage.rings.number_field.order import each_is_integral
if check_is_integral and not each_is_integral(gens):
raise ValueError("each generator must be integral")

# The top number field that contains the order.
K = iter(gens).next().parent()

# The absolute version of that field.
Kabs = K.absolute_field('z')
from_Kabs, to_Kabs = Kabs.structure()

module_gens = [to_Kabs(a) for a in gens]
n = [a.absolute_minpoly().degree() for a in gens]
from sage.rings.monomials import monomials
absolute_order_module_gens = monomials(module_gens, n)

from sage.rings.number_field.order import absolute_order_from_module_generators
abs_order = absolute_order_from_module_generators(absolute_order_module_gens,
check_integral=False, check_is_ring=False,
check_rank=check_rank)

from sage.rings.number_field.order import RelativeOrder
return RelativeOrder(K, abs_order, check=False, is_maximal=is_maximal)


Monkey().patch()
36 changes: 36 additions & 0 deletions henselization/monkey/trac_25226.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-

#*****************************************************************************
# Copyright (C) 2018 Julian Rüth <julian.rueth@fsfe.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# http://www.gnu.org/licenses/
#*****************************************************************************

from .util import AbstractMonkey

class Monkey(AbstractMonkey):
_trac = "https://trac.sagemath.org/ticket/25226"

def _test(self):
# not testable, see #25226
pass

def _patch(self):
import patchy
import sage.rings.valuation.inductive_valuation
patchy.patch(sage.rings.valuation.inductive_valuation.NonFinalInductiveValuation.equivalence_decomposition, r"""
@@ -1209,7 +1209,7 @@ class NonFinalInductiveValuation(FiniteInductiveValuation, DiscreteValuation):
v = self.extension(domain)
ret = v.equivalence_decomposition(v.domain()(f))
return Factorization([(self._eliminate_denominators(g), e)
- for (g,e) in ret], unit=self._eliminate_denominators(ret.unit()))
+ for (g,e) in ret], unit=self._eliminate_denominators(ret.unit()), sort=False)
""")


Monkey().patch()
113 changes: 113 additions & 0 deletions henselization/monkey/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
r"""
Shared monkey patching utilities
"""
#*****************************************************************************
# Copyright (C) 2018 Julian Rüth <julian.rueth@fsfe.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# http://www.gnu.org/licenses/
#*****************************************************************************

class AbstractMonkey:
r"""
Derived monkeys must implement ``_test()`` and ``_patch()`` to test whether
the feature to be patched has been fixed already and to perform the actual
patching.
EXAMPLES::
sage: from henselization.monkey.util import AbstractMonkey
sage: class Monkey(AbstractMonkey):
....: _trac = "https://trac.sagemath.org/..."
....: def _test(self):
....: pass # run tests here
....: def _patch(self):
....: pass # call patchy.patch() here
sage: Monkey().patch()
When patching fails, we run ``_test()`` to check whether the patch is
necessary at all::
sage: class Monkey(AbstractMonkey):
....: _trac = "https://trac.sagemath.org/..."
....: def _test(self):
....: pass # run tests here
....: def _patch(self):
....: raise NotImplementedError("call patchy.patch() here")
sage: Monkey().patch()
sage: class Monkey(AbstractMonkey):
....: _trac = "https://trac.sagemath.org/..."
....: def _test(self):
....: raise Exception("test failed")
....: def _patch(self):
....: raise NotImplementedError("call patchy.patch() here")
sage: Monkey().patch()
Your installation of Sage has a known issue: https://trac.sagemath.org/.... We tried to install a workaround for this problem but failed to do so. Please make sure that you are using the latest stable version of Sage. If you are using the latest stable version of Sage already, please report this issue at https://github.com/MCLF/henselization/issues including the traceback below.
Traceback (most recent call last):
...
NotImplementedError: call patchy.patch() here
We do not trust our patches. Tests are always run to check that
``_patch()`` did the right thing::
sage: from henselization.monkey.util import AbstractMonkey
sage: class Monkey(AbstractMonkey):
....: _trac = "https://trac.sagemath.org/..."
....: def _test(self):
....: raise Exception("test failed")
....: def _patch(self):
....: pass # call patchy.patch() here
sage: Monkey().patch()
Your installation of Sage has a known issue: https://trac.sagemath.org/.... We thought that we had installed a workaround but apparently failed to do so. Please report this issue at https://github.com/MCLF/henselization/issues including the traceback below.
Traceback (most recent call last):
...
Exception: test failed
"""
def patch(self):
r"""
Run ``_patch()`` with a more pretty error handling.
"""
try:
import patchy
patchy; # silence pyflakes "imported but not used"
except Exception:
if not self.is_fixed():
import warnings
warnings.warn("Your installation of Sage has a known issue: %s. Please install patchy with `sage -pip install patchy` to fix this problem."%(self._trac,))
return

try:
self._patch()
except Exception:
if not self.is_fixed():
print("Your installation of Sage has a known issue: %s. We tried to install a workaround for this problem but failed to do so. Please make sure that you are using the latest stable version of Sage. If you are using the latest stable version of Sage already, please report this issue at https://github.com/MCLF/henselization/issues including the traceback below."%(self._trac,))
import traceback
traceback.print_exc()
else:
try:
self._test()
except:
print("Your installation of Sage has a known issue: %s. We thought that we had installed a workaround but apparently failed to do so. Please report this issue at https://github.com/MCLF/henselization/issues including the traceback below."%(self._trac,))
import traceback
traceback.print_exc()

def is_fixed(self):
r"""
Return whether ``_test()`` did not throw an exception.
"""
try:
self._test()
except Exception:
return False
else:
return True

0 comments on commit ab1bb06

Please sign in to comment.