1 year ago

#43552

test-img

Codejoy

python2 to python3 conversion cannot find imports now. Namespacing issue?

While I am able to fix this and it is not a huge issue. I am curious if I am doing something wrong or went about something wrong. I inherited a codebase that is python2 to upgrade it to python3. So I just bit the bullet and dug in, started with a 2to3 conversion. Now bit by bit I am running and seeing where it croaks, fixing it and rinse repeat. I noticed most the issues are in the module imports from local modules created.

An example:

a file in a folder called Label.py has the line:

__all__ = ['Label', 'BoolLabel', 'StrLabel', 'IntLabel', 'FloatLabel']

class Label(tkinter.Label, CtxMenuMixin, IsCurrentMixin, SeverityMixin):
    ...code...
    ...code...

class FloatLabel(Label):
    ...code...
    ...code...

This file is used by another one called GrayImageDispWdg.py and in that it is trying to do a call as such:

self.currXPosWdg = Label.FloatLabel()

This USED to work in python2.7 now though after the conversion I get a:

type object 'Label' has no attribute 'FloatLabel'

Now I can fix this at the top of say GrayImageDispWdg.py file by instead of saying just this:

from . import Label

if I also say:

from . import FloatLabel

and then change the line:

self.currXPosWdg = Label.FloatLabel()

to

self.currXPosWdg = FloatLabel()

Then it seems to work, but I am doing this a ton on all files since the conversion. Maybe I had something wrong setup? Maybe was some sort of path issue I missed? (I am not a Python guru by any means whilst editing / converting this stuff either)

__all__ = ['Label', 'BoolLabel', 'StrLabel', 'IntLabel', 'FloatLabel', 'DMSLabel']

import sys
import tkinter
import RO.Constants
import RO.MathUtil
import RO.StringUtil
from . import CtxMenu
from . import CtxMenuMixin
from .SeverityMixin import SeverityMixin
from .IsCurrentMixin import IsCurrentMixin

class Label(tkinter.Label, CtxMenuMixin, IsCurrentMixin, SeverityMixin):
    """Base class for labels (display ROWdgs); do not use directly.
    
    Inputs:
    - formatStr: formatting string; if omitted, formatFunc is used.
        Displayed value is formatStr % value.
    - formatFunc: formatting function; ignored if formatStr specified.
        Displayed value is formatFunc(value).
    - helpText  short text for hot help
    - helpURL   URL for on-line help
    - isCurrent is value current?
    - severity  one of RO.Constants.sevNormal, sevWarning or sevError
    - **kargs: all other keyword arguments go to Tkinter.Label;
        the defaults are anchor="e", justify="right"
        
    Inherited methods include:
    getIsCurrent, setIsCurrent
    getSeverity, setSeverity
        
    Note: if display formatting fails (raises an exception)
    then "?%r?" % value is displayed.
    """

    def __init__ (self,
        master,
        formatStr = None,
        formatFunc = str,       
        helpText = None,
        helpURL = None,
        isCurrent = True,
        severity = RO.Constants.sevNormal,
    **kargs):
        kargs.setdefault("anchor", "e")
        kargs.setdefault("justify", "right")
        
        tkinter.Label.__init__(self, master, **kargs)
        
        CtxMenuMixin.__init__(self, helpURL=helpURL)
        
        IsCurrentMixin.__init__(self, isCurrent)
        
        SeverityMixin.__init__(self, severity)

        self._formatStr = formatStr
        if formatStr != None:
            formatFunc = self._formatFromStr
        self._formatFunc = formatFunc
        self.helpText = helpText

        self._value = None

    def get(self):
        """Return a tuple consisting of (set value, isCurrent).
        
        If the value is None then it is invalid or unknown.
        If isCurrent is false then the value is suspect
        Otherwise the value is valid and current.
        """
        return (self._value, self._isCurrent)
    
    def getFormatted(self):
        """Return a tuple consisting of the (displayed value, isCurrent).
        
        If the value is None then it is invalid.
        If isCurrent is false then the value is suspect
        Otherwise the value is valid and current.
        """
        if self._value == None:
            return (None, self._isCurrent)
        else:
            return (self["text"], self._isCurrent)
    
    def clear(self, isCurrent=1):
        """Clear the display; leave severity unchanged.
        """
        self.set(value="", isCurrent=isCurrent)
    
    def set(self,
        value,
        isCurrent = True,
        severity = None,
    **kargs):
        """Set the value

        Inputs:
        - value: the new value
        - isCurrent: is value current (if not, display with bad background color)
        - severity: the new severity, one of: RO.Constants.sevNormal, sevWarning or sevError;
          if omitted, the severity is left unchanged          
        kargs is ignored; it is only present for compatibility with KeyVariable callbacks.
        
        Raises an exception if the value cannot be coerced.
        """
        # print "RO.Wdg.Label.set called: value=%r, isCurrent=%r, **kargs=%r" % (value, isCurrent, kargs)
        self._value = value
        self.setIsCurrent(isCurrent)
        if severity != None:
            self.setSeverity(severity)
        self._updateText()
    
    def setNotCurrent(self):
        """Mark the data as not current.
        
        To mark the value as current again, set a new value.
        """
        self.setIsCurrent(False)
    
    def _formatFromStr(self, value):
        """Format function based on formatStr.
        """
        return self._formatStr % value

    def _updateText(self):
        """Updates the displayed value. Ignores isCurrent and severity.
        """
        if self._value == None:
            self["text"] = ""
        else:
            try:
                self["text"] = self._formatFunc(self._value)
            except Exception as e:
                sys.stderr.write("format of value %r failed with error: %s\n" % (self._value, e))
                self["text"] = "?%r?" % (self._value,)


class BoolLabel(Label):
    """Label to display string data.
    Inputs are those for Label, but formatStr and formatFunc are forbidden.
    """
    def __init__ (self,
        master,
        helpText = None,
        helpURL = None,
        trueValue = "True",
        falseValue = "False",
        isCurrent = True,
        **kargs
    ):
        assert "formatStr" not in kargs, "formatStr not allowed for %s" % self.__class__.__name__
        assert "formatFunc" not in kargs, "formatFunc not allowed for %s" % self.__class__.__name__

        def formatFnct(val):
            if val:
                return trueValue
            else:
                return falseValue

        Label.__init__(self,
            master,
            formatFunc = formatFnct,
            helpText = helpText,
            helpURL = helpURL,
            isCurrent = isCurrent,
        **kargs)


class StrLabel(Label):
    """Label to display string data.
    Inputs are those for Label but the default formatFunc is str.
    """
    def __init__ (self,
        master,
        helpText = None,
        helpURL = None,
        isCurrent = True,
        **kargs
    ):
        kargs.setdefault("formatFunc", str)
        
        Label.__init__(self,
            master,
            helpText = helpText,
            helpURL = helpURL,
            isCurrent = isCurrent,
        **kargs)


class IntLabel(Label):
    """Label to display integer data; truncates floating point data
    Inputs are those for Label, but the default formatStr is "%s" and formatFunc is forbidden.
    """
    def __init__ (self,
        master,
        helpText = None,
        helpURL = None,
        isCurrent = True,
        **kargs
    ):
        kargs.setdefault("formatStr", "%d")
        assert "formatFunc" not in kargs, "formatFunc not allowed for %s" % self.__class__.__name__
        
        Label.__init__(self,
            master,
            helpText = helpText,
            helpURL = helpURL,
            isCurrent = isCurrent,
        **kargs)


class FloatLabel(Label):
    """Label to display floating point data.
    
    If you specify a format string, that is used and the specified is ignored
    else you must specify a precision, in which case the data is displayed
    as without an exponent and with "precision" digits past the decimal.
    The default precision is 2 digits.
    
    Inputs:
    - precision: number of digits past the decimal point; ignored if formatStr specified
    The other inputs are those for Label but formatFunc is forbidden.
    """
    def __init__ (self,
        master,
        formatStr=None,
        precision=2,
        helpText = None,
        helpURL = None,
        isCurrent = True,
    **kargs):
        assert "formatFunc" not in kargs, "formatFunc not allowed for %s" % self.__class__.__name__

        # handle default format string
        if formatStr == None:
            formatStr = "%." + str(precision) + "f"
            
        # test and set format string
        try:
            formatStr % (1.1,)
        except:
            raise ValueError("Invalid floating point format string %s" % (formatStr,))

        Label.__init__(self,
            master,
            formatStr = formatStr,
            helpText = helpText,
            helpURL = helpURL,
            isCurrent = isCurrent,
        **kargs)


class DMSLabel(Label):
    """Label to display floating point data as dd:mm:ss.ss.
    Has the option to store data in degrees but display in hh:mm:ss.ss;
    this option can be changed at any time and the display updates correctly.
    
    Inputs:
    - precision: number of digits past the decimal point
    - nFields: number of sexagesimal fields to display
    - cnvDegToHrs: if True, data is in degrees but display is in hours
    The other inputs are those for Label, but formatStr and formatFunc are forbidden.
    """
    def __init__ (self,
        master,
        precision,
        nFields = 3,
        cvtDegToHrs = False,
        helpText = None,
        helpURL = None,
        isCurrent = True,
    **kargs):
        assert "formatStr" not in kargs, "formatStr not allowed for %s" % self.__class__.__name__
        assert "formatFunc" not in kargs, "formatFunc not allowed for %s" % self.__class__.__name__
        
        self.precision = precision
        self.nFields = nFields
        self.cvtDegToHrs = cvtDegToHrs

        Label.__init__(self,
            master,
            formatFunc = self.formatFunc,
            helpText = helpText,
            helpURL = helpURL,
            isCurrent = isCurrent,
        **kargs)
    
    def formatFunc(self, value):
        if self.cvtDegToHrs and value != None:
            value = value / 15.0
        return RO.StringUtil.dmsStrFromDeg (
            value,
            precision = self.precision,
            nFields = self.nFields,
        )
    
    def setCvtDegToHrs(self, cvtDegToHrs):
        if RO.MathUtil.logNE(self.cvtDegToHrs, cvtDegToHrs):
            self.cvtDegToHrs = cvtDegToHrs
            self._updateText()


if __name__ == "__main__":
    from . import PythonTk
    from RO.TkUtil import Timer
    root = PythonTk.PythonTk()

    wdgSet = (
        BoolLabel(root,
            helpText = "Bool label",
        ),
        StrLabel(root,
            helpText = "String label",
        ),
        IntLabel(root,
            width=5,
            helpText = "Int label; width=5",
        ),
        FloatLabel(root,
            precision=2,
            width=5,
            helpText = "Float label; precision = 2, width=5",
        ),
        FloatLabel(root,
            formatStr="%.5g",
            width=8,
            helpText = "Float label; format = '\%.5g', width = 8",
        ),
        DMSLabel(root,
            precision=2,
            width=10,
            helpText = "DMS label; precision = 2, width = 10",
        ),
        DMSLabel(root,
            precision=2,
            cvtDegToHrs=1,
            width=10,
            helpText = "DMS label; precision = 2, width = 10, convert degrees to hours",
        ),
    )
    for wdg in wdgSet:
        wdg.pack(fill=tkinter.X)
    
    # a list of (value, isCurrent) pairs
    testData = [
        ("some text", True),
        ("invalid text", False),
        (0, True),
        ("", True),
        (False, True),
        (1, True),
        (1234567890, True),
        (1234567890, False),
        (1.1, True),
        (1.9, True),
        (-1.1, True),
        (-1.9, True),
        (-0.001, True),
        (-1.9, False),
    ]
    
    ind = 0
    def displayNext():
        global ind, testData
        val = testData[ind]
        print("\nvalue = %r, isCurrent = %s" % tuple(val))
        for wdg in wdgSet:
            wdg.set(*val)
        ind += 1
        if ind < len(testData):
            Timer(1.2, displayNext)
    Timer(1.2, displayNext)
            
    root.mainloop()

python

python-3.x

import

python-2to3

0 Answers

Your Answer

Accepted video resources