Package: instancemethod
instancemethodSections:PurposeContentsInstallationUsageDeclarationValid UsageInvalid UsageIssues/LimitationsTestingPerformanceCurrent StatsBottlenecksPurposeThis package containing code and its example usage for restricting
method calls to instances of the class or subclass that contains the
method. Support has been added for nesting classes as attributes.ContentsThis package contains one module written in pure Python (3.5 or newer)
with the following code blocks:instancemethod: Higher order function for wrapping methodsNotAnInstanceError: Error raised when validation failsInstallationThis package is distributed to PyPi, and can be installed with either
of the following commands:pip install instancemethodpip3 install instancemethodUsageUse the Python decorator (@) symbol along with theinstancemethodfunction to wrap a method and designate it as an instance method.Declarationclass Foo:
    def __init__():
        ...

    @instancemethod
    def bar():
        ...Valid Usagefoo = Foo()
bar = foo.bar()Invalid Usagebar = Foo.bar()ReturnsNotAnInstanceError:
...Issues/LimitationsThere are currently no known stability or performance issues, so this
package has been marked as:ProductionPreviousbottlenecks, adeep-diveinto their causes, and the
explanation of theirsolutionare located in
theBottlenecksportion of thePerformancesection.TestingThe following test cases are currently implemented:Decorator allows the following valid cases:Calling method from an instance of a classFoo().bar()Calling method from an instance of a subclassSubFoo().bar()Calling method from an instance of a class in a nested hierarchyFoo.Bar().foo()Decorator blocks all other cases:Calling method without an instance of any above mentioned casesFuzzing implemented using instances of all built-in types
and non-inheriting classesPerformancePerformance testing is implemented for all valid test cases against a
control method. The control method is
decorated with anull_decoratordecorator that adds no functionality.
This is compared to theinstancemethoddecorator; 1 Million
function calls are made for each.Current StatsTheinstancemethoddecorator is currently:
1.64
times slower thannull_decoratorover the course of 1 Million calls.Average Nanoseconds per Call:instancemethod: 139.2null_decorator: 85.0Noappreciable difference has been found between the valid test
cases.BottlenecksPrevious versions contained three main bottlenecks that hindered
performance significantly (50x slower thannull_decorator):The usage of theinspect.getmodulefunctionThe usage of theinspect.getmembersfunctionThe ownership attribute loopBottleneck Deep-DiveTheinspectmodule and itsgetmoduleandgetmembersfunctions
are necessary for determining the class that has direct ownership of
the wrapped method. In previous versions, both thegetmoduleandgetmembersfunctions were used in the wrapper function declared
inside the higher order decorator.Although marginal (5%) performance benefits were found by hoisting thegetmoduleup to the scope of the decorator function, by shifting the
computation burden to declaration time as opposed to call time, thegetmembersfunction was unable to be hoisted due to the changing
state of a module at load time versus call time. Some performance was
reclaimed by the use of a lambda filtering predicate, but much was
left to be desired.Along with theinspectmodule functions, the "ownership attribute"
loop was another potential bottleneck. Since the only obvious way to
ascertain the class was through the method's fully qualified name,
accessing nested classes to reach down towards the method's direct
owner needed to be done using string attribute searches. Although the
loop is not too computationally intensive, this could be a bottleneck
for deeply nested or inherited class structures.Bottleneck SolutionThe current solution in place was to extract the declaration and
computation of these bottlenecks to a separate function in the
package's local scope, as opposed to the decorator's scope, and
enabling LRU caching from thefunctoolsmodule.For those unfamiliar, the lru_cache function, when used as a
decorator, creates a memory-optimized call-result caching solution
that exchanges the full computation requirements of a function call
for a dictionary lookup of the result.Since the penalty for a first-time call is
~4 Microseconds, the caching of the results on a per-method basis
provides a performance increase of 3,000%. This is because the
dictionary lookup has a time complexity of, on average, O(1) that
results in ~125 Nanosecond follow-up calls.AuthorBraden Toone is the sole author and maintainer of this code, and can
be contacted via email atbraden@toonetown.comLicenseThis package is licensed under the OSI Approved MIT License for free
commercial and personal use as stated in the LICENSE file.