redditadmin 0.0.6__tar.gz → 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. {redditadmin-0.0.6 → redditadmin-0.1.0}/.idea/inspectionProfiles/profiles_settings.xml +1 -0
  2. {redditadmin-0.0.6 → redditadmin-0.1.0}/.idea/reddit-admin.iml +3 -1
  3. {redditadmin-0.0.6 → redditadmin-0.1.0}/.idea/workspace.xml +32 -3
  4. {redditadmin-0.0.6 → redditadmin-0.1.0}/PKG-INFO +12 -8
  5. {redditadmin-0.0.6 → redditadmin-0.1.0}/README.md +11 -7
  6. {redditadmin-0.0.6 → redditadmin-0.1.0}/pyproject.toml +1 -1
  7. {redditadmin-0.0.6 → redditadmin-0.1.0}/src/redditadmin/core.py +27 -67
  8. redditadmin-0.1.0/src/redditadmin/plugin/__init__.py +5 -0
  9. {redditadmin-0.0.6 → redditadmin-0.1.0}/src/redditadmin/plugin/asynchronouspluginsexecutor.py +3 -8
  10. {redditadmin-0.0.6 → redditadmin-0.1.0}/src/redditadmin/plugin/plugin.py +29 -17
  11. {redditadmin-0.0.6 → redditadmin-0.1.0}/src/redditadmin/plugin/pluginsexecutor.py +37 -17
  12. redditadmin-0.1.0/src/redditadmin/plugin/redditinterfacefactory.py +47 -0
  13. redditadmin-0.1.0/src/redditadmin/program/__init__.py +5 -0
  14. {redditadmin-0.0.6 → redditadmin-0.1.0}/src/redditadmin/program/program.py +30 -17
  15. {redditadmin-0.0.6 → redditadmin-0.1.0}/src/redditadmin/program/streamprocessingprogram.py +22 -21
  16. redditadmin-0.1.0/src/redditadmin/utility/__init__.py +9 -0
  17. redditadmin-0.1.0/src/redditadmin/utility/botcredentials.py +83 -0
  18. redditadmin-0.0.6/src/redditadmin/utility/contributionsutility.py → redditadmin-0.1.0/src/redditadmin/utility/contributions.py +1 -1
  19. {redditadmin-0.0.6 → redditadmin-0.1.0}/src/redditadmin/utility/decorators.py +3 -4
  20. redditadmin-0.1.0/src/redditadmin/utility/miscellaneous.py +28 -0
  21. redditadmin-0.1.0/src/redditadmin/utility/redditinterface.py +27 -0
  22. redditadmin-0.1.0/src/redditadmin/utility/redditsubmission.py +44 -0
  23. redditadmin-0.0.6/src/redditadmin/plugin/__init__.py +0 -3
  24. redditadmin-0.0.6/src/redditadmin/plugin/exceptions.py +0 -11
  25. redditadmin-0.0.6/src/redditadmin/plugin/redditinterfacefactory.py +0 -49
  26. redditadmin-0.0.6/src/redditadmin/program/__init__.py +0 -3
  27. redditadmin-0.0.6/src/redditadmin/utility/__init__.py +0 -5
  28. redditadmin-0.0.6/src/redditadmin/utility/botcredentials.py +0 -63
  29. redditadmin-0.0.6/src/redditadmin/utility/exceptions.py +0 -28
  30. redditadmin-0.0.6/src/redditadmin/utility/redditinterface.py +0 -19
  31. redditadmin-0.0.6/src/redditadmin/utility/redditsubmission.py +0 -36
  32. {redditadmin-0.0.6 → redditadmin-0.1.0}/.gitignore +0 -0
  33. {redditadmin-0.0.6 → redditadmin-0.1.0}/.idea/.gitignore +0 -0
  34. {redditadmin-0.0.6 → redditadmin-0.1.0}/.idea/misc.xml +0 -0
  35. {redditadmin-0.0.6 → redditadmin-0.1.0}/.idea/modules.xml +0 -0
  36. {redditadmin-0.0.6 → redditadmin-0.1.0}/.idea/vcs.xml +0 -0
  37. {redditadmin-0.0.6 → redditadmin-0.1.0}/.prettierrc +0 -0
  38. {redditadmin-0.0.6 → redditadmin-0.1.0}/.vscode/settings.json +0 -0
  39. {redditadmin-0.0.6 → redditadmin-0.1.0}/LICENSE +0 -0
  40. {redditadmin-0.0.6 → redditadmin-0.1.0}/praw.ini +0 -0
  41. {redditadmin-0.0.6 → redditadmin-0.1.0}/requirements.txt +0 -0
  42. {redditadmin-0.0.6 → redditadmin-0.1.0}/src/__init__.py +0 -0
  43. {redditadmin-0.0.6 → redditadmin-0.1.0}/src/redditadmin/__init__.py +0 -0
@@ -1,5 +1,6 @@
1
1
  <component name="InspectionProjectProfileManager">
2
2
  <settings>
3
+ <option name="PROJECT_PROFILE" value="Default" />
3
4
  <option name="USE_PROJECT_PROFILE" value="false" />
4
5
  <version value="1.0" />
5
6
  </settings>
@@ -1,7 +1,9 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <module type="PYTHON_MODULE" version="4">
3
3
  <component name="NewModuleRootManager">
4
- <content url="file://$MODULE_DIR$" />
4
+ <content url="file://$MODULE_DIR$">
5
+ <excludeFolder url="file://$MODULE_DIR$/venv" />
6
+ </content>
5
7
  <orderEntry type="jdk" jdkName="Python 3.11 (reddit-admin)" jdkType="Python SDK" />
6
8
  <orderEntry type="sourceFolder" forTests="false" />
7
9
  </component>
@@ -4,7 +4,28 @@
4
4
  <option name="autoReloadType" value="SELECTIVE" />
5
5
  </component>
6
6
  <component name="ChangeListManager">
7
- <list default="true" id="195f63d3-192c-4e64-8cd8-909be23b7f21" name="Changes" comment="" />
7
+ <list default="true" id="195f63d3-192c-4e64-8cd8-909be23b7f21" name="Changes" comment="">
8
+ <change beforePath="$PROJECT_DIR$/.idea/inspectionProfiles/profiles_settings.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/inspectionProfiles/profiles_settings.xml" afterDir="false" />
9
+ <change beforePath="$PROJECT_DIR$/.idea/reddit-admin.iml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/reddit-admin.iml" afterDir="false" />
10
+ <change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
11
+ <change beforePath="$PROJECT_DIR$/src/redditadmin/core.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/redditadmin/core.py" afterDir="false" />
12
+ <change beforePath="$PROJECT_DIR$/src/redditadmin/plugin/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/redditadmin/plugin/__init__.py" afterDir="false" />
13
+ <change beforePath="$PROJECT_DIR$/src/redditadmin/plugin/asynchronouspluginsexecutor.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/redditadmin/plugin/asynchronouspluginsexecutor.py" afterDir="false" />
14
+ <change beforePath="$PROJECT_DIR$/src/redditadmin/plugin/exceptions.py" beforeDir="false" />
15
+ <change beforePath="$PROJECT_DIR$/src/redditadmin/plugin/plugin.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/redditadmin/plugin/plugin.py" afterDir="false" />
16
+ <change beforePath="$PROJECT_DIR$/src/redditadmin/plugin/pluginsexecutor.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/redditadmin/plugin/pluginsexecutor.py" afterDir="false" />
17
+ <change beforePath="$PROJECT_DIR$/src/redditadmin/plugin/redditinterfacefactory.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/redditadmin/plugin/redditinterfacefactory.py" afterDir="false" />
18
+ <change beforePath="$PROJECT_DIR$/src/redditadmin/program/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/redditadmin/program/__init__.py" afterDir="false" />
19
+ <change beforePath="$PROJECT_DIR$/src/redditadmin/program/program.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/redditadmin/program/program.py" afterDir="false" />
20
+ <change beforePath="$PROJECT_DIR$/src/redditadmin/program/streamprocessingprogram.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/redditadmin/program/streamprocessingprogram.py" afterDir="false" />
21
+ <change beforePath="$PROJECT_DIR$/src/redditadmin/utility/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/redditadmin/utility/__init__.py" afterDir="false" />
22
+ <change beforePath="$PROJECT_DIR$/src/redditadmin/utility/botcredentials.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/redditadmin/utility/botcredentials.py" afterDir="false" />
23
+ <change beforePath="$PROJECT_DIR$/src/redditadmin/utility/contributionsutility.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/redditadmin/utility/contributions.py" afterDir="false" />
24
+ <change beforePath="$PROJECT_DIR$/src/redditadmin/utility/decorators.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/redditadmin/utility/decorators.py" afterDir="false" />
25
+ <change beforePath="$PROJECT_DIR$/src/redditadmin/utility/exceptions.py" beforeDir="false" />
26
+ <change beforePath="$PROJECT_DIR$/src/redditadmin/utility/redditinterface.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/redditadmin/utility/redditinterface.py" afterDir="false" />
27
+ <change beforePath="$PROJECT_DIR$/src/redditadmin/utility/redditsubmission.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/redditadmin/utility/redditsubmission.py" afterDir="false" />
28
+ </list>
8
29
  <option name="SHOW_DIALOG" value="false" />
9
30
  <option name="HIGHLIGHT_CONFLICTS" value="true" />
10
31
  <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@@ -20,6 +41,14 @@
20
41
  <component name="Git.Settings">
21
42
  <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
22
43
  </component>
44
+ <component name="GitSEFilterConfiguration">
45
+ <file-type-list>
46
+ <filtered-out-file-type name="LOCAL_BRANCH" />
47
+ <filtered-out-file-type name="REMOTE_BRANCH" />
48
+ <filtered-out-file-type name="TAG" />
49
+ <filtered-out-file-type name="COMMIT_BY_MESSAGE" />
50
+ </file-type-list>
51
+ </component>
23
52
  <component name="MarkdownSettingsMigration">
24
53
  <option name="stateVersion" value="1" />
25
54
  </component>
@@ -34,8 +63,8 @@
34
63
  <component name="PropertiesComponent">
35
64
  <property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
36
65
  <property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
37
- <property name="last_opened_file_path" value="$PROJECT_DIR$/../reddit-admin-configurator" />
38
- <property name="settings.editor.selected.configurable" value="preferences.keymap" />
66
+ <property name="last_opened_file_path" value="$PROJECT_DIR$/src/redditadmin/utility/contributionsutility.py" />
67
+ <property name="settings.editor.selected.configurable" value="Errors" />
39
68
  </component>
40
69
  <component name="RecentsManager">
41
70
  <key name="MoveFile.RECENT_KEYS">
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: redditadmin
3
- Version: 0.0.6
3
+ Version: 0.1.0
4
4
  Summary: Extensible Python administrative bot
5
5
  Project-URL: Homepage, https://github.com/Grod56/reddit-admin
6
6
  Project-URL: Issues, https://github.com/Grod56/reddit-admin/issues
@@ -130,17 +130,21 @@ Plugin:
130
130
 
131
131
  ```py
132
132
  class MyPlugin[Plugin[MyProgram]]
133
- """The plugin for our program"""
134
133
 
135
- def __init__():
136
- super().__init__("myprogram")
137
134
 
138
- def get_program(self, reddit_interface):
139
- """Overriden from Plugin superclass"""
135
+ """The plugin for our program"""
140
136
 
141
- praw_reddit = reddit_interface.get_praw_reddit
142
137
 
143
- return MyProgram(praw_reddit)
138
+ def __init__():
139
+ super().__init__("myprogram")
140
+
141
+
142
+ def get_program(self, reddit_interface):
143
+ """Overriden from Plugin superclass"""
144
+
145
+ praw_reddit = reddit_interface.praw_reddit
146
+
147
+ return MyProgram(praw_reddit)
144
148
  ```
145
149
 
146
150
  Voila! We may now supply our plugin to the bot and run it just as we did before
@@ -116,17 +116,21 @@ Plugin:
116
116
 
117
117
  ```py
118
118
  class MyPlugin[Plugin[MyProgram]]
119
- """The plugin for our program"""
120
119
 
121
- def __init__():
122
- super().__init__("myprogram")
123
120
 
124
- def get_program(self, reddit_interface):
125
- """Overriden from Plugin superclass"""
121
+ """The plugin for our program"""
126
122
 
127
- praw_reddit = reddit_interface.get_praw_reddit
128
123
 
129
- return MyProgram(praw_reddit)
124
+ def __init__():
125
+ super().__init__("myprogram")
126
+
127
+
128
+ def get_program(self, reddit_interface):
129
+ """Overriden from Plugin superclass"""
130
+
131
+ praw_reddit = reddit_interface.praw_reddit
132
+
133
+ return MyProgram(praw_reddit)
130
134
  ```
131
135
 
132
136
  Voila! We may now supply our plugin to the bot and run it just as we did before
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "redditadmin"
7
- version = "0.0.6"
7
+ version = "0.1.0"
8
8
  authors = [
9
9
  { name="Grod56", email="providenceuniversalstudios@gmail.com" },
10
10
  ]
@@ -1,43 +1,36 @@
1
1
  import logging
2
2
  import os
3
3
  import signal
4
- import sys
5
4
  import time
6
- from abc import ABC, abstractmethod
5
+ from abc import ABCMeta, abstractmethod
7
6
  from logging.handlers import TimedRotatingFileHandler
8
7
  from pathlib import Path
9
8
  from typing import List
10
9
 
11
- from .utility.botcredentials import BotCredentials
12
- from .utility.exceptions import BotInitializationError, InvalidBotCredentialsError
10
+ from .utility.botcredentials import InvalidBotCredentialsError, BotCredentials,\
11
+ BotCredentialsImplementation
12
+ from .utility.miscellaneous import BotInitializationError
13
13
  from .plugin.asynchronouspluginsexecutor import AsynchronousPluginsExecutor
14
- from .plugin.exceptions import PluginsExecutorInitializationError
15
14
  from .plugin.plugin import Plugin
16
- from .plugin.pluginsexecutor import PluginsExecutor
17
- from .plugin.redditinterfacefactory import RedditInterfaceFactory
15
+ from .plugin.pluginsexecutor import PluginsExecutor, PluginsExecutorInitializationError
16
+ from .plugin.redditinterfacefactory import RedditInterfaceFactory, DefaultRedditInterfaceFactory
18
17
 
19
18
 
20
- class RedditAdmin(ABC):
21
- """Type encapsulating a RedditAdmin instance"""
22
-
23
- def __init__(self, *args):
24
- pass
19
+ class RedditAdmin(metaclass=ABCMeta):
20
+ """Encapsulates RedditAdmin bot"""
25
21
 
26
22
  @abstractmethod
27
23
  def run(self, bot_credentials: BotCredentials, listen: bool = False):
28
24
  """Run the bot"""
29
-
30
- raise NotImplementedError
25
+ ...
31
26
 
32
27
  @abstractmethod
33
- def stop(self):
28
+ def stop(self, wait: bool):
34
29
  """Shutdown the bot"""
30
+ ...
35
31
 
36
- raise NotImplementedError
37
32
 
38
-
39
- class RedditAdminImplementation(RedditAdmin):
40
- """Reddit Admin Bot"""
33
+ class _RedditAdminImplementation(RedditAdmin):
41
34
 
42
35
  __plugins: List[Plugin]
43
36
  __pluginsExecutor: PluginsExecutor
@@ -106,7 +99,7 @@ class RedditAdminImplementation(RedditAdmin):
106
99
  # Setting the default console logging level global variable
107
100
  self.__defaultConsoleLoggingLevel = console_handler.level
108
101
 
109
- def ___get_new_bot_credentials(self) -> BotCredentials:
102
+ def __get_new_bot_credentials(self) -> BotCredentials:
110
103
  """Convenience method to retrieve bot credentials from user input"""
111
104
 
112
105
  try:
@@ -125,7 +118,7 @@ class RedditAdminImplementation(RedditAdmin):
125
118
  # Resume console logging
126
119
  self.__resume_console_logging()
127
120
 
128
- return BotCredentials(
121
+ return BotCredentialsImplementation(
129
122
  user_agent, client_id,
130
123
  client_secret, username,
131
124
  password
@@ -144,7 +137,7 @@ class RedditAdminImplementation(RedditAdmin):
144
137
  # instance from provided credentials
145
138
 
146
139
  try:
147
- reddit_interface_factory = RedditInterfaceFactory(
140
+ reddit_interface_factory = DefaultRedditInterfaceFactory(
148
141
  bot_credentials
149
142
  )
150
143
  # Handle if credential authentication fails
@@ -154,7 +147,7 @@ class RedditAdminImplementation(RedditAdmin):
154
147
  "Please enter new valid credentials"
155
148
  )
156
149
  try:
157
- new_bot_credentials = self.___get_new_bot_credentials()
150
+ new_bot_credentials = self.__get_new_bot_credentials()
158
151
  reddit_interface_factory = self.__get_reddit_interface_factory(new_bot_credentials)
159
152
  except (KeyboardInterrupt, EOFError):
160
153
  raise BotInitializationError(
@@ -206,6 +199,7 @@ class RedditAdminImplementation(RedditAdmin):
206
199
  self.__pluginsExecutor = self.__initialize_plugins_executor(
207
200
  bot_credentials
208
201
  )
202
+ self.__mainLogger.info("Bot successfully initialized")
209
203
 
210
204
  # -------------------------------------------------------------------------------
211
205
 
@@ -213,13 +207,10 @@ class RedditAdminImplementation(RedditAdmin):
213
207
  except BotInitializationError as er:
214
208
  self.__mainLogger.critical(
215
209
  "A fatal error occurred during the "
216
- "bot's initialization. The application "
217
- "will now exit. Error(s): " + str(er),
210
+ "bot's initialization. Error(s): " + str(er),
218
211
  exc_info=True
219
212
  )
220
- sys.exit(2) # TODO: May need future cleaning up
221
-
222
- self.__mainLogger.info("Bot successfully initialized")
213
+ raise er
223
214
 
224
215
  # -------------------------------------------------------------------------------
225
216
 
@@ -310,21 +301,7 @@ class RedditAdminImplementation(RedditAdmin):
310
301
  "'{}' is not a valid bot command".format(command)
311
302
  )
312
303
 
313
- @staticmethod
314
- def __kill_bot():
315
- """Forcefully shut down the bot"""
316
-
317
- # Windows kill command
318
- if (
319
- sys.platform.startswith('win32') or
320
- sys.platform.startswith('cygwin')
321
- ):
322
- os.kill(os.getpid(), signal.CTRL_BREAK_EVENT)
323
-
324
- # Linux kill command
325
- os.kill(os.getpid(), signal.SIGKILL)
326
-
327
- def __shut_down_bot(self, wait=True, shutdown_exit_code=0):
304
+ def __shut_down_bot(self, wait=True):
328
305
  """Shut down the bot"""
329
306
 
330
307
  if wait:
@@ -342,30 +319,13 @@ class RedditAdminImplementation(RedditAdmin):
342
319
  )
343
320
  )
344
321
  )
345
- try:
346
- self.__pluginsExecutor.shut_down(True)
347
- self.__mainLogger.info('Bot successfully shut down')
348
- if shutdown_exit_code != 0:
349
- sys.exit(shutdown_exit_code)
350
-
351
- # Handle keyboard interrupt midway through graceful shutdown
352
- except KeyboardInterrupt:
353
-
354
- self.__mainLogger.warning(
355
- 'Graceful shutdown aborted.'
356
- )
357
- self.__pluginsExecutor.shut_down(False)
358
- self.__mainLogger.info('Bot shut down')
359
-
360
- # Killing the process (only way to essentially stop all threads)
361
- self.__kill_bot()
322
+ self.__pluginsExecutor.shut_down(True)
323
+ self.__mainLogger.info('Bot successfully shut down')
362
324
 
363
325
  else:
364
326
  self.__pluginsExecutor.shut_down(False)
365
327
  self.__mainLogger.info('Bot shut down')
366
328
 
367
- self.__kill_bot()
368
-
369
329
  def __is_bot_shut_down(self):
370
330
  """Check if bot is shutdown"""
371
331
 
@@ -389,7 +349,7 @@ class RedditAdminImplementation(RedditAdmin):
389
349
  'a graceful shutdown is attempted or press '
390
350
  'Ctrl+C to exit immediately'
391
351
  )
392
- self.__shut_down_bot(True, 1)
352
+ self.__shut_down_bot(True)
393
353
 
394
354
  # Handle unknown exception while bot is running
395
355
  except BaseException as ex:
@@ -399,7 +359,7 @@ class RedditAdminImplementation(RedditAdmin):
399
359
  "a graceful shutdown is attempted or press "
400
360
  "Ctrl+C to exit immediately: " + str(ex.args), exc_info=True
401
361
  )
402
- self.__shut_down_bot(True, 2)
362
+ self.__shut_down_bot(True)
403
363
 
404
364
  def run(self, bot_credentials: BotCredentials, listen: bool = False):
405
365
 
@@ -427,9 +387,9 @@ class RedditAdminImplementation(RedditAdmin):
427
387
  if not self.__is_bot_shut_down():
428
388
  self.__shut_down_bot()
429
389
 
430
- def stop(self):
390
+ def stop(self, wait: bool):
431
391
 
432
- self.__shut_down_bot()
392
+ self.__shut_down_bot(wait=wait)
433
393
 
434
394
  # -------------------------------------------------------------------------------
435
395
 
@@ -437,4 +397,4 @@ class RedditAdminImplementation(RedditAdmin):
437
397
  def get_reddit_admin(plugins: List[Plugin]) -> RedditAdmin:
438
398
  """Get a Reddit Admin instance"""
439
399
 
440
- return RedditAdminImplementation(plugins=plugins)
400
+ return _RedditAdminImplementation(plugins=plugins)
@@ -0,0 +1,5 @@
1
+ from .plugin import Plugin as IPlugin, AbstractPlugin as Plugin, PluginInitializationError
2
+ from .pluginsexecutor import PluginsExecutor as IPluginsExecutor, \
3
+ AbstractPluginsExecutor as PluginsExecutor, PluginsExecutorInitializationError
4
+ from .redditinterfacefactory import RedditInterfaceFactory as IRedditInterfaceFactory,\
5
+ DefaultRedditInterfaceFactory as RedditInterfaceFactory
@@ -3,22 +3,19 @@
3
3
  import concurrent.futures
4
4
  from concurrent.futures import ThreadPoolExecutor, Future
5
5
  from typing import Dict, List
6
- from .pluginsexecutor import PluginsExecutor
6
+ from .pluginsexecutor import AbstractPluginsExecutor, PluginsExecutorInitializationError
7
7
  from .redditinterfacefactory import RedditInterfaceFactory
8
8
  from .plugin import Plugin
9
- from .exceptions import PluginsExecutorInitializationError
10
9
 
11
10
 
12
- class AsynchronousPluginsExecutor(PluginsExecutor):
11
+ class AsynchronousPluginsExecutor(AbstractPluginsExecutor):
13
12
  """
14
- Class responsible for asynchronously executing
15
- multiple plugins in different threads
13
+ Executes multiple plugins in different threads
16
14
  """
17
15
 
18
16
  __executor: ThreadPoolExecutor
19
17
  __plugins: Dict[str, Plugin]
20
18
  __executedPrograms: Dict[str, Future]
21
- __redditInterfaceFactory: RedditInterfaceFactory
22
19
 
23
20
  def __init__(
24
21
  self,
@@ -182,7 +179,6 @@ class AsynchronousPluginsExecutor(PluginsExecutor):
182
179
  )
183
180
 
184
181
  def get_program_statuses(self):
185
- """Get the executed program statuses"""
186
182
 
187
183
  program_statuses = \
188
184
  {
@@ -192,7 +188,6 @@ class AsynchronousPluginsExecutor(PluginsExecutor):
192
188
  return program_statuses
193
189
 
194
190
  def shut_down(self, wait):
195
- """Shut down the plugin executor"""
196
191
 
197
192
  super().shut_down()
198
193
  for plugin in self.__plugins.values():
@@ -1,20 +1,42 @@
1
1
  import logging
2
- from abc import ABC, abstractmethod
2
+ from abc import ABCMeta, abstractmethod
3
3
  from typing import TypeVar, Generic
4
4
 
5
5
  from ..program.program import Program
6
6
  from ..utility.redditinterface import RedditInterface
7
- from ..utility.exceptions import InitializationError
7
+ from ..utility.miscellaneous import InitializationError
8
8
 
9
9
  T = TypeVar("T", bound=Program)
10
10
 
11
11
 
12
- class Plugin(Generic[T], ABC):
12
+ class Plugin(Generic[T], metaclass=ABCMeta):
13
13
  """
14
- Class responsible for generating multiple
15
- instances of a specific program
14
+ Generates multiple instances of a specific program
16
15
  """
17
16
 
17
+ @abstractmethod
18
+ def get_program(self, reddit_interface: RedditInterface) -> T:
19
+ """Get new program instance"""
20
+ ...
21
+
22
+ @abstractmethod
23
+ def get_program_command(self) -> str:
24
+ """Get the program command string"""
25
+ ...
26
+
27
+ @abstractmethod
28
+ def is_shut_down(self) -> bool:
29
+ """Check if plugin is shut down"""
30
+ ...
31
+
32
+ @abstractmethod
33
+ def shut_down(self):
34
+ """Shut down the plugin"""
35
+ ...
36
+
37
+
38
+ class AbstractPlugin(Generic[T], Plugin, metaclass=ABCMeta):
39
+
18
40
  _programCommand: str
19
41
  _pluginLogger: logging.Logger
20
42
  _isPluginShutDown: bool
@@ -29,33 +51,23 @@ class Plugin(Generic[T], ABC):
29
51
  )
30
52
  self._isPluginShutDown = False
31
53
 
32
- @abstractmethod
33
- def get_program(self, reddit_interface: RedditInterface) -> T:
34
- """Get new program instance"""
35
-
36
- raise NotImplementedError
37
-
38
54
  def get_program_command(self) -> str:
39
- """Get the program command string"""
40
55
  return self._programCommand
41
56
 
42
57
  def is_shut_down(self) -> bool:
43
- """Check if plugin is shut down"""
44
58
  return self._isPluginShutDown
45
59
 
46
60
  def shut_down(self):
47
- """Shut down the plugin"""
48
61
  self._isPluginShutDown = True
49
62
 
50
63
  def __eq__(self, value) -> bool:
51
- return isinstance(value, Plugin) and \
64
+ return isinstance(value, AbstractPlugin) and \
52
65
  self.get_program_command() == value.get_program_command()
53
66
 
54
67
 
55
68
  class PluginInitializationError(InitializationError):
56
69
  """
57
- Class to encapsulate an error in the initialization
58
- of a plugin module
70
+ Raised when initialization of a plugin module fails
59
71
  """
60
72
 
61
73
  def __init__(self, *args):
@@ -1,43 +1,54 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  import logging
4
- from abc import ABC, abstractmethod
4
+ from abc import ABCMeta, abstractmethod
5
5
  from typing import Dict
6
6
 
7
+ from src.redditadmin.utility.miscellaneous import InitializationError
7
8
 
8
- class PluginsExecutor(ABC):
9
+
10
+ class PluginsExecutor(metaclass=ABCMeta):
9
11
  """
10
- Class responsible for executing plugins
12
+ Executes plugins
11
13
  """
12
14
 
13
- _isPluginsExecutorShutDown: bool
14
- _pluginsExecutorLogger: logging.Logger
15
-
16
- def __init__(self, plugins_executor_name: str):
17
- self._pluginsExecutorLogger = logging.getLogger(
18
- plugins_executor_name
19
- )
20
- self._isPluginsExecutorShutDown = False
21
-
22
15
  @abstractmethod
23
16
  def execute_program(self, program_command):
24
17
  """Execute the provided program command"""
25
-
26
- raise NotImplementedError
18
+ ...
27
19
 
28
20
  @abstractmethod
29
21
  def get_program_statuses(self) -> Dict[str, str]:
30
22
  """Get the executed program statuses"""
23
+ ...
31
24
 
32
- raise NotImplementedError
33
-
25
+ @abstractmethod
34
26
  def shut_down(self, *args):
35
27
  """Shut down the plugins executor"""
28
+ ...
29
+
30
+ @abstractmethod
31
+ def is_shut_down(self) -> bool:
32
+ """Check if the Plugins Executor is shut down"""
33
+ ...
34
+
35
+
36
+ class AbstractPluginsExecutor(PluginsExecutor, metaclass=ABCMeta):
37
+
38
+ _isPluginsExecutorShutDown: bool
39
+ _pluginsExecutorLogger: logging.Logger
40
+
41
+ def __init__(self, plugins_executor_name: str):
42
+ self._pluginsExecutorLogger = logging.getLogger(
43
+ plugins_executor_name
44
+ )
45
+ self._isPluginsExecutorShutDown = False
46
+
47
+ def shut_down(self, *args):
36
48
 
37
49
  self._isPluginsExecutorShutDown = True
38
50
 
39
51
  def is_shut_down(self) -> bool:
40
- """Check if the Plugins Executor is shut down"""
41
52
 
42
53
  return self._isPluginsExecutorShutDown
43
54
 
@@ -52,3 +63,12 @@ class PluginsExecutor(ABC):
52
63
  "The plugins executor cannot execute any more program "
53
64
  "after it has been shut down"
54
65
  )
66
+
67
+
68
+ class PluginsExecutorInitializationError(InitializationError):
69
+ """
70
+ Raised when initialization of a Plugins Executor module fails
71
+ """
72
+
73
+ def __init__(self, *args):
74
+ super().__init__(*args)
@@ -0,0 +1,47 @@
1
+ from abc import ABCMeta, abstractmethod
2
+
3
+ import praw
4
+
5
+ from ..utility.botcredentials import BotCredentials, InvalidBotCredentialsError
6
+ from ..utility.miscellaneous import is_reddit_authenticated
7
+ from ..utility.redditinterface import RedditInterface, RedditInterfaceImplementation
8
+
9
+
10
+ class RedditInterfaceFactory(metaclass=ABCMeta):
11
+ """Factory for RedditInterface objects"""
12
+
13
+ @abstractmethod
14
+ def get_reddit_interface(self) -> RedditInterface:
15
+ """Retrieve new Reddit Interface"""
16
+ ...
17
+
18
+
19
+ class DefaultRedditInterfaceFactory(RedditInterfaceFactory):
20
+
21
+ def __init__(
22
+ self,
23
+ bot_credentials: BotCredentials
24
+ ):
25
+ praw_reddit = praw.Reddit(
26
+ user_agent=bot_credentials.user_agent,
27
+ client_id=bot_credentials.client_id,
28
+ client_secret=bot_credentials.client_secret,
29
+ username=bot_credentials.username,
30
+ password=bot_credentials.password
31
+ )
32
+ if not is_reddit_authenticated(praw_reddit):
33
+ raise InvalidBotCredentialsError
34
+
35
+ self.__botCredentials = bot_credentials
36
+
37
+ def get_reddit_interface(self):
38
+
39
+ bot_credentials = self.__botCredentials
40
+ praw_reddit = praw.Reddit(
41
+ user_agent=bot_credentials.user_agent,
42
+ client_id=bot_credentials.client_id,
43
+ client_secret=bot_credentials.client_secret,
44
+ username=bot_credentials.username,
45
+ password=bot_credentials.password
46
+ )
47
+ return RedditInterfaceImplementation(praw_reddit)
@@ -0,0 +1,5 @@
1
+ from .program import Program as IProgram, AbstractProgram as Program,\
2
+ RecurringProgram as IRecurringProgram, AbstractRecurringProgram as RecurringProgram
3
+ from .streamprocessingprogram import StreamProcessingProgram as IStreamProcessingProgram,\
4
+ AbstractStreamProcessingProgram as StreamProcessingProgram,\
5
+ StreamFactory, SubmissionStreamFactory, CommentStreamFactory, CustomStreamFactory