Repository: evennia/evennia

Issue:
[BUG] tickerhandler.clear() not functioning correctly.
#### Describe the bug
I ran a script to clean up some lingering tickers that I created without corresponding code to get rid of them when done. The active elements were:

```python
tickerhandler.clear(interval=900)
tickerhandler.clear(interval=1800)
```
After running, any command that called tickerhandler created an error similar to this (apologies for some typos, had to use a text converter from a photo).

```python
Traceback (most recent call last): 
File "c:\users\bradm\muddev\evennia\evennia\commands\cmdhandler.py", line 644, in _run_command ret = cmd. func() 

File ".\commands combat_commands.py", line 464, in func combat = rules_combat.create_combat(caller, target) 

File "C:\Users\bradm\muddev\echoes\world\rules_combat.py", line 25, in create_combat combat = create_object("commands.combat_commands.Combat", key=("combat_handler_%S" % attacker.location.db. vnum)) 

File "c:\users\bradm\muddev\evennia\evennia\utils\create.py", line 171, in create_object new_object. save() 

File "c:\users\bradm\muddev\evennia\evennia\utils\idmapper\models.py", line 442, in save super().save(*args, **kwargs) 

File "C:\Users\bradm\muddev\evenv\Lib\site-packages\django\db\models\base.py", line 740, in save force_update=force_update, update_fields=update_fields) 

File "C:\Users\bradm\muddev\evenv\Lib\site-packages\django\db\models\base.py", line 789, in save_base update_fields=update_fields, raw=raw, using=using, 

File "C:\Users\bradm\muddev\evenv\Lib\site-packages\django\dispatch\dispatcher.py", line 182, in send for receiver in self._live_receivers ( sender) 

File "C:\Users\bradm\muddev\evenv\Lib\site-packages\django\dispatch\dispatcher.py", line 182, in <listcomp> for receiver in self._live_receivers ( sender) 

File "c:\users\bradm\muddevlevennialevennia\typeclasses\models.py", line 70, in call_at_first_save instance.at_first_save(). 

File "c:\users\bradm\muddevlevennia\evennia\objects\objects.py", line 1141, in at_first_save self.at_object_creation() 

File ".\commands\combat_commands.py", line 22, in at_object_creation 
tickerhandler.add(2, self.at_repeat) 

File "c:\users\bradm\muddev\evennialevennia\scripts\tickerhandler.py", line 533, in add self.save(). 

File "c:\users\bradm\muddev\evennia\evennia\scripts\tickerhandler.py", line 416, in save for store_key, (args, kwargs) in self.ticker_storage. items (). 

File "c:\users\bradm\muddev\evennialevennia\scripts\tickerhandler.py", line 416, in <dictcomp> for store_key, (args, kwargs) in self.ticker_storage. items () 

ValueError: too many values to unpack (expected 2) 
An untrapped error occurred. 
(Traceback was logged 22-04-08 08:39:23-04). 
```
#### To Reproduce

Below is a best guess, since I haven't been able to ELIMINATE the behavior yet. ;)

Steps to reproduce the behavior:
1. Have existing tickers of interval 900 (approx. 10)
2. Have one existing ticker of interval 1800
3. Run a script containing the above lines as a part of at_script_creation
4. See error

#### Expected behavior

I expected it to simply eliminate all of the 900 and 1800 duration tickers.

#### Environment, Evennia version, OS etc
I am in 0.9.5, running in Windows. Will have to check version, as it is a new laptop.

#### Additional context
I'd suggest ideas if I had any, but this is WAY above my Python level of understanding.



Discussion:
Here's the signature for the `clear` method:
https://github.com/evennia/evennia/blob/f093c8bcb1321a44e33003cf91cf9f565e028de7/evennia/scripts/tickerhandler.py#L573-L578

Here's what you're feeding it:
```
tickerhandler.clear(interval=900)
```

So off the bat, I see a syntax error here. You're providing a kwarg when it expects an arg.

Try `tickerhandler.clear(900)`

> but this is WAY above my Python level of understanding

This arg/kwarg dichotomy is (IMO) the most irritating aspect of Python. You provided a kwarg that looks and acts like the arg it expects, but for non-obvious reasons (lack of `*args` and `**kwargs`) the concept of duck-typing is abandoned in this case.
Silly of me. I've been doing a ton of stuff with kwargs lately, so I can see how I goofed it up, especially as the definition makes it look exactly like a kwarg if you are a rookie like me. Any suggestions on how to fix the error-filled mess that I'm now getting?

Thanks for the help! 
Strikeaco is incorrect; the syntax is fine. 

Calling `tickerhandler.clear(interval=900)` is perfectly acceptable. You don't HAVE to, but you certainly can. You can do this with any python method. Arguments can be positional and/or keyworded.

**kwargs is different from a straight keyword argument. It takes a dictionary and unpacks it into the format `key=value`. 

As far as a python function goes, these are all the same call:

`tickerhandler.clear(900)`
`tickerhandler.clear(interval=900)`
`foo = {interval: 900}
tickerhandler.clear(**foo)`

I don't think you can use **kwargs in this particular case since the method doesn't have **kwargs, I'm just demonstrating how arguments work. You can ALWAYS call a method with a straight keyword argument (`method(keyword=value)`) though.
> As far as a python function goes, these are all the same call:
> 
> `tickerhandler.clear(900)` `tickerhandler.clear(interval=900)` `foo = {interval: 900} tickerhandler.clear(**foo)`

Ouch, you're right-- but only when `*args` is not specified. 

```
def stuff(thing='default'):
    return thing

a = stuff(thing='foo')
b = stuff('foo')

# works
assert a == b

def stuff(*args, thing='default', **kwargs):
    return thing

a = stuff(thing='foo')
b = stuff('foo')

# will fail
assert a == b

```

Sorry, I should have mentioned on this one that the script successfully deleted all the 900 interval timers, but NOT the one 1800 interval timer.
One time Griatch told me that, while you can position your kwargs, you should always explicitly state the kwarg you are assigning.

Your code should look like this:
`method(arg1, arg2, kwarg1=data1, kwarg2=data2, kwarg3=data3)`

Not like this:
`method(arg1, arg2, data1, data2, data3)`

@Tegiminis is correct, the `tickerhandler.clear()` method has a **very obvious kwarg** and no argument. A normal argument is REQUIRED, whereas a keyword argument is optional. Keyword arguments always have this assignment format of `kwarg=variable`.

`tickerhandler.clear()` should **always** be called as `tickerhandler.clear(interval=variable)`, otherwise the interval will default to `None` and the `self.ticker_storage` will be set to an empty dictionary.

The docstring on `tickerhandler.clear()` method needs corrected, as it says that interval is an arg and not a kwarg.
This seems to have drifted from the original point of this ticket ...

Both `tickerhandler.clear(900)` and `tickerhandler.clear(interval=900)` are correct syntaxes. Adding the keyword is (in this case) just a way to make it more readable and 100% sure what you are sending. 

The tickerhandler should not traceback on this, so it's a bug to be fixed.


It appears that the problem (or at least _a_ problem) with `tickerhandler.clear(interval=900)` or otherwise specifying the interval is that it checks `store_key[1]` against the interval. However, the interval is actually in `store_key[2]`

EDIT
Based on this traceback from discord:
```
File "C:\Users\bradm\mudstuff\mygame\typeclasses\scripts.py", line 204, in at_script_creation
   tickerhandler.clear(interval=900)
 File "c:\users\bradm\mudstuff\evennia\evennia\scripts\tickerhandler.py", line 593, in clear
   self.save()
 File "c:\users\bradm\mudstuff\evennia\evennia\scripts\tickerhandler.py", line 416, in save
   for store_key, (args, kwargs) in self.ticker_storage.items()
 File "c:\users\bradm\mudstuff\evennia\evennia\scripts\tickerhandler.py", line 416, in <dictcomp>
   for store_key, (args, kwargs) in self.ticker_storage.items()
ValueError: too many values to unpack (expected 2)
```

it looks like https://github.com/evennia/evennia/blob/5d708f72f3a9ba9a857f1b6e0a63d442da5513ae/evennia/scripts/tickerhandler.py#L587 is the main problem, as it saves `store_key` as the value in the dict instead of a two-value args/kwargs tuple that `.save()` expects

Resolving patch:
diff --git a/evennia/scripts/tickerhandler.py b/evennia/scripts/tickerhandler.py
--- a/evennia/scripts/tickerhandler.py
+++ b/evennia/scripts/tickerhandler.py
@@ -585,9 +585,9 @@ def clear(self, interval=None):
         self.ticker_pool.stop(interval)
         if interval:
             self.ticker_storage = dict(
-                (store_key, store_key)
-                for store_key in self.ticker_storage
-                if store_key[1] != interval
+                (store_key, store_value)
+                for store_key, store_value in self.ticker_storage.items()
+                if store_key[3] != interval
             )
         else:
             self.ticker_storage = {}


Test patch: