Giter VIP home page Giter VIP logo

pyproprop's People

Contributors

brocksam avatar jackirvine97 avatar restyled-commits avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar

pyproprop's Issues

Support additional string formatting methods

Support formatting of strings to other cases such as camel and snake as well as upper, lower and title. This will mainly involve handling replacement of spaces and dealing with invalid characters. Upper cases used in abbreviations/acronyms will also need to be handled intelligently.

Examples:

some_string = "Some String with ABRV."
format_snake(some_string)
> "some_string_with_abrv"
format_camel(some_string)
> "SomeStringWithABRV"

Expose string formatting capability as stand-alone function

It would be great to have the ability to directly access pyproprop's string formatting functionality through a stand-alone function as well as through processed properties (as is currently implemented). The desired use case is outlined below:

from pyproprop import format_case
my_string = "this IS some-string__that I have etc."
print(format_case(mystring, case="hyphen"))
>>> "this-is-some-string-that-i-have-etc"

Cannot cast objects to `np.ndarray`

Problem caused when trying to cast objects to Numpy ndarrays caused by the fact that the type name is ndarray while the constructor method is typically np.array.

Error in post method

When calling a processed property with a post method, for example:

squares = processed_property(
        "squares",
        type=int,
        iterable_allowed=True,
        method=square()
)

The following is raised: NameError: name 'optional' is not defined.

This is comping from line processed_property line 537:

def apply_method(value):
"""Applies a specified method at the end of the property setter.
Parameters
----------
value : obj
Property object value for setting.
Returns
-------
obj
Property object value with post-method applied.
"""
if optional and value is None:
return None
return post_method(value)

Optional is not is in the scope of apply_method.

Can use of `exec()` and `locals()` be avoided in `processed_property`'s `cast_type()` function?

Code safety may be improved if used of exec() and locals() is avoided. See:

def cast_type(value):
"""Enforce type casting of property value to be set to specific type.
Parameters
----------
value : obj
Property object value for setting.
Returns
-------
obj
Supplied value cast to the specified type
Raises
------
ValueError
If the casting fails.
TypeError
If the casting fails.
"""
cast_str = f"processed_value = {expected_type.__name__}({value})"
try:
exec(cast_str)
except (ValueError, TypeError) as e:
name_str = generate_name_description_error_message()
msg = (f"{name_str} must be a {repr(expected_type)}, instead got "
f"a {repr(type(value))} which cannot be cast.")
raise e(msg)
return locals()['processed_value']

A utility designed to work with processed properties with options.

Processed properties allow the specification of a group of options that a user can then choose from. If an option from this group is chosen then an error is raised. Processed property options also allow for a default option to be
specified as well as for options to be specified as unsupported. Additionally dispatchers can be built using these options so that a specific function or class handle can be linked to the option identifiers. This module implements a framework to provide all of these things in a clean and easy-to-use way that is designed with use alongside processed properties in mind.

To do this currently is something like:

from pyproprop import processed_property

KEYWORD_IDENTIFIER_1 = "keyword_1"
KEYWORD_IDENTIFIER_2 = "keyword_2"
KEYWORD_IDENTIFIER_3 = "keyword_3"
OPTIONS = (KEYWORD_IDENTIFIER_1, KEYWORD_IDENTIFIER_2, KEYWORD_IDENTIFIER_3)
DEFAULT_OPTION = KEYWORD_IDENTIFIER_1
UNSUPPORTED_OPTIONS = KEYWORD_IDENTIFIER_3


class MyClassWithOptionsProperty:

    my_property = processed_property(
        "my_property",
        type=str,
        options=OPTIONS,
        default=KEYWORD_IDENTIFIER_1
        unsupported_options=KEYWORD_IDENTIFIER_3,
    )

    def __init__(self, my_property):
        self.my_property = my_property

    def my_dispatcher(self):
        return {
            KEYWORD_IDENTIFIER_1: SomeClass1,
            KEYWORD_IDENTIFIER_2: SomeClass2,
            KEYWORD_IDENTIFIER_3: SomeClass3,
        }

Ideally this would be done something like:

from pyproprop import Options, processed_property

my_options = Options(
    ["keyword_1", "keyword_2", "keyword_3"],
    unsupported=2,
    callables=[SomeClass1, SomeClass2, SomeClass3]
)

class MyClassWithOptionsProperty:

    my_property = processed_property(
        "my_property",
        type=str,
        options=OPTIONS,
    )

    def __init__(self, my_property):
        self.my_property = my_property

Default-optional use case only functions for 'castable' types

return expected_type(default)

some_property = processed_property("some_property", type=SomeType, optional=True, default=some_type_instance)

In the above use case, when some_property is set to None, pyproprop attempts to cast some_type_instance to SomeType and return the result (see referenced line in processed_property.py) . If SomeType is not 'castable' this raises an error.

Might it be more intuitive to simply return the user-supplied default value in this use case?

Improve processed properties by using dispatcher in setter

Setter method currently includes a number of chained if statements that must be traversed every single time the setter of a property is entered, even if the relevant keywords were ignored when instantiating the processed property. A dispatcher could be created when the processed property is instantiated that only dispatches calls to the required methods in the setter improving performance.

Processed properties should be able to be compared to one another

Example would be:

from pyproprop import processed_property

class SomeClass:

    some_property_min = processed_property(
        "some_property_min",
        description="some property's minimum bounded value",
        less_than="some_property_max",
    )
    some_property_max = processed_property(
        "some_property_max",
        description="some property's maximum bounded value",
        less_than="some_property_min",
    )

Uses new keyword:

  • less_than
  • greater_than
  • at_least
  • at_most
  • equals

Add ability to format iterables as `collections.namedtuple`s to support dot-indexing of attributes

Objects created that have an obvious (and valid) named that can be used as an identifier should be instantiated as collections.namedtuples so that they can be indexed using dot-indexing. Example:

obj_x = Obj(name="x")
obj_y = Obj(name="y")
obj_z = Obj(name="z")

some_iterable = [obj_x, obj_y, obj_z]
some_attribute = named_iterable(some_iterable)

assert some_attribute[0] is some_attribute.x is obj_x
assert some_attribute[1] is some_attribute.y is obj_y
assert some_attribute[2] is some_attribute.z is obj_z

Make string formatting compatible with options

I would like to be able to format a string before checking if it is one of a set of options:

prop = processed_property(
        "prop_name",
        str_format="hyphen",
        options=OPTIONS_DISPATCHER.keys(),
)

This would involve simply reordering conditionals in the processed property setter method so that the string formatting comes before option checking.

I am happy to implement, but first want to check this potential refactor is compatible with other pyproprop use cases?

Refactor string formatting test cases to use pytest-cases

Pytest-cases enables parametrisation of text fixtures and would allow (currently hardcoded) string formatting examples to be refactored into fixtures and used across multiple tests. Pytest-cases is presently not available on conda-forge, however this has been raised with maintainers: smarie/python-pytest-cases#135.

If pytest-cases is added to conda-forge the refactored tests are implemented in the repo at commit 3672345.

Float processed property raises TypeError incorrectly when iterable_allowed property is set

Steps to reproduce

Use pyproprop version 0.4.3 or 0.4.2

Example

For example see the following script:

from pyproprop import processed_property


class ExampleClass:
    error_float = processed_property(
        "error_float",
        type=float,
        iterable_allowed=True,
    )

    def __init__(self,
                 error_float):
        self.error_float = error_float

this_will_error = ExampleClass(5.1)

5.1 is a valid float, however when the script is run, the following error is raised:

Traceback (most recent call last):
  File "/Users/.../pyproproperror.py", line 15, in <module>
    this_will_error = ExampleClass(5.1)
  File "/Users/.../pyproproperror.py", line 13, in __init__
    self.error_float = error_float
  File "/Users/.../site-packages/pyproprop/processed_property.py", line 228, in prop
    value = method(value, *args, **kwargs)
  File "/Users/.../site-packages/pyproprop/processed_property.py", line 260, in check_expected_type
    value = check_type(value, expected_type, name_str, optional,
  File "/Users/.../site-packages/pyproprop/processed_property.py", line 299, in check_type
    raise TypeError(msg)
TypeError: `error_float` must be a <class 'float'>, instead got a <class 'tuple'>.

Process finished with exit code 1

Now running the same script again, but with iterable_allowed=False, the script runs without issue.

Bug in error message capitalisation splitting

Traceback (most recent call last):
  File "match_sprint_sim.py", line 26, in <module>
    standing_lap_forward_simulation = standing_lap_event.initialise_forward_simulation()
  File "/home/peter/live/lapysim/lapysim/event.py", line 594, in initialise_forward_simulation
    return self.event_phases[0]._initialise_event_phase_forward_simulation()
  File "/home/peter/live/lapysim/lapysim/event.py", line 232, in _initialise_event_phase_forward_simulation
    self._initialise_athletes()
  File "/home/peter/live/lapysim/lapysim/event.py", line 248, in _initialise_athletes
    athlete._initialise_dynamics(self)
  File "/home/peter/live/lapysim/lapysim/athlete.py", line 503, in _initialise_dynamics
    rolling_resistance_model = event.settings._instantiate_rolling_model(self.dynamics)
  File "/home/peter/live/lapysim/lapysim/settings.py", line 54, in _instantiate_rolling_model
    dynamics_model.associated_rr_model = self.rolling_resistance_model
  File "/home/peter/anaconda3/envs/lapysim/lib/python3.7/site-packages/pyproprop/processed_property.py", line 85, in prop
    check_options(value)
  File "/home/peter/anaconda3/envs/lapysim/lib/python3.7/site-packages/pyproprop/processed_property.py", line 196, in check_options
    with_preposition=True)
  File "/home/peter/anaconda3/envs/lapysim/lib/python3.7/site-packages/pyproprop/processed_property.py", line 437, in format_for_output
    return format_multiple_items_for_output(items, *args, **kwargs)
  File "/home/peter/anaconda3/envs/lapysim/lib/python3.7/site-packages/pyproprop/processed_property.py", line 469, in format_multiple_items_for_output
    first_word, _ = formatted_items.split(maxsplit=1)
ValueError: not enough values to unpack (expected 2, got 1)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.