Metadata-Version: 2.4
Name: python_sharp
Version: 1.0.1
Summary: python# (python sharp) is a module created to add EOP (event oriented programing) into python in the most native feeling, easy sintax way possible. Based on C# event implementation structure
Home-page: https://github.com/juanclopgar97/python_sharp.git
Author: Juan Carlos Lopez Garcia
Author-email: juanclopgar@gmail.com
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.6
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license-file
Dynamic: requires-python
Dynamic: summary

<!--img src="https://github.com/juanclopgar97/python_sharp/blob/master/documentation_images/python_sharp.png" width="300"-->
<img src="./documentation_images/python_sharp.png" width="300">

```python
    def project_finished(sender:object,e:EventArgs)->None:
      print("Develop is fun!!!")

    project = Project() 
    project.Finished += project_finished
    project.Finish()

```
```
"Develop is fun!!!"
```

# Python# (Python sharp)

## Table of Contents
1. [Introduction](#Introduction)
2. [Installation](#Installation)
3. [Use cases and examples](#Use-cases-and-examples)
    1. [Delegates](#Delegates)
        1. [How to add callables into a Delegate](#How-to-add-callables-into-a-Delegate)
        2. [How to get returned values of callables out of a Delegate](#How-to-get-returned-values-of-callables-out-of-a-Delegate)
        3. [Delegates Summary](#Delegates-Summary)
    2. [Events](#Events)
        1. [EventArgs, CustomEventArgs and CancellableEventArgs class](#eventargs-customeventargs-and-cancellableeventargs-class)
        2. [Implementation](#Implementation)
            1. [Simple events](#Simple-events)
            2. [Events with arguments](#Events-with-arguments)
            3. [Events with modifiable arguments](#Events-with-modifiable-arguments)
    3. [Static events](#Static-events)

## Introduction

python# (python sharp) module was created with the intention of adding EOP (event oriented programing) into python in the most native feeling, easy sintax way possible.

EOP is a programming paradigm that allows execute actions (code) based on "doings" or events, this is really usefull when you have to execute specific actions when something happens but you do not have the certainty when or how many times is going to happen.

This module was thought to accomplish EOP with 2 objetives in mind:

1. Features should look and feel like a native python feature.
2. Implementation should be based in another famous EOP language to decrease learning curve and improve user experience.

Events are just another possible way to declare a class member like: fields/attributes, properties and methods, python already have a way to define a property with **@property**, this helps to define objective number 1, for this reason events are implemented with **@event** sintax to be consistent with python:

```python #5
class Person:
  def __init__(self,name:str)->None:
    self._name = name

  @property
  def Name(self)->str: 
        return self._name

  @Name.setter 
  def Name(self,value:str)->None:
        self._name = value

  @event
  def NameChanged(self,value):
    #some implementation
    pass
```

For objective 2, the module was architected thinking in how another EOP language (in this case C#) implements its events. This implementation will be explain below, keep in mind this is a really simplified explanation of how C# events actually work, if you are interested in learn how they work exactly please go to C# documentation. With this clarified, let's move on to the explanation: 

1. C# implements events as a collection of callbacks that will be executed in some point of time, this collection of functions are called **Delegates**, invoking(executing) the delegate will cause the execution of all functions(callables) in its collection.

2. delegates are not publicly expose, commonly due security reasons, as the fields/attributes have to be encapsulated, delegates as well, and the way to encapsulate them is with events. Fields/attributes are to properties as delegates are to events.

3. Properties encapsulate fields/attributes with 2 functions/methods called "get" and "set", which define the logic of how data should be GET and SET out of the object, in C# events encapsulate delegates with 2 functions as well called "add" and "remove", which define the logic of how functions/subscribers should be added or removed out of the delegate.


## Installation

### Requirements

- **Python**: Version 3.6 or higher
- **pip**: Python package manager

To install `python_sharp` you can follow either of the options listed:

### Disclaimer

version 1.0.0 is only available through GitHub Pypi does not contain that version.

### 1. Clone the Repository 
If you want to explore the source code, you can clone the repository:
```bash
git clone https://github.com/juanclopgar97/python_sharp.git
cd python_sharp
```

### 2. Install the package directly from GitHub using pip:

```bash
pip install git+https://github.com/juanclopgar97/python_sharp.git
```

from a specific branch/commit/version:

```bash
pip install git+https://github.com/juanclopgar97/python_sharp.git@<branch_or_commit_or_version>
```

Example:

```bash
pip install git+https://github.com/juanclopgar97/python_sharp.git@v1.0.0
```

### 3. Install from Pypi

```bash
pip install python_sharp
```
or select your version

```bash
pip install python_sharp==<version>
```

Example:

```bash
pip install python_sharp==1.0.1
```
Upgrade it:

```bash
pip install python-sharp --upgrade
```


### Usage

```python
from python_sharp import *

#your code
```

## Use cases and examples:

In this repository there are 2 main files "python_sharp.py" (which is the module file) and "test.py". This last file contains all the features applied into one single script, this could be really usefull if you want to do a quick check about how something is implemented, however, due it is a "testing" script and not a "walk through" it could be confusing if you do not know what is going on, so it is **Highly recommended** read the below documentation which explains step by step how to implement every single feature in the module.

### Delegates

Python sharp Delegates are a list of callables with the same signature, when a delegate is being executed (delegates are callable objects), it executes every single callable in its list.

#### How to add callables into a Delegate
It is really important to keep the callables added into the delegate with consistent signatures due parameters passed to the delegate when is being executed are the same ones passed to every single callable in the collection, so if one callable signature is expecting only 2 parametters and the next callable 3 parametters this is going to cause a TypeError that might look like this: 

```python
from python_sharp import *

def function1(parameter1:int): #defining a function with 1 parameter (int type)
  print("function1")

def function2(parameter1:int,parameter2:str): #defining a function with 2 parametrs (int,str types)
  print("function2")

delegate = Delegate() #creating a Delegate
delegate += function1 #adding function1
delegate += function2 #adding function2

delegate(5) # executing the delegate with only 1 parameter

```

OUTPUT:
```
function1
Traceback (most recent call last):
  File "c:\PATH\test.py", line 341, in <module>
    delegate(5) # executing the delegate with only 1 parameter
    ^^^^^^^^^^^
  File "c:\PATH\python_sharp.py", line 72, in __call__
    results.append(callable( *args, **kwds))
                   ^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: function2() missing 1 required positional argument: 'parameter2'
```

Here *function1* was executed correctly due the signature of the function match with how the delegate was executed (passing only one integer "5"), and *function2* was expecting a second string parameter resulting in a TypeError. So, it is really important keep signatures in a homogeneous manner.

#### How to get returned values of callables out of a Delegate

Once the delegate is executed you can get the returned values (if Any) as a tuple returned by the delegate, this tuple represents the values returned by every callable in the delegate's callable collection:

```python
from python_sharp import *

def function(text:str):
  print("%s, Function is being executed!" % text)
  return "function result"

class Test:
  def method(self,text:str):
    print("%s, Method is being executed!" % text)
    return "method result"

test_instance = Test()

first_delegate = Delegate(function) #adding a function. You can pass the first callble optionally through the constructor

delegate = Delegate() # creates an empty delegate
delegate += first_delegate #adding a delegate. You can add a delegate to another delegate due a Delegate is callable
delegate += test_instance.method #adding a method

results = delegate("Hello!")

print(f"returned values: {results}")
```

OUTPUT:
```
Hello!, Function is being executed!
Hello!, Method is being executed!
returned values: (('function result'), 'method result')
```
In this example we can see that *delegate* executes its first item added which is *first_delegate*, as result 'function' is executed and *first_delegate* return a tuple with the return value of 'function', this tuple is added into *delegate* results, then *delegate* executes its next item *test_instance.method* as result it returns a string that is going to be added into the *delegate* results.

At the end we finish with all callables executed and the results: 
  - ('function result'): result of *first_delegate* execution
  - 'method result': result of *test_instance.method* execution.

#### Delegates Summary

As summary, Delegates are really usefull to execute a bulk of callables, and its return values (if any) are returned by the delegate in a tuple.

### Events

In programming, an event refers to an action or occurrence that a program can detect and respond to. Events can be triggered by user interactions (like clicking a button, typing text, or moving a mouse), system-generated activities (like a file being updated or a timer expiring), or even messages from other parts of the program. Typically, an event is associated with subscribers (event listeners) which are functions or blocks of code designed to execute when the specific event occurs.

Events are commonly part of an event-driven programming paradigm, where the flow of the program is determined by these events.

Events can be implemented as members of an instance or a class (static events) on different ways, in this module we can group this "ways" into 3 main implementations:

1. **Simple events** (Normally implemented as *property changed* events):
  These events only "notify" that something relevant happens, they do not provide extra information about the event like: How, When, Why etc.
  
  Name connvention for this events is VERB + 'ed' (past simple):

  - NameChanged (this is an example of property change implementation in this case for Name property)
  - LocationChanged (this is an example of property change implementation in this case for Location property)
  - Moved
  - Executed

2. **Events with arguments**:
  This events are like *simple events* but they are capable of provide extra information about the event like: How, When, Why etc, to the subscribers through a parameter. They follow the same name convention as *simple events*.

3. **Events with modifiable arguments** (Normally implemented as *pre-events*)

  *Events with modifiable arguments* are most likely implemented as **pre-events** this means the event advertise something that is about to happen, and it will let the subscribers provide information to determine the future, like cancelling what was about to happen or modify how it was going to be done.

  Name convention for this events is VERB + 'ing' (present continous)

  - An example to clarify this could be an event called "WindowClosing", this event will notify that a window is about to close, the subscribers will have the power to pass information through the event arguments to cancel the action, this is really useful if the changes in the app are not saved.

#### EventArgs, CustomEventArgs and CancellableEventArgs class

*EventArgs* class is an empty class designed to be a base class to pass the event arguments, these arguments are going to be passed from the publisher to the subscriber in order to provide more information about what happens.

-  **Simple events** use *EventArgs* objects to pass the event arguments to the subscriber, due *EventArgs* is an empty class, no arguments are passed to the subscriber, this is the reason why these events are the simplest to implement and the ones used for *property changed* events, they only notify something happens and that's it, no more information. Worth mentioning *property changed* events are not the only use for these event types, it is just a use case example

-  **Events with arguments** use a custom class that inherit from *EventArgs* class to describe what arguments are going to be passed to the subscriber. The arguments passed to the subscriber are passed as read_only properties (properties with only getter). If a **simple event** is not enough, you might need an **Event with arguments**, in this case, you can use a custom EventArgs that contains your arguments.

    As a use case example imagine an event called *Moved*, this event notifies when the object moves, but maybe only notify the movement is not enough and we want to inform how much the object moves, this is a perfect use for our custom *EventArgs* class:


    ```python
    class MovedEventArgs(EventArgs): # example of Custom EventArgs to pass event information (distance moved in this case)

        _delta:int

        def __init__(self,delta:int)->None: # Request the distance of the movement
            super().__init__()
            self._delta = delta # Save the distance

        @property
        def Delta(self)->int: #encapsulate the value and placing its getter
            return self._delta
    ```

- **Events with modifiable arguments** use a custom class that inherit from *EventArgs* class to describe what arguments are going to be passed from the subscriber to the publisher, this module already include one example of this aproach *CancellableEventargs*:

    ```python
    
    class CancellableEventArgs(EventArgs):
    
        _cancel:bool
    
        def __init__(self)->None:
            super().__init__()
            self._cancel = False 
    
        
        @property
        def Cancel(self)->bool: #to show the value of _cancel attribute
            return self._cancel
        
        @Cancel.setter
        def Cancel(self,value:bool)->None: #to let the subscriber set a value into _cancel
            self._cancel = value
    ```

    as you can see, this implementation is really similar to **Events with arguments**, the only difference is we are placing a setter method to let modify the cancel value, this value can be used for the publisher at the end of the exectution of all the callbacks stored.

#### Implementation

Below this text, the use cases and explanation about the events are shown, please read the examples and after READ THE EXPLANATION OF THE EXAMPLE CODE, this is really important because it specifies step by step the "WHY"s of the implementation.

##### Simple events

  ```python
    from python_sharp import *

    class Person: 
    
      def __init__(self, name:str)->None: 
        self._name = name 
        self._namechanged_callbacks = Delegate() 

      @property 
      def Name(self)->str:
        return self._name

      @Name.setter 
      def Name(self,value:str)->None:
        self._name = value
        self._OnNameChanged(EventArgs()) 

      def _OnNameChanged(self,e:EventArgs)->None:
        self._namechanged_callbacks(self,e) 

      @event 
      def NameChanged(self,value)->None:
        self._namechanged_callbacks += value

      @NameChanged.remove
      def NameChanged(self,value)->None:
        self._namechanged_callbacks -= value 


    def person_NameChanged(sender:object,e:EventArgs)->None:
      print("person change its name to %s" % sender.Name)

    person = Person("Juan")
    person.NameChanged += person_NameChanged 
    person.Name = "Carlos" 
    person.NameChanged -= person_NameChanged 
    person.Name = "Something" 
  ```

  OUTPUT
  ```
  person change its name to Carlos
  ```

"simple events"" notify" that something relevant happens, they do not provide extra information about the event like.

On this example an event *NameChanged* is implemented to notify when the person's name change.

To implement a *simple event* the first thing you have to do is create a variable to store the subscribers, look at this variable as a "To do list" due it contains the callables that are going to be executed at some specific time.

```python
self._namechanged_callbacks = Delegate() # it can be viewed as a "To do list"
```

As you might notice the variable that is going to store the subscribers is a Delegate and the name starts with '_' to "protect" the attribute. Expose the attribute "publicly" is not a good practice, due other part of the code can manipulate the attribute wrongly or get/set information in a way that was not mean to. To fix this, we can define 2 methods to encapsulate the delegate (add/remove methods), Through these 2 methods the other objects in the code can subscribe/unsubscribe (add/remove) callables to our delegate.

```python
      @event 
      def NameChanged(self,value)->None:
        self._namechanged_callbacks += value # add the new callable to the attribute with a delegate

      @NameChanged.remove
      def NameChanged(self,value)->None:
        self._namechanged_callbacks -= value # remove the callable to the attribute with a delegate
```

Code above implements add/remove logic to the delegate. Function below *@event* decorator defines the logic for the *add* or how a callable should be added to our "To do list". Function below *@NameChanged.remove* defines the logic for the *remove* or how a callable should be removed from the delegate

Notice the functions HAVE to be named exactly with the same name, and if an *@event* is defined you **must** implement *@IDENTIFIER.remove* or the code will throw a traceback, this is to protect the integrity of the code and provide instructions about how to add AND remove a callable.

The callable to be added/removed will be passed through the "value" parameter. Notice in this example "value" parameter doesn't have any type annotation, this is only to keep this first example "simple/readable" at first sight, however is HIGHLY RECOMMENDED annotate the type as the following examples (Events with arguments or Events with modifiable arguments), due this is the way to indicate clearly what is the signature expected from the event to their subcribers (callables).

Once this is in place, we have:

- A place to store the callables 
- Logic to let to other parts of the code add/remove callables

Now we need to execute the callables in the right momment, in this case the event is called "NameChanged" so the callables should be executed when the name changes, this means our extra logic needs to be added in the Name setter due that is the part of the code that has this responsability (change the person's name).

```python
      @Name.setter 
      def Name(self,value:str)->None:
        self._name = value
        # execute our "To do list" or delegate
```

In the snippet code above the comment defines where the "To do list" needs to be executed, however, sometimes the own object needs to implement its own logic when (in this case) the property Name change, for this purpose is HIGHLY RECOMMENDED as a good practice define another function/method called "_On[EVENT NAME]"

```python
      @Name.setter 
      def Name(self,value:str)->None:
        self._name = value
        self._OnNameChanged()

      def _OnNameChanged(self)->None:
        #logic when the name change (if any)
        self._namechanged_callbacks() #external logic
```
Inside of this method the own internal and external logic when the name change must be implemented, in other words, *What as a Person I need to do when my name changes?* (own/internal logic), and after, attend external logic (To do list) in other words instructionss provided by other objects or parts of the code. *What others needs to do when my name changes?*

In this case the class Person doesn't need to do "something" when the name changes (internal logic), so we only need to execute the external logic (execute the delegate)


Now we have a way to add/remove subscribers and trigger the event, however, you might notice the code above is not exactly the same as the example code, this is because despite the event is now implemented and working is not following a good practice CONVENTION. So even with a working code, is HIGHLY RECOMMENDED follow next convention:

```python
      @Name.setter 
      def Name(self,value:str)->None:
        self._name = value
        self._OnNameChanged(EventArgs())

      def _OnNameChanged(self,e:EventArgs)->None:
        #internal logic if any
        self._namechanged_callbacks(self,e)
```

You can notice 2 things

1. *_OnNameChanged* now requires a parametter called 'e' which is an EventArgs, this is a safety implementation, every "_On[EVENT NAME]" must require an EventArgs (or any other class that inherits from it), this is a way to say "Are you sure the event happens? show me the evidence!", in this case there is no arguments so the evidence is an empty EventArgs object. EventArgs object is used first for the internal logic and then passed to the external logic as a parameter.

2. 'self' is passed to the external logic as first parameter, this is to allow the subcribers know 'Who is executing my piece of code"



**As summary:** 

- There are 2 main sections to implement when you want to define an event: 

    1. Part that store and define how to add/remove callables
    2. Part that executes/trigger those callables stored

- There are conventions about how the logic must be implemented to facilitate reading and maintenance of the code.

- Callables to be subscribed to a simple event should follow the next signature:

    *Callable[[object, EventArgs], None]* (a callable with 2 parameters, first one contains the publisher and second the event arguments, the function must return None)

The next snipped code shows and example of how the *simple events* should be implemented with the recomended annotation: 

```python
      @event 
      def NameChanged(self,value:Callable[[object, EventArgs], None])->None:
        self._namechanged_callbacks += value

      @NameChanged.remove
      def NameChanged(self,value:Callable[[object, EventArgs], None])->None:
        self._namechanged_callbacks -= value 
```

This is done with the intention of clarify what is the event expecting from its subscribers signature.


To use the event:

```python
    def person_NameChanged(sender:object,e:EventArgs)->None: #function to be executed when the name changes (subscriber)
      print("person change its name to %s" % sender.Name)

    person = Person("Juan")  #creates a person
    person.NameChanged += person_NameChanged # we add 'person_NameChanged' (subcriber) to event NameChanged of 'person', this line will execute function under @event decorator (add function)
    person.Name = "Carlos" # change the name to trigger the event (this will execute 'person_NameChanged') 
    person.NameChanged -= person_NameChanged #unsubcribe the function, this line will execute function under @NameChanged.remove decorator (remove function)
    person.Name = "Something" # change the name again to prove 'person_NameChanged' is not executed anymore
```


##### Events with arguments

  ```python
    from python_sharp import *
    from typing import Callable

    class MovedEventArgs(EventArgs):

        _delta:int

        def __init__(self,delta:int)->None:
            super().__init__()
            self._delta = delta

        @property
        def Delta(self)->int:
            return self._delta

    class Person:
    
        def __init__(self)->None:
            self._location = 0
            self._movedcallbacks = Delegate()

        @property
        def Location(self)->int:
            return self._location

        @Location.setter
        def Location(self,value:int)->None:      
            previous = self.Location 
            self._location = value
            self._OnMoved(MovedEventArgs(self.Location - previous))

        def Move(self,distance:int)->None:
            self.Location += distance

        def _OnMoved(self,e:MovedEventArgs)->None:
            self._movedcallbacks(self,e)

        @event 
        def Moved(self,value:Callable[[object, MovedEventArgs], None])->None:
            self._movedcallbacks += value

        @Moved.remove
        def Moved(self,value:Callable[[object, MovedEventArgs], None])->None:
           self._movedcallbacks -= value  


    def person_Moved(sender:object,e:MovedEventArgs)->None:
      print("Person moves %d units" % e.Delta)

    person = Person()
    person.Move(15)
    person.Moved += person_moved
    person.Location = 25
    person.Moved -= person_moved
    person.Location = 0
  ```

  OUTPUT
  ```
  Person moves 10 units
  ```

*Events with arguments* are almost the same as *simple events* so, the next explanation will only address the differences between the 2 cases.

On this example an event named "Moved" is implemented to notify when a person moves and provide how much does the person move.

```python
    class MovedEventArgs(EventArgs):

        _delta:int

        def __init__(self,delta:int)->None:
            super().__init__()
            self._delta = delta

        @property
        def Delta(self)->int:
            return self._delta
```

In this case a custom EventArgs is created in order to be capable of store the event arguments, on this example the event is named "Moved", and is going to be triggered when the person changes its location, in addition, it will provide HOW MUCH the person moves, this is the job of the *MovedEventArgs* and the main difference with a *simple event*.

In the next code block we can see how the event is being defined:

```python
        @event 
        def Moved(self,value:Callable[[object, MovedEventArgs], None])->None:
            self._movedcallbacks += value

        @Moved.remove
        def Moved(self,value:Callable[[object, MovedEventArgs], None])->None:
           self._movedcallbacks -= value  
```

in this case the only difference is the 'value' parameter annotation,  this indicates that the event requieres a *Callable[[object, MovedEventArgs], None]* subscriber signature, in other words a *MovedEventArgs* will be provided to the subscriber.

It is HIGHLY IMPORTANT to realize *Moved* event signature is *Callable[[object, MovedEventArgs], None]* therefore it can accept subscribers with the next signatures:

- *Callable[[object, MovedEventArgs], None]*
- *Callable[[object, EventArgs], None]*

This 2 signatures are ok due polimorfism, it can be confusing due at first sight seems like we are asigning an *EventArgs* to a *MovedEventArgs* (*MovedEventArgs* <- *EventArgs*), this case is not valid, due it might throw a Traceback if a *MovedEventArgs* member is trying to be acceses into a *EventArgs* object. However in this example case is not the same,
the subscriber with *Callable[[object, EventArgs], None]* signature defines how the paramater object is going to be treated by the callable, in this case, the parameter will be used/treated as an *EventArgs*, and the event will provide a *MovedEventArgs* object to the callable so in reallity we are asigning a *MovedEventArgs* object to an *EventArgs* variable (*EventArgs* <- *MovedEventArgs*) wich by polimorfism will not cause any issue trying to access any of the *EventArgs* members.

Next diagram explains a general case for what was explained above (event subscriber signatures accepted by an event):

<img src="./documentation_images/event_assignament_example.png" width="100%">


And the last difference but not less important is how the event is going to be trigerred:

```python
        @Location.setter
        def Location(self,value:int)->None:      
            previous = self.Location 
            self._location = value
            self._OnMoved(MovedEventArgs(self.Location - previous))

        def _OnMoved(self,e:MovedEventArgs)->None:
            self._movedcallbacks(self,e)
```

We can see in the code block above now *_OnMoved* method now requires a *MovedEventArgs*, as *simple events* did, this is for security reasons, if we are going to execute *_OnMoved* method because the event happens, that is a way to say "prove it or show the evidence!".

Second difference is when *Location* settter is calling *_OnMoved* method, now it needs to create an instance of *MovedEventArgs* and to do so, it requieres a quantity to be passed to the constructor, this quantity of "how much the person moves" can be calculated with a substraction of previous and current location.


**As summary:** 

*Events with arguments* and *simple arguments* are really similar and there are only some differences:

- Needs a custom *EventArgs* class defined.
- Events with custom *EventArgs* can accept different subscriber signatures
- _On[Event name] method uses the custom *EventArgs* class and this causes an extra security layer
- Trigger code now needs to create an instance of the new custom *EventArgs* and to do so, it needs to provide/calculate the arguments needed by the custom *EventArgs* constructor


##### Events with modifiable arguments 

  ```python
    from python_sharp import *
    from typing import Callable

    class LocationChangingEventArgs(CancellableEventArgs):

        _value:int

        def __init__(self,value:int)->None:
            super().__init__()
            self._value = value

        @property
        def Value(self)->int:
            return self._value

    class Person:
    
      def __init__(self)->None:
        self._location = 0
        self._locationChangingcallbacks = Delegate()

        @property
        def Location(self)->int:
            return self._location

        @Location.setter
        def Location(self,value:int)->None:

            locationEventArgs = LocationChangingEventArgs(value)
            self._OnLocationChanging(locationEventArgs)

            if(not locationEventArgs.Cancel):
                self._location = value


        def _OnLocationChanging(self,e:LocationChangingEventArgs)->None:
            self._locationChangingcallbacks(self,e)


        @event
        def LocationChanging(self,value:Callable[[object, LocationChangingEventArgs], None])->None:
            self._locationChangingcallbacks += value

        @LocationChanging.remove
        def LocationChanging(self,value:Callable[[object, LocationChangingEventArgs], None])->None:
           self._locationChangingcallbacks -= value


    def person_LocationChanging(sender:object,e:LocationChangingEventArgs):
      if e.Value > 100:
        e.Cancel = True

    person = Person()
    person.Location = 50
    person.LocationChanging += person_LocationChanging
    person.Location = 150
    print(person.Location)

  ```

 OUTPUT
 ```python
 50
 ```

*Events with modifiable arguments* are really similar to *Events with arguments* this explanation will address only the differences, so if you have doubts about the implementation go back to that section. 

*Events with modifiable arguments* are most likely implemented as **pre-events** this means the event advertise something that is about to happen, and it will let the subscribers provide information (Thorugh a custom *EventArgs*) to determine the future, like cancelling what was about to happen or modify how it was going to be done.

On this example an event named "LocationChanging" is implemented to notify when the person's location is about to be changed, this will let the subscribers cancel or modify the future behaviour of that action.

Key difference is the way the custom *EventArgs* is defined:

```python
class CancellableEventArgs(EventArgs): #Defined already on python_sharp module
    _cancel:bool

    def __init__(self)->None:
        super().__init__()
        self._cancel = False 

    
    @property
    def Cancel(self)->bool:
        return self._cancel
    
    @Cancel.setter
    def Cancel(self,value:bool)->None:
        self._cancel = value

class LocationChangingEventArgs(CancellableEventArgs):

        _value:int

        def __init__(self,value:int)->None:
            super().__init__()
            self._value = value

        @property
        def Value(self)->int:
            return self._value
```

As you can see our custom *EventArgs* is *LocationChangingEventArgs* this class inherits from *CancellableEventArgs*, an *IMPORTANT* remark is Inherit from *CancellableEventArgs* is not necessary to create an *Event with modifiable argument*. *CancellableEventArgs* is just a built in custom *EventArgs* used for *Event with modifiable argument*, the fact *LocationChangingEventArgs* inherits from it is just to show case a use of it.

Key factor to know when an event is an *Event with modifiable argument* is if the ***EventArgs* class class contains a property with a setter**.

*LocationChangingEventArgs* does not contain a property with a setter by itself, but due it inherits from an *EventArgs* which contains it (*CancellableEventArgs*), we can consider that *LocationChangingEventArgs* actually contains a property with a setter.

For this particular example *Cancel* property is set to *False* by default, when the  *EventArgs* object is passed to the subscribers now they have the ability to change *Cancel* value property:

```python
    def person_LocationChanging(sender:object,e:LocationChangingEventArgs):
      if e.Value > 100:
        e.Cancel = True
```

In the code block above is shown how the subscriber uses *e.Value* to determine if *e.Cancel* is going to be set to *True*, subsequently the publisher can use this value to modify some behaviour:

```python
        @Location.setter
        def Location(self,value:int)->None:

            locationEventArgs = LocationChangingEventArgs(value)
            self._OnLocationChanging(locationEventArgs)

            if(not locationEventArgs.Cancel):
                self._location = value
```

Code above shows how the *LocationChangingEventArgs* is created and stored in *locationEventArgs* variable in order to keep a reference to the object, once that is done, the *LocationChangingEventArgs* object is send to *_OnLocationChanging* method to execute internal and external logic (external logic will execute all subscribers that might change *Cancel* property value), and at the end of the  *_OnLocationChanging* execution we can check the *locationEventArgs* variable to evaluate if the *LocationChangingEventArgs* object *Cancel* property is *True* or *False*, with this value we can alter the code behaviour. For this particular example *Cancel* property is being use to determine if the person should change its location or not


### Static events

Static events are almost the same as the events described previoulsy, they can be implemented as well as "simple events", "with arguments" or "with modifiable arguments", key difference is the event is applied as a class event, no an instance event.

For this section the example provided is a *simple static event* due it is the simplest way to show the differences, in case you want implement a *static event with arguments* go back to [Events with arguments](#Events-with-arguments) section and apply it to the class instead of the instance as the example of *simple static event* shows.

Imagine a class that provides the number of instances that it creates, this variable should be defined as an *static variable*, due there is no necessity for every single class instance to contain the same exactly number, and even worse, if the number changes it needs to be updated on every single instance created , that is the reason why this variable should be implemented as *static variable*.

Now imagine we want to notify when an instance is created, in other words when the *static variable* changes its value, as this event is going to notify something is going on with a static variable, we need a static event:

```python
from python_sharp import *

class Person:
    _instance_created:int = 0
    _personCreatedcallbacks:Delegate = Delegate()

    def __init__(self)->None:
        Person._OnPersonCreated(EventArgs())

    @staticmethod
    def get_InstanceCreated()->int:
        return Person._instance_created
    
    @staticmethod
    def _set_InstanceCreated(value:int)->None:
        Person._instance_created = value


    @staticmethod
    def _OnPersonCreated(e:EventArgs)->None:
        Person._set_InstanceCreated(Person.get_InstanceCreated() + 1)
        Person._personCreatedcallbacks(None,e)
        
    @staticevent
    def PersonCreated(value:Callable[[object, EventArgs], None])->None:
        Person._personCreatedcallbacks += value

    @PersonCreated.remove
    def PersonCreated(value:Callable[[object, EventArgs], None])->None:
        Person._personCreatedcallbacks -= value
```

As you can see a *simple event* implementations is almost identical to *simple static event*

Key differences:

- All members used (variable, methods and event) are static (use of @staticevent instead of @event)
- Get/Set methods to encapsulate the static variable are implemented as static methods due a lack of static properties python implementation

And that is it, those are all difference, so if you have questions about how this code works, is HIGHLY RECOMMENDED go back to [Events](#Events) section.
