By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
440,304 Members | 3,172 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 440,304 IT Pros & Developers. It's quick & easy.

Making class attributes non-case-sensitive?

P: n/a
Hi,

I'm working within an application (making a lot of wrappers), but the
application is not case sensitive. For example, Typing obj.name,
obj.Name, or even object.naMe is all fine (as far as the app is
concerned). The problem is, If someone makes a typo, they may get an
unexpected error due accidentally calling the original attribute
instead of the wrapped version. Does anyone have a simple solution for
this?

I can protect against some cases just by making an 'alias':
class AClass(object):
def name(self):
print "hello"

Name = name

....but this doesn't protect against typos, it gets more complicated
with multi-word attribute names, and it makes my epydocs confusing to
read since all spelling versions are shown (I AM concerned about my
docs being clear, but not as much as stopping typo related errors).

I thought about using my wrapper's __getattr__ and __setattr__, but I
I am concerned about the overhead of every delegated attribute call
running a search and compare (<paramName>.lower() based compare?).

Any ideas or precedence?
Cheers,

- Rafe
Oct 13 '08 #1
Share this Question
Share on Google+
11 Replies


P: n/a
Rafe wrote:
Hi,

I'm working within an application (making a lot of wrappers), but the
application is not case sensitive. For example, Typing obj.name,
obj.Name, or even object.naMe is all fine (as far as the app is
concerned). The problem is, If someone makes a typo, they may get an
unexpected error due accidentally calling the original attribute
instead of the wrapped version. Does anyone have a simple solution for
this?

I can protect against some cases just by making an 'alias':
class AClass(object):
def name(self):
print "hello"

Name = name

...but this doesn't protect against typos, it gets more complicated
with multi-word attribute names, and it makes my epydocs confusing to
read since all spelling versions are shown (I AM concerned about my
docs being clear, but not as much as stopping typo related errors).

I thought about using my wrapper's __getattr__ and __setattr__, but I
I am concerned about the overhead of every delegated attribute call
running a search and compare (<paramName>.lower() based compare?).

Any ideas or precedence?
Ideas? Don't do that...

Seriously: where does that code come from, who's typing it? If it is python,
then make people follow python's rules. If it is some sort of homebrewn
language you map to python, adapt the mapper to enforce lower-case and make
all your properties lower case.

Diez
Oct 13 '08 #2

P: n/a
Just so I don't hijack my own thread, the issue is 'how to wrap an
object which is not case sensitive'.

The reason I am stuck dealing with this?... The application's API is
accessed through COM, so I don't know if I can do anything but react
to what I get. The API was written while the app (Softimage|XSI - one
of 3 leading 3D applications for high-end visual effects) was owned by
Microsoft. I'm not sure if it is standard for Microsoft or just the
way this app was implemented (perhaps because under-users were
scripting in VBscript which is not case sensitive).

XSI allows many languages to be used via COM, even from within the
software (there are built-in code editors). In the early days,
VBScript was the most common scripting language used while anything
more hard-core was done in C++ (of course the C implementation is case
sensitive - well as far as I know). Then JScript became the most
common, now Python is considered standard.

Anyway, the standard practice is to use mixed-case, so I need to
adhere to it as the resulting framework I am creating needs to be
intuitive to use (my end-user is still writing code. It's an API for
an API I guess...)

I don't *think* I need to worry too much about performance because I'm
not doing any serious processing, this is more about convention
enforcement and quality control rather than number crunching. I might
try to write something generic which gets executed by the wrappers
__getattr__ and __setattr__, but I was hoping for some nifty
workaround, maybe in the form of a decorator or something? Again...
any ideas?
Cheers,

- Rafe
On Oct 13, 4:15*pm, "Diez B. Roggisch" <de...@nospam.web.dewrote:
Rafe wrote:
Hi,
I'm working within an application (making a lot of wrappers), but the
application is not case sensitive. For example, Typing obj.name,
obj.Name, or even object.naMe is all fine (as far as the app is
concerned). The problem is, If someone makes a typo, they may get an
unexpected error due accidentally calling the original attribute
instead of the wrapped version. Does anyone have a simple solution for
this?
I can protect against some cases just by making an 'alias':
class AClass(object):
* * def name(self):
* * * * print "hello"
* * Name = name
...but this doesn't protect against typos, it gets more complicated
with multi-word attribute names, and it makes my epydocs confusing to
read since all spelling versions are shown (I AM concerned about my
docs being clear, but not as much as stopping typo related errors).
I thought about using my wrapper's __getattr__ and __setattr__, but I
I am concerned about the overhead of every delegated attribute call
running a search and compare (<paramName>.lower() based compare?).
Any ideas or precedence?

Ideas? Don't do that...

Seriously: where does that code come from, who's typing it? If it is python,
then make people follow python's rules. If it is some sort of homebrewn
language you map to python, adapt the mapper to enforce lower-case and make
all your properties lower case.

Diez
Oct 13 '08 #3

P: n/a
Rafe wrote:
Just so I don't hijack my own thread, the issue is 'how to wrap an
object which is not case sensitive'.

The reason I am stuck dealing with this?... The application's API is
accessed through COM, so I don't know if I can do anything but react
to what I get. The API was written while the app (Softimage|XSI - one
of 3 leading 3D applications for high-end visual effects) was owned by
Microsoft. I'm not sure if it is standard for Microsoft or just the
way this app was implemented (perhaps because under-users were
scripting in VBscript which is not case sensitive).

XSI allows many languages to be used via COM, even from within the
software (there are built-in code editors). In the early days,
VBScript was the most common scripting language used while anything
more hard-core was done in C++ (of course the C implementation is case
sensitive - well as far as I know). Then JScript became the most
common, now Python is considered standard.

Anyway, the standard practice is to use mixed-case, so I need to
adhere to it as the resulting framework I am creating needs to be
intuitive to use (my end-user is still writing code. It's an API for
an API I guess...)

I don't *think* I need to worry too much about performance because I'm
not doing any serious processing, this is more about convention
enforcement and quality control rather than number crunching. I might
try to write something generic which gets executed by the wrappers
__getattr__ and __setattr__, but I was hoping for some nifty
workaround, maybe in the form of a decorator or something? Again...
any ideas?
I still don't get the full picture - anyway, what I infer is this:

- for your own sake, you want to stick with "proper" naming, whatever that
means

- you can't control what will be called.

Then I'd go with the __getattr__-way, plus maybe a meta-class (or
postprocessing) that maps all attributes to a lower-case-variant as well,
so that the lookup is efficient.

Does that help?

Diez

Oct 13 '08 #4

P: n/a
On Oct 13, 10:11 am, Rafe <rafesa...@gmail.comwrote:
Hi,

I'm working within an application (making a lot of wrappers), but the
application is not case sensitive. For example, Typing obj.name,
obj.Name, or even object.naMe is all fine (as far as the app is
concerned). The problem is, If someone makes a typo, they may get an
unexpected error due accidentally calling the original attribute
instead of the wrapped version. Does anyone have a simple solution for
this?

I can protect against some cases just by making an 'alias':
class AClass(object):
def name(self):
print "hello"

Name = name

...but this doesn't protect against typos, it gets more complicated
with multi-word attribute names, and it makes my epydocs confusing to
read since all spelling versions are shown (I AM concerned about my
docs being clear, but not as much as stopping typo related errors).

I thought about using my wrapper's __getattr__ and __setattr__, but I
I am concerned about the overhead of every delegated attribute call
running a search and compare (<paramName>.lower() based compare?).

Any ideas or precedence?
If you define '__getattr__' then it will only be called for attribute
names that don't exist. So only explicitly define the names you want
in the lowercase variant and then have something like the following:

def __getattr__(self, name):
return object.__getattr__(self, name.lower())

That way each name only appears once and you only get the extra
'__getattr__' in your epydoc docs.

Michael

>
Cheers,

- Rafe
--
http://www.ironpythoninaction.com/
Oct 13 '08 #5

P: n/a
On Mon, 13 Oct 2008 04:08:03 -0700 (PDT), Rafe <ra*******@gmail.comwrote:
Just so I don't hijack my own thread, the issue is 'how to wrap an
object which is not case sensitive'.

The reason I am stuck dealing with this?... The application's API is
accessed through COM,
[snip]
XSI allows many languages to be used via COM, even from within the
software (there are built-in code editors). In the early days,
VBScript was the most common scripting language used while anything
more hard-core was done in C++ (of course the C implementation is case
sensitive - well as far as I know). Then JScript became the most
common, now Python is considered standard.
[Much other stuff I didn't understand is snipped here.]

I apologize in advance if this appears unhelpful. I
couldn't make hide nor hair of the above explanation.

The apparent requirement that there be no boundary between
the software's developers (who presumably are trained and
can learn case conventions) and its users is a completely
new one to me. Can you possibly make such software
reliable? Doesn't this open you up to the mother of all SQL
injection attacks? I really would like to understand the
circumstances in which "Keep the users out of the code" (or,
pretty much equivalent, "Don't let the implementation
language drive the user interface") isn't the right answer.

--
To email me, substitute nowhere->spamcop, invalid->net.
Oct 13 '08 #6

P: n/a
On Oct 13, 4:08*am, Rafe <rafesa...@gmail.comwrote:
Just so I don't hijack my own thread, the issue is 'how to wrap an
object which is not case sensitive'.

The reason I am stuck dealing with this?... The application's API is
accessed through COM, so I don't know if I can do anything but react
to what I get. The API was written while the app (Softimage|XSI - one
of 3 leading 3D applications for high-end visual effects) was owned by
Microsoft. I'm not sure if it is standard for Microsoft or just the
way this app was implemented (perhaps because under-users were
scripting in VBscript which is not case sensitive).

XSI allows many languages to be used via COM, even from within the
software (there are built-in code editors). In the early days,
VBScript was the most common scripting language used while anything
more hard-core was done in C++ (of course the C implementation is case
sensitive - well as far as I know). Then JScript became the most
common, now Python is considered standard.

Anyway, the standard practice is to use mixed-case, so I need to
adhere to it as the resulting framework I am creating needs to be
intuitive to use (my end-user is still writing code. It's an API for
an API I guess...)

I don't *think* I need to worry too much about performance because I'm
not doing any serious processing, this is more about convention
enforcement and quality control rather than number crunching. I might
try to write something generic which gets executed by the wrappers
__getattr__ and __setattr__, but I was hoping for some nifty
workaround, maybe in the form of a decorator or something? Again...
any ideas?

Cheers,

- Rafe

On Oct 13, 4:15*pm, "Diez B. Roggisch" <de...@nospam.web.dewrote:
Rafe wrote:
Hi,
I'm working within an application (making a lot of wrappers), but the
application is not case sensitive. For example, Typing obj.name,
obj.Name, or even object.naMe is all fine (as far as the app is
concerned). The problem is, If someone makes a typo, they may get an
unexpected error due accidentally calling the original attribute
instead of the wrapped version. Does anyone have a simple solution for
this?
I can protect against some cases just by making an 'alias':
class AClass(object):
* * def name(self):
* * * * print "hello"
* * Name = name
...but this doesn't protect against typos, it gets more complicated
with multi-word attribute names, and it makes my epydocs confusing to
read since all spelling versions are shown (I AM concerned about my
docs being clear, but not as much as stopping typo related errors).
I thought about using my wrapper's __getattr__ and __setattr__, but I
I am concerned about the overhead of every delegated attribute call
running a search and compare (<paramName>.lower() based compare?).
Any ideas or precedence?
Ideas? Don't do that...
Seriously: where does that code come from, who's typing it? If it is python,
then make people follow python's rules. If it is some sort of homebrewn
language you map to python, adapt the mapper to enforce lower-case and make
all your properties lower case.
Diez

So, this application you are writing for allows you to script/write
callbacks in python? They are then somehow accessable through a COM
interface exposed by the application? You are worried that someone
using the application will run into case-sensitivity if they access
the code written in python?

There isn't much overhead with __getattr__ since it is _only_ called
if the initial look-up didn't find the name. You can do something like
this:

class C(object):
def __init__(self, ...):
...
self._lookingup = False
...

# If this is re-entered while already looking up a value,
# then we know that there is a problem. Not thread safe.
def __getattr__(self, attr):
try:
if self._lookingup:
raise AttributeError("'C' object has no attribute
%r"%attr)
self._lookingup = True
return getattr(self, attr.lower())
finally:
self._lookingup = False
def __setattr__(self, attr, value):
super(C, self).__setattr__(attr.lower(), value)

Matt
Oct 13 '08 #7

P: n/a
I really appreciate the replies. I hope you gyus will stick with me
through one more round.

super(C, self).__setattr__(attr.lower(), value)

Unfortunately, this will not work because an attribute name such as
"getObject" is legal (I'll explain the convention in a moment.) I
think I would have to loop over all attributes and force both sides of
the compare to lower case to test for a match.

just skip ahead to the example code if you don't want more confusing
background ;)
Bear with me while I try to explain.

Basically, I am working with this application like (I think) any
application would work through a COM object. That said, I can access
python from within the application as well because it is a kind of dev
environment. 3D applications are blended GUI and development
environment and users are expected to use it through both the API and
the GUI. What may seem strange to most people here, is that you will
get hard-core programmers and surface-level users (and everything in
between, like me) working and/or developing in the same environment.
These 3D software applications are quite large and complex.

The application is called "Softimage|XSI", commonly called "XSI". It
is a 3D application. Most companies will licenses the software but
then build layers on top of it for pipeline productivity and
communication reasons. So, it is standard for a user of the
application to also write scripts or more complex OO models. I
mentioned it was written during the brief period of time where
Softimage was owned by Microsoft because I thought there might be some
precedence for the case sensitivity issues. It was not written by
Microsoft engineers directly, but they did enforce *some* standards.
The common naming convention in XSI is (using PEP008 terminology)
"CapitalizedWords" for objects and functions/methods, and "mixedCase"
for variables: This is from the C++ API:

C++ Example: connecting to XSI
// gets the application object, which you can use to communicate
with XSI
Application app;
app.LogMessage( "Welcome to XSI!" );

C++ Example: creating an X3DObject
// returns the reference root object
namespace XSI;
Application app;
CRef rootRef = app.GetActiveSceneRoot();

// create object with a reference object
X3DObject rootObj(rootRef);

The python version of the above C++ example looks like this.
from win32com.client.dynamic import Dispatch
XSI = Dispatch('XSI.Application').Application
XSI.LogMessage("Welcome to XSI!")
root = XSI.ActiveSceneRoot
As for the convention I chose, it is right out of PEP008.
"Function Names

Function names should be lowercase, with words separated by
underscores
as necessary to improve readability.

mixedCase is allowed only in contexts where that's already the
prevailing style (e.g. threading.py), to retain backwards
compatibility."

Too keep my code in line with XSI's API, I took this second part to
hear. All other conventions are in line with PEP008 I believe. Lastly,
though I can see how this might sound confusing, I stick with the XSI
API convension exactly when accessing it directly("CapitalizedWords"),
but anything I write is PEP008 with mixedCase.

The most important part of all this though is my original issue. For
some reason, the XSI implementation is not case sensitive. This
works!...

from win32com.client.dynamic import Dispatch
XSI = Dispatch('XSI.Application').Application
XSI.LogMessage("Welcome to XSI!")
XSI.loGmeSSAGE("Welcome to XSI!")
This is probably totally usless info for this discussion (like I
haven't already provided enough of that!), but the XSI API, or object
model, is a little like a complex XML DOM tree...

obj = XSI.Dictionary.GetObject("my3DObject")
children = obj.Children
for child in children:
XSI.LogMessage(child.Name)
To wrap and override the 'name' attribute I use this class. (Note I
had some trouble with __setattr__ but this IS stable. I welcome
comments as this is probably one of the most confusing things to work
with for new python users.)
class DelegationWrapper(object):
"""
This is a new-style base class that allows python to extend, or
override
attributes of a given X3DObject.

:parameters:
obj : object instance
If this class (or a sub-class of this class) do not have
an
attribute, this wrapped object will be checked before
failing.
"""
def __init__(self, obj):
"""
Store the object to delegate to.
"""
self.__obj = obj
def __repr__(self):
"""
Makes the object's name the string representation of the
object, just
like XSI does.
"""
return str(self.__obj.name)
def __getattr__(self, name):
"""
Tries to delegate any attribute calls not found in this class
to the
X3DObject.
"""
# Try to delegate to the 3DObject.
obj = self.__dict__["__obj"]
try:
return obj.__getattr__(name)
except:
pass

# Raise an attribute error (Python requires this to avoid
problems)
className = self.__class__.__name__
raise AttributeError("%s has no attribute '%s'." % (className,
name))
def __setattr__(self, name, val):
"""
Tries to delegate any attribute assignment not found in this
class to
the X3DObject.
"""
# This allows sub-classes to add "private" attributes freely.
# dir is checked insteaf od __dict__ because it contains bound
# attributes not available in the instance __dict__.
if name in dir(self) or name.startswith("_"):
object.__setattr__(self, name, val)
return

# Try to delegate to the X3DObject.
try:
self.__dict__["__obj"].__setattr__(name, val)
return
except TypeError, err:
raise TypeError(err)
except AttributeError:
pass # raised later
except Exception, err:
raise Exception(err)

# Don't allow addition of new 'public' attributes with
AttributeError
className = self.__class__.__name__
raise AttributeError("%s has no attribute '%s'." % (className,
name))
@property
def name(self):
"""
This doesn't do anything here, but in my real code it does.
The
problem is, if the user types 'Name' this will be bypassed.
"""
return self.__obj.Name

So is iterating through dir() to force both the members of dir(), and
the requested attribute name, to lower case for a comparison, really
the easiest way?

Thanks again for sticking with me. I hope I didn't add to the
confusion. What I learn I will of course pass on.

- Rafe


On Oct 14, 12:14*am, Matimus <mccre...@gmail.comwrote:
On Oct 13, 4:08*am, Rafe <rafesa...@gmail.comwrote:
Just so I don't hijack my own thread, the issue is 'how to wrap an
object which is not case sensitive'.
The reason I am stuck dealing with this?... The application's API is
accessed through COM, so I don't know if I can do anything but react
to what I get. The API was written while the app (Softimage|XSI - one
of 3 leading 3D applications for high-end visual effects) was owned by
Microsoft. I'm not sure if it is standard for Microsoft or just the
way this app was implemented (perhaps because under-users were
scripting in VBscript which is not case sensitive).
XSI allows many languages to be used via COM, even from within the
software (there are built-in code editors). In the early days,
VBScript was the most common scripting language used while anything
more hard-core was done in C++ (of course the C implementation is case
sensitive - well as far as I know). Then JScript became the most
common, now Python is considered standard.
Anyway, the standard practice is to use mixed-case, so I need to
adhere to it as the resulting framework I am creating needs to be
intuitive to use (my end-user is still writing code. It's an API for
an API I guess...)
I don't *think* I need to worry too much about performance because I'm
not doing any serious processing, this is more about convention
enforcement and quality control rather than number crunching. I might
try to write something generic which gets executed by the wrappers
__getattr__ and __setattr__, but I was hoping for some nifty
workaround, maybe in the form of a decorator or something? Again...
any ideas?
Cheers,
- Rafe
On Oct 13, 4:15*pm, "Diez B. Roggisch" <de...@nospam.web.dewrote:
Rafe wrote:
Hi,
I'm working within an application (making a lot of wrappers), but the
application is not case sensitive. For example, Typing obj.name,
obj.Name, or even object.naMe is all fine (as far as the app is
concerned). The problem is, If someone makes a typo, they may get an
unexpected error due accidentally calling the original attribute
instead of the wrapped version. Does anyone have a simple solution for
this?
I can protect against some cases just by making an 'alias':
class AClass(object):
* * def name(self):
* * * * print "hello"
* * Name = name
...but this doesn't protect against typos, it gets more complicated
with multi-word attribute names, and it makes my epydocs confusing to
read since all spelling versions are shown (I AM concerned about my
docs being clear, but not as much as stopping typo related errors).
I thought about using my wrapper's __getattr__ and __setattr__, butI
I am concerned about the overhead of every delegated attribute call
running a search and compare (<paramName>.lower() based compare?).
Any ideas or precedence?
Ideas? Don't do that...
Seriously: where does that code come from, who's typing it? If it is python,
then make people follow python's rules. If it is some sort of homebrewn
language you map to python, adapt the mapper to enforce lower-case and make
all your properties lower case.
Diez

So, this application you are writing for allows you to script/write
callbacks in python? They are then somehow accessable through a COM
interface exposed by the application? You are worried that someone
using the application will run into case-sensitivity if they access
the code written in python?

There isn't much overhead with __getattr__ since it is _only_ called
if the initial look-up didn't find the name. You can do something like
this:

class C(object):
* * def __init__(self, ...):
* * * * ...
* * * * self._lookingup = False
* * * * ...

* * # If this is re-entered while already looking up a value,
* * # then we know that there is a problem. Not thread safe.
* * def __getattr__(self, attr):
* * * * try:
* * * * * * if self._lookingup:
* * * * * * * * raise AttributeError("'C' object has no attribute
%r"%attr)
* * * * * * self._lookingup = True
* * * * * * return getattr(self, attr.lower())
* * * * finally:
* * * * * * self._lookingup = False

* * def __setattr__(self, attr, value):
* * * * super(C, self).__setattr__(attr.lower(), value)

Matt
Oct 14 '08 #8

P: n/a
I'm not sure what went wrong with the formatting in my last post. my
code is under 80 characters wide. Here is a more narrow copy and
paste...

class DelegationWrapper(object):
"""
This is a new-style base class that allows python to
extend, or override attributes of a given X3DObject.

:parameters:
obj : object instance
If this class (or a sub-class of this class) do
not have an attribute, this wrapped object will
be checked before failing.
"""
def __init__(self, obj):
"""
Store the object to delegate to.
"""
self.__obj = obj
def __repr__(self):
"""
Makes the object's name the string representation
of the object, just like XSI does.
"""
return str(self.__obj.name)
def __getattr__(self, name):
"""
Tries to delegate any attribute calls not found in
this class to the X3DObject.
"""
# Try to delegate to the 3DObject.
obj = self.__dict__["__obj"]
try:
return obj.__getattr__(name)
except:
pass

# Raise an attribute error (Python requires this
# to avoid problems)
className = self.__class__.__name__
msg = "%s has no attribute '%s'." % (className, name)
raise AttributeError(msg)
def __setattr__(self, name, val):
"""
Tries to delegate any attribute assignment not found
in this class to the X3DObject.
"""
# This allows sub-classes to add "private" attributes
# freely. dir is checked insteaf od __dict__ because
# it contains bound attributes not available in the
# instance __dict__.
if name in dir(self) or name.startswith("_"):
object.__setattr__(self, name, val)
return

# Try to delegate to the X3DObject.
try:
self.__dict__["__obj"].__setattr__(name, val)
return
except TypeError, err:
raise TypeError(err)
except AttributeError:
pass # raised later
except Exception, err:
raise Exception(err)

# Don't allow addition of new 'public' attributes
# with AttributeError
className = self.__class__.__name__
msg = "%s has no attribute '%s'." % (className, name)
raise AttributeError(msg)
@property
def name(self):
"""
This doesn't do anything here, but in my real code it
does. The problem is, if the user types 'Name' this
will be bypassed.
"""
return self.__obj.Name
- Rafe


On Oct 14, 11:29*am, Rafe <rafesa...@gmail.comwrote:
I really appreciate the replies. I hope you gyus will stick with me
through one more round.

super(C, self).__setattr__(attr.lower(), value)

Unfortunately, this will not work because an attribute name such as
"getObject" is legal (I'll explain the convention in a moment.) I
think I would have to loop over all attributes and force both sides of
the compare to lower case to test for a match.

just skip ahead to the example code if you don't want more confusing
background ;)

Bear with me while I try to explain.

Basically, I am working with this application like (I think) any
application would work through a COM object. That said, I can access
python from within the application as well because it is a kind of dev
environment. 3D applications are blended GUI and development
environment and users are expected to use it through both the API and
the GUI. What may seem strange to most people here, is that you will
get hard-core programmers and surface-level users (and everything in
between, like me) working and/or developing in the same environment.
These 3D software applications are quite large and complex.

The application is called "Softimage|XSI", commonly called "XSI". It
is a 3D application. Most companies will licenses the software but
then build layers on top of it for pipeline productivity and
communication reasons. So, it is standard for a user of the
application to also write scripts or more complex OO models. I
mentioned it was written during the brief period of time where
Softimage was owned by Microsoft because I thought there might be some
precedence for the case sensitivity issues. It was not written by
Microsoft engineers directly, but they did enforce *some* standards.

The common naming convention in XSI is (using PEP008 terminology)
"CapitalizedWords" for objects and functions/methods, and "mixedCase"
for variables: This is from the C++ API:

C++ Example: connecting to XSI
* * // gets the application object, which you can use to communicate
with XSI
* * Application app;
* * app.LogMessage( "Welcome to XSI!" );

C++ Example: creating an X3DObject
* * // returns the reference root object
* * namespace XSI;
* * Application app;
* * CRef rootRef = app.GetActiveSceneRoot();

* * // create object with a reference object
* * X3DObject rootObj(rootRef);

The python version of the above C++ example looks like this.
* * from win32com.client.dynamic import Dispatch
* * XSI = Dispatch('XSI.Application').Application
* * XSI.LogMessage("Welcome to XSI!")
* * root = XSI.ActiveSceneRoot

As for the convention I chose, it is right out of PEP008.
"Function Names

* * * Function names should be lowercase, with words separated by
underscores
* * * as necessary to improve readability.

* * * mixedCase is allowed only in contexts where that's already the
* * * prevailing style (e.g. threading.py), to retain backwards
compatibility."

Too keep my code in line with XSI's API, I took this second part to
hear. All other conventions are in line with PEP008 I believe. Lastly,
though I can see how this might sound confusing, I stick with the XSI
API convension exactly when accessing it directly("CapitalizedWords"),
but anything I write is PEP008 with mixedCase.

The most important part of all this though is my original issue. For
some reason, the XSI implementation is not case sensitive. This
works!...

from win32com.client.dynamic import Dispatch
XSI = Dispatch('XSI.Application').Application
XSI.LogMessage("Welcome to XSI!")
XSI.loGmeSSAGE("Welcome to XSI!")

This is probably totally usless info for this discussion (like I
haven't already provided enough of that!), but the XSI API, or object
model, is a little like a complex XML DOM tree...

obj = XSI.Dictionary.GetObject("my3DObject")
children = obj.Children
for child in children:
* * XSI.LogMessage(child.Name)

To wrap and override the 'name' attribute I use this class. (Note I
had some trouble with __setattr__ but this IS stable. I welcome
comments as this is probably one of the most confusing things to work
with for new python users.)

class DelegationWrapper(object):
* * """
* * This is a new-style base class that allows python to extend, or
override
* * attributes of a given X3DObject.

* * :parameters:
* * * * obj : object instance
* * * * * * If this class (or a sub-class of this class) do not have
an
* * * * * * attribute, this wrapped object will be checked before
failing.
* * """
* * def __init__(self, obj):
* * * * """
* * * * Store the object to delegate to.
* * * * """
* * * * self.__obj = obj

* * def __repr__(self):
* * * * """
* * * * Makes the object's name the string representation of the
object, just
* * * * like XSI does.
* * * * """
* * * * return str(self.__obj.name)

* * def __getattr__(self, name):
* * * * """
* * * * Tries to delegate any attribute calls not found in this class
to the
* * * * X3DObject.
* * * * """
* * * * # Try to delegate to the 3DObject.
* * * * obj = self.__dict__["__obj"]
* * * * try:
* * * * * * return obj.__getattr__(name)
* * * * except:
* * * * * * pass

* * * * # Raise an attribute error (Python requires this to avoid
problems)
* * * * className = self.__class__.__name__
* * * * raise AttributeError("%s has no attribute '%s'." % (className,
name))

* * def __setattr__(self, name, val):
* * * * """
* * * * Tries to delegate any attribute assignment not found in this
class to
* * * * the X3DObject.
* * * * """
* * * * # This allows sub-classes to add "private" attributes freely.
* * * * # dir is checked insteaf od __dict__ because it contains bound
* * * * # attributes not available in the instance __dict__.
* * * * if name in dir(self) or name.startswith("_"):
* * * * * * object.__setattr__(self, name, val)
* * * * * * return

* * * * # Try to delegate to the X3DObject.
* * * * try:
* * * * * * self.__dict__["__obj"].__setattr__(name, val)
* * * * * * return
* * * * except TypeError, err:
* * * * * * raise TypeError(err)
* * * * except AttributeError:
* * * * * * pass * # raised later
* * * * except Exception, err:
* * * * * * raise Exception(err)

* * * * # Don't allow addition of new 'public' attributes with
AttributeError
* * * * className = self.__class__.__name__
* * * * raise AttributeError("%s has no attribute '%s'." % (className,
name))

* * @property
* * def name(self):
* * * * """
* * * * This doesn't do anything here, but in my real code it does.
The
* * * * problem is, if the user types 'Name' this will be bypassed.
* * * * """
* * * * return self.__obj.Name

So is iterating through dir() to force both the members of dir(), and
the requested attribute name, to lower case for a comparison, really
the easiest way?

Thanks again for sticking with me. I hope I didn't add to the
confusion. What I learn I will of course pass on.

- Rafe

On Oct 14, 12:14*am, Matimus <mccre...@gmail.comwrote:
On Oct 13, 4:08*am, Rafe <rafesa...@gmail.comwrote:
Just so I don't hijack my own thread, the issue is 'how to wrap an
object which is not case sensitive'.
The reason I am stuck dealing with this?... The application's API is
accessed through COM, so I don't know if I can do anything but react
to what I get. The API was written while the app (Softimage|XSI - one
of 3 leading 3D applications for high-end visual effects) was owned by
Microsoft. I'm not sure if it is standard for Microsoft or just the
way this app was implemented (perhaps because under-users were
scripting in VBscript which is not case sensitive).
XSI allows many languages to be used via COM, even from within the
software (there are built-in code editors). In the early days,
VBScript was the most common scripting language used while anything
more hard-core was done in C++ (of course the C implementation is case
sensitive - well as far as I know). Then JScript became the most
common, now Python is considered standard.
Anyway, the standard practice is to use mixed-case, so I need to
adhere to it as the resulting framework I am creating needs to be
intuitive to use (my end-user is still writing code. It's an API for
an API I guess...)
I don't *think* I need to worry too much about performance because I'm
not doing any serious processing, this is more about convention
enforcement and quality control rather than number crunching. I might
try to write something generic which gets executed by the wrappers
__getattr__ and __setattr__, but I was hoping for some nifty
workaround, maybe in the form of a decorator or something? Again...
any ideas?
Cheers,
- Rafe
On Oct 13, 4:15*pm, "Diez B. Roggisch" <de...@nospam.web.dewrote:
Rafe wrote:
Hi,
I'm working within an application (making a lot of wrappers), butthe
application is not case sensitive. For example, Typing obj.name,
obj.Name, or even object.naMe is all fine (as far as the app is
concerned). The problem is, If someone makes a typo, they may getan
unexpected error due accidentally calling the original attribute
instead of the wrapped version. Does anyone have a simple solution for
this?
I can protect against some cases just by making an 'alias':
class AClass(object):
* * def name(self):
* * * * print "hello"
* * Name = name
...but this doesn't protect against typos, it gets more complicated
with multi-word attribute names, and it makes my epydocs confusing to
read since all spelling versions are shown (I AM concerned about my
docs being clear, but not as much as stopping typo related errors).
I thought about using my wrapper's __getattr__ and __setattr__, but I
I am concerned about the overhead of every delegated attribute call
running a search and compare (<paramName>.lower() based compare?)..
Any ideas or precedence?
Ideas? Don't do that...

...

read more
Oct 14 '08 #9

P: n/a
[I posted a version of the following but it contained a broken
example. I deleted it through GoogleGroups, but I'm adding this
message in case the old version is still visible on the list. If it
isn't ignore this.]
I really appreciate the replies. I hope you guys will stick with me
through one more round.

super(C, self).__setattr__(attr.lower(), value)

Unfortunately, this will not work because an attribute name such as
"getObject" is legal (I'll explain the convention in a moment.) I
think I would have to loop over all attributes and force both sides of
the compare to lower case to test for a match.

just skip ahead to the example code if you don't want more confusing
background ;)

Bear with me while I try to explain.

Basically, I am working with this application like (I think) any
application would work through a COM object. That said, I can access
python from within the application as well because it is a kind of dev
environment. 3D applications are blended GUI and development
environment and users are expected to use it through both the API and
the GUI. What may seem strange to most people here, is that you will
get hard-core programmers and surface-level users (and everything in
between, like me) working and/or developing in the same environment.
These 3D software applications are quite large and complex.

The application is called "Softimage|XSI", commonly called "XSI". It
is a 3D application. Most companies will licenses the software but
then build layers on top of it for pipeline productivity and
communication reasons. So, it is standard for a user of the
application to also write scripts or more complex OO models. I
mentioned it was written during the brief period of time where
Softimage was owned by Microsoft because I thought there might be some
precedence for the case sensitivity issues. It was not written by
Microsoft engineers directly, but they did enforce *some* standards.

The common naming convention in XSI is (using PEP008 terminology)
"CapitalizedWords" for objects and functions/methods, and "mixedCase"
for variables: This is from the C++ API:

C++ Example: connecting to XSI
// gets the application object, which you can use to communicate
// with XSI
Application app;
app.LogMessage( "Welcome to XSI!" );

C++ Example: creating an X3DObject
// returns the reference root object
namespace XSI;
Application app;
CRef rootRef = app.GetActiveSceneRoot();

// create object with a reference object
X3DObject rootObj(rootRef);

The python version of the above C++ example looks like this.
from win32com.client.dynamic import Dispatch
XSI = Dispatch('XSI.Application').Application
XSI.LogMessage("Welcome to XSI!")
root = XSI.ActiveSceneRoot

As for the convention I chose, it is right out of PEP008.
"Function Names

Function names should be lowercase, with words separated by
underscores as necessary to improve readability.

mixedCase is allowed only in contexts where that's already the
prevailing style (e.g. threading.py), to retain backwards
compatibility."

Too keep my code in line with XSI's API, I took this second part to
hear. All other conventions are in line with PEP008 I believe. Lastly,
though I can see how this might sound confusing, I stick with the XSI
API convension exactly when accessing it directly("CapitalizedWords"),
but anything I write is PEP008 with mixedCase.

The most important part of all this though is my original issue. For
some reason, the XSI implementation is not case sensitive. This
works!...

from win32com.client.dynamic import Dispatch
XSI = Dispatch('XSI.Application').Application
XSI.LogMessage("Welcome to XSI!")
XSI.loGmeSSAGE("Welcome to XSI!")

This is probably totally usless info for this discussion (like I
haven't already provided enough of that!), but the XSI API, or object
model, is a little like a complex XML DOM tree...

obj = XSI.Dictionary.GetObject("my3DObject")
children = obj.Children
for child in children:
XSI.LogMessage(child.Name)

To wrap and override the 'name' attribute I use this class. (Note I
had some trouble with __setattr__ but this IS stable. I welcome
comments as this is probably one of the most confusing things to work
with for new python users.)
class DelegationWrapper(object):
"""
This is a new-style base class that allows python to
extend, or override attributes of a given X3DObject.

:parameters:
obj : object instance
If this class (or a sub-class of this class) do
not have an attribute, this wrapped object will
be checked before failing.
"""
def __init__(self, obj):
"""
Store the object to delegate to.
"""
self.__obj = obj
def __repr__(self):
"""
Makes the object's name the string representation
of the object, just like XSI does.
"""
return str(self.__obj.name)
def __getattr__(self, name):
"""
Tries to delegate any attribute calls not found in
this class to the X3DObject.
"""
# Try to delegate to the 3DObject.
obj = self.__dict__["_DelegationWrapper__obj"]
try:
return getattr(obj, name)
except:
pass

# Raise an attribute error (Python requires this
# to avoid problems)
className = self.__class__.__name__
msg = "%s has no attribute '%s'." % (className, name)
raise AttributeError(msg)
def __setattr__(self, name, val):
"""
Tries to delegate any attribute assignment not found
in this class to the X3DObject.
"""
# This allows sub-classes to add "private" attributes
# freely. dir is checked insteaf od __dict__ because
# it contains bound attributes not available in the
# instance __dict__.
if name in dir(self) or name.startswith("_"):
super(DelegationWrapper, self).__setattr__(name, val)
return

# Try to delegate to the X3DObject.
obj = self.__dict__["_DelegationWrapper__obj"]
try:
obj.__setattr__(name, val)
return
except TypeError, err:
raise TypeError(err)
except AttributeError:
pass # raised later
except Exception, err:
raise Exception(err)

# Don't allow addition of new 'public' attributes
# with AttributeError
className = self.__class__.__name__
msg = "%s has no attribute '%s'." % (className, name)
raise AttributeError(msg)
def set_name(self, val):
self.__obj.name = val

def get_name(self):
return self.__obj.name

name = property(get_name, set_name)
# Usage...
class A(object):
name = "a name"
test = "a test"

a = A()
b = DelegationWrapper(a)
print b.name
print b.test
b.name = "new name"
print b.name

So is iterating through dir() to force both the members of dir(), and
the requested attribute name, to lower case for a comparison, really
the easiest way?

Thanks again for sticking with me. I hope I didn't add to the
confusion. What I learn I will of course pass on.

- Rafe

On Oct 14, 12:14*am, Matimus <mccre...@gmail.comwrote:
On Oct 13, 4:08*am, Rafe <rafesa...@gmail.comwrote:
Just so I don't hijack my own thread, the issue is 'how to wrap an
object which is not case sensitive'.
The reason I am stuck dealing with this?... The application's API is
accessed through COM, so I don't know if I can do anything but react
to what I get. The API was written while the app (Softimage|XSI - one
of 3 leading 3D applications for high-end visual effects) was owned by
Microsoft. I'm not sure if it is standard for Microsoft or just the
way this app was implemented (perhaps because under-users were
scripting in VBscript which is not case sensitive).
XSI allows many languages to be used via COM, even from within the
software (there are built-in code editors). In the early days,
VBScript was the most common scripting language used while anything
more hard-core was done in C++ (of course the C implementation is case
sensitive - well as far as I know). Then JScript became the most
common, now Python is considered standard.
Anyway, the standard practice is to use mixed-case, so I need to
adhere to it as the resulting framework I am creating needs to be
intuitive to use (my end-user is still writing code. It's an API for
an API I guess...)
I don't *think* I need to worry too much about performance because I'm
not doing any serious processing, this is more about convention
enforcement and quality control rather than number crunching. I might
try to write something generic which gets executed by the wrappers
__getattr__ and __setattr__, but I was hoping for some nifty
workaround, maybe in the form of a decorator or something? Again...
any ideas?
Cheers,
- Rafe
On Oct 13, 4:15*pm, "Diez B. Roggisch" <de...@nospam.web.dewrote:
Rafe wrote:
Hi,
I'm working within an application (making a lot of wrappers), but the
application is not case sensitive. For example, Typing obj.name,
obj.Name, or even object.naMe is all fine (as far as the app is
concerned). The problem is, If someone makes a typo, they may get an
unexpected error due accidentally calling the original attribute
instead of the wrapped version. Does anyone have a simple solution for
this?
I can protect against some cases just by making an 'alias':
class AClass(object):
* * def name(self):
* * * * print "hello"
* * Name = name
...but this doesn't protect against typos, it gets more complicated
with multi-word attribute names, and it makes my epydocs confusing to
read since all spelling versions are shown (I AM concerned about my
docs being clear, but not as much as stopping typo related errors).
I thought about using my wrapper's __getattr__ and __setattr__, butI
I am concerned about the overhead of every delegated attribute call
running a search and compare (<paramName>.lower() based compare?).
Any ideas or precedence?
Ideas? Don't do that...
Seriously: where does that code come from, who's typing it? If it is python,
then make people follow python's rules. If it is some sort of homebrewn
language you map to python, adapt the mapper to enforce lower-case and make
all your properties lower case.
Diez

So, this application you are writing for allows you to script/write
callbacks in python? They are then somehow accessable through a COM
interface exposed by the application? You are worried that someone
using the application will run into case-sensitivity if they access
the code written in python?

There isn't much overhead with __getattr__ since it is _only_ called
if the initial look-up didn't find the name. You can do something like
this:

class C(object):
* * def __init__(self, ...):
* * * * ...
* * * * self._lookingup = False
* * * * ...

* * # If this is re-entered while already looking up a value,
* * # then we know that there is a problem. Not thread safe.
* * def __getattr__(self, attr):
* * * * try:
* * * * * * if self._lookingup:
* * * * * * * * raise AttributeError("'C' object has no attribute
%r"%attr)
* * * * * * self._lookingup = True
* * * * * * return getattr(self, attr.lower())
* * * * finally:
* * * * * * self._lookingup = False

* * def __setattr__(self, attr, value):
* * * * super(C, self).__setattr__(attr.lower(), value)

Matt
Oct 14 '08 #10

P: n/a
So is iterating through dir() to force both the members of dir(), and
the requested attribute name, to lower case for a comparison, really
the easiest way?

Thanks again for sticking with me. I hope I didn't add to the
confusion. What I learn I will of course pass on.

- Rafe
It still isn't clear to me _why_ you are wrapping this COM object. You
aren't adding any functionality. If you are using win32com and the TLB
object you are using has a tlb, then you can generate wrapper classes
for them automatically using makepy. You can extend those. If you want
to do it by hand you should be able to just create a class and inherit
win32com.client.DispatchBaseClass (and object if you want to make it
new-style). Unless your users are screaming for this feature, or there
is some technical reason that it is required, then implementing it is
a waste of time. Anybody who is used to developing at all is going to
accept that the software is case sensitive.

Matt
Oct 14 '08 #11

P: n/a
Thanks for the COM pointers Matt. I'll definitely look in to these.
Perhaps this will become a non-issue when I use one of these COM
wrappers...

Anybody who is used to developing at all is going to
accept that the software is case sensitive.
Case sensitive? Yes. Letting types create hard to debug behaviors that
raise either no exceptions or strange ones? No. This is what I am
trying to add. Protection.

It still isn't clear to me _why_ you are wrapping this COM object. You
aren't adding any functionality.
I've actually been able to add a lot of functionality. I just didn't
post the details of how I'm using it because I didn't think it had any
bearing on the original question. I can add a lot of automation and
convention enforcement to the API by wrapping and extending the
applications object-model. If you want me to give some real-world
examples (which would be related to 3D animation production) I
wouldn't mind doing so at all. I was just trying really hard to keep
the question generic (and failed it seems).
Thanks again for sticking with the discussion!

- Rafe
On Oct 15, 4:03*am, Matimus <mccre...@gmail.comwrote:
So is iterating through dir() to force both the members of dir(), and
the requested attribute name, to lower case for a comparison, really
the easiest way?
Thanks again for sticking with me. I hope I didn't add to the
confusion. What I learn I will of course pass on.
- Rafe

It still isn't clear to me _why_ you are wrapping this COM object. You
aren't adding any functionality. If you are using win32com and the TLB
object you are using has a tlb, then you can generate wrapper classes
for them automatically using makepy. You can extend those. If you want
to do it by hand you should be able to just create a class and inherit
win32com.client.DispatchBaseClass (and object if you want to make it
new-style). Unless your users are screaming for this feature, or there
is some technical reason that it is required, then implementing it is
a waste of time. Anybody who is used to developing at all is going to
accept that the software is case sensitive.

Matt
Oct 16 '08 #12

This discussion thread is closed

Replies have been disabled for this discussion.