apirlpy 0.0.4__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.
@@ -0,0 +1,8 @@
1
+ Copyright 2026 Shaik Naseer John Ahmed
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8
+
apirlpy-0.0.4/PKG-INFO ADDED
@@ -0,0 +1,55 @@
1
+ Metadata-Version: 2.4
2
+ Name: apirlpy
3
+ Version: 0.0.4
4
+ Summary: A Python Based API RateLimiter
5
+ Author-email: Shaik Naseer John Ahmed <sknaseer.fez@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Naseer-fez/Apirl-py
8
+ Project-URL: Issues, https://github.com/Naseer-fez/Apirl-py/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.4
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE.txt
14
+ Dynamic: license-file
15
+
16
+ ## How to Run
17
+ ```python
18
+ from apirlpy import ratelimiter as APIRL
19
+ #Import the Function
20
+
21
+ # Checking rate limit for a specific IP
22
+ CoolDown=APIRL(IP_Adrs="127.0.0.1", #IP address ofthe Client
23
+ #_______________________________________________________________
24
+ Cleaning=False, #To Enable the Ip Address Cleaning
25
+ CleaningFreq=80,# The Frequency at which the cleaning shoudl occur
26
+ AutoUpdate=False, #If you want to update the Data only after a certain time to reduce I/O operations
27
+ UpdateFreq=70, #(value in sec) of auto update
28
+ CooldownTime=20, #Amount of time differnce the Clients last vist shoudld
29
+ # exced for the IP address cleaner to remove the data
30
+ # *** FOR JSON AND SQL Format only ***
31
+ #If passed to "no stograge" option then this arguments will not be passed to the ratelimiter
32
+ #_______________________________________________________________
33
+ AllowedFreq=8, #The Number of Fair attempts the Client can send the request to the server
34
+ ResetTime=8, # After the Client is denied the access , how much time
35
+ #they need to wait to get the access again
36
+ Filename="Test", # The file name to store the data
37
+ Format="sql", # The File extenstion type which the ratelimiter is going to use to store the data
38
+ # Extensions available are : sql to use sqllite , json to use json format and "None" for No storage option
39
+ # If nothing is mentioned then the data will not be stored
40
+ FolderPath=None # The path at which u want to store this results
41
+
42
+ )
43
+ if CoolDown == 1: #The Client can access the API
44
+ print( "You are a Valid user")
45
+ else: # the Request is denied
46
+ print( f"You need to wait {CoolDown}")
47
+ ```
48
+ **This is a test code **
49
+ ### The Code Return
50
+ - 1 : Means the Client Can Access the API
51
+ - other than 1: Means the Client Need to wait for the access of the API
52
+ ___
53
+
54
+
55
+
@@ -0,0 +1,40 @@
1
+ ## How to Run
2
+ ```python
3
+ from apirlpy import ratelimiter as APIRL
4
+ #Import the Function
5
+
6
+ # Checking rate limit for a specific IP
7
+ CoolDown=APIRL(IP_Adrs="127.0.0.1", #IP address ofthe Client
8
+ #_______________________________________________________________
9
+ Cleaning=False, #To Enable the Ip Address Cleaning
10
+ CleaningFreq=80,# The Frequency at which the cleaning shoudl occur
11
+ AutoUpdate=False, #If you want to update the Data only after a certain time to reduce I/O operations
12
+ UpdateFreq=70, #(value in sec) of auto update
13
+ CooldownTime=20, #Amount of time differnce the Clients last vist shoudld
14
+ # exced for the IP address cleaner to remove the data
15
+ # *** FOR JSON AND SQL Format only ***
16
+ #If passed to "no stograge" option then this arguments will not be passed to the ratelimiter
17
+ #_______________________________________________________________
18
+ AllowedFreq=8, #The Number of Fair attempts the Client can send the request to the server
19
+ ResetTime=8, # After the Client is denied the access , how much time
20
+ #they need to wait to get the access again
21
+ Filename="Test", # The file name to store the data
22
+ Format="sql", # The File extenstion type which the ratelimiter is going to use to store the data
23
+ # Extensions available are : sql to use sqllite , json to use json format and "None" for No storage option
24
+ # If nothing is mentioned then the data will not be stored
25
+ FolderPath=None # The path at which u want to store this results
26
+
27
+ )
28
+ if CoolDown == 1: #The Client can access the API
29
+ print( "You are a Valid user")
30
+ else: # the Request is denied
31
+ print( f"You need to wait {CoolDown}")
32
+ ```
33
+ **This is a test code **
34
+ ### The Code Return
35
+ - 1 : Means the Client Can Access the API
36
+ - other than 1: Means the Client Need to wait for the access of the API
37
+ ___
38
+
39
+
40
+
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["setuptools >= 77.0.3"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+
6
+
7
+ [project]
8
+ name = "apirlpy"
9
+ # name="Api_RateLimiter"
10
+ version = "0.0.4"
11
+ authors = [
12
+ { name="Shaik Naseer John Ahmed",email="sknaseer.fez@gmail.com" },
13
+ ]
14
+ description = "A Python Based API RateLimiter "
15
+ readme = "README.md"
16
+ requires-python = ">=3.4"
17
+ classifiers = [
18
+ "Programming Language :: Python :: 3",
19
+ "Operating System :: OS Independent",
20
+ ]
21
+ license = "MIT"
22
+ license-files = ["LICEN[CS]E*"]
23
+
24
+ [project.urls]
25
+ Homepage = "https://github.com/Naseer-fez/Apirl-py"
26
+ Issues = "https://github.com/Naseer-fez/Apirl-py/issues"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,245 @@
1
+ import os
2
+ import sys
3
+ from pathlib import Path
4
+ import json
5
+ import time
6
+ import threading
7
+ import copy
8
+
9
+
10
+
11
+ class __RateLimiter:
12
+ __isfileopen :bool = False
13
+ __samplefile = Path(sys.modules['__main__'].__file__).stem
14
+ def __init__(self,filename=__samplefile,filetype="json",folder=None):
15
+ self.thread_running = False
16
+ self.filename=filename
17
+ self.filetype=filetype
18
+ self.Data=dict()
19
+ self.CurrentFile=None
20
+ self.lock = threading.RLock()
21
+ self.stop_event = threading.Event()
22
+ self.fullpath=os.path.join(folder or "", f"{filename}.{filetype}")
23
+ self.Validopen=self.__Fileopener()
24
+
25
+ pass
26
+ def API_RL(self,IP_Adrs:str,Cleaning=False,AutoUpdate=False,
27
+ CooldownTime=20,AllowedFreq=8,CleaningFreq=80,ResetTime=8,
28
+ UpdateFreq=80)->int:
29
+ # if isinstance(ipaddress,str):
30
+ # self.ip=int(ipaddress.ip_address(IP_Adrs))
31
+ # else:print("Here")
32
+ # self.ip=IP_Adrs
33
+ self.AutoUpdate=AutoUpdate
34
+ self.Metrics={
35
+ "CooldownTime":CooldownTime,
36
+ "AllowedFreq":AllowedFreq,
37
+
38
+ }
39
+ # background_thread = threading.Thread(target=self.hehe, args=(start_val,), daemon=True)
40
+ if (Cleaning or (AutoUpdate)) and not self.thread_running:
41
+ self.thread_running=True
42
+ BackgroundThread=threading.Thread(
43
+
44
+ target=self.__BackgroundWorker,
45
+ args=(CleaningFreq,ResetTime,UpdateFreq,Cleaning,),
46
+ daemon=False)
47
+ BackgroundThread.start()
48
+
49
+ return self.__Validator(ip=IP_Adrs)
50
+ def __Fileopener(self)->int:
51
+ if getattr(self, "__isfileopen", False):
52
+ return 0
53
+ data=dict()
54
+ fullpath=self.fullpath
55
+ try:
56
+ with open (fullpath,'r') as File:
57
+ try:
58
+ self.CurrentFile = open(fullpath, 'r+')
59
+ self.Data=json.load(File)
60
+ self.__isfileopen=True
61
+ return 1
62
+ except json.JSONDecodeError as error:
63
+ return 0
64
+
65
+ except FileNotFoundError as Fnf:
66
+
67
+ return self.__Filedumper(operation=0)
68
+ except FileNotFoundError as Fnf:
69
+ self.CurrentFile=open(fullpath, 'w')
70
+ # print("HAHHA")
71
+ # json.dump({},self.CurrentFile,indent=4)
72
+ return 1
73
+ # return self.__Filedumper(operation=0)
74
+
75
+ except FileExistsError as FEE:
76
+
77
+ return 0
78
+ except Exception as error:
79
+
80
+ print(error)
81
+ return 0
82
+ def __Filedumper(self,operation=0,Data=None,update=1)->int:
83
+ # print(Data)
84
+ # if msg is not None: print(msg[0])
85
+ if (self.AutoUpdate and update) :
86
+ return 1
87
+ try:
88
+ with self.lock:
89
+ # if msg is not None: print(msg[1])
90
+ fullpath=self.fullpath
91
+ flag=0
92
+
93
+ if Data is None:
94
+ Data={}
95
+ if self.CurrentFile is None:
96
+ self.CurrentFile = open(fullpath, 'w')
97
+ try:
98
+ self.CurrentFile.seek(0)
99
+ json.dump(Data,self.CurrentFile,indent=4)
100
+ self.CurrentFile.truncate()
101
+ self.CurrentFile.flush()
102
+ flag=1
103
+
104
+ except Exception as e:
105
+ try :
106
+ with open("LOg..txt",'a') as file:
107
+ record=f"{time.time()}:Error is {e}\n"
108
+ file.write(record)
109
+
110
+ flag=0
111
+ except Exception as err:
112
+ flag=0
113
+
114
+ if operation==1:
115
+ self.CurrentFile.close()
116
+ self.__isfileopen=False
117
+ return 1
118
+ else:
119
+ self.__isfileopen=True
120
+ return flag
121
+ except Exception as e:
122
+ print("HEHEH")
123
+
124
+
125
+
126
+ pass
127
+ def __Validator(self,ip:int)->int:
128
+
129
+ currenttime=int(time.time())
130
+ error=False
131
+ flag=1
132
+ with self.lock:
133
+ try:
134
+ lastseen=currenttime-self.Data[ip]["LastSeenTime"]
135
+ except KeyError:
136
+
137
+ self.Data[ip]={
138
+ # "WaitTime":0,
139
+ "WaitStamp":0,
140
+ "LastSeenTime":currenttime,
141
+ "Visits":1
142
+ }
143
+ error=True
144
+ self.Data[ip]["LastSeenTime"]=currenttime
145
+ if error is False:
146
+ if lastseen>self.Metrics["CooldownTime"]:
147
+ # self.Data[ip]["WaitTime"]=0
148
+ self.Data[ip]["WaitStamp"]=0
149
+ self.Data[ip]["Visits"]=1
150
+ flag= 1
151
+ else:
152
+ Datacopy=copy.deepcopy(self.Data)
153
+ self.Data[ip],flag= self.__RecentVists(Data=Datacopy[ip],CrnTime=currenttime)
154
+
155
+ self.__Filedumper(Data=self.Data,update=0)
156
+ return (flag or error)
157
+ def __RecentVists(self,Data,CrnTime)->tuple: #Solution For DeadLock
158
+
159
+
160
+ if Data["Visits"]<=self.Metrics["AllowedFreq"]:
161
+ Data["WaitStamp"]=0
162
+ Data["Visits"]+=1
163
+ return (Data,1)
164
+ # timetowait=0
165
+ if Data["WaitStamp"]==0:
166
+ Data["WaitStamp"]=CrnTime+self.Metrics["CooldownTime"]
167
+ Data["Visits"]+=1
168
+ return (Data,self.Metrics["CooldownTime"])
169
+ else:
170
+ timetosend=Data["WaitStamp"]-CrnTime
171
+ Data["Visits"]+=1
172
+ if timetosend<=0:
173
+ Data["WaitStamp"]=0
174
+ Data["Visits"]=0
175
+ timetosend=1
176
+
177
+ return (Data,timetosend)
178
+ def __BackgroundWorker(self,ClnFrq=100,restlimit=80,UpdateFreq=80,Cleaning=False):
179
+
180
+ self.thread_running=True
181
+ CleanData={}
182
+ Slptime=self.__Timecalculator(ClnFrq,UpdateFreq)
183
+ while not self.stop_event.is_set():
184
+ # print("Inside the Loop")
185
+ # self.stop_event.wait(ClnFrq)
186
+ if self.stop_event.is_set():
187
+ break
188
+ if Cleaning:
189
+ with self.lock:
190
+ # print("Inside the Lock")
191
+ currenttime=int(time.time())
192
+ # Keystodelete=[]
193
+ changes=False
194
+ CleanData=self.Data.copy()
195
+ keys=list(CleanData.keys())
196
+ for ip in keys:
197
+ # print("Checking IPs")
198
+ # print("Key here")
199
+ if(currenttime-CleanData[ip]["LastSeenTime"]>restlimit):
200
+ # print("BYEEE")
201
+ # Keystodelete.append(ip)
202
+ # print("Found IP")
203
+ changes=True
204
+ try:
205
+ del CleanData[ip]
206
+ except Exception as e:
207
+ print(e)
208
+ if changes is True:
209
+ # print("Bye IPPPP")
210
+ self.Data=CleanData
211
+ changes=False
212
+
213
+ self.__Filedumper(Data=self.Data)
214
+ # print("Sleep")
215
+
216
+ # print("Loop End")
217
+ time.sleep(Slptime)
218
+ self.thread_running = False
219
+ def __Timecalculator(self,CleaningFrq,UpdateFreq)->int:
220
+ return UpdateFreq
221
+
222
+ __Rl=__RateLimiter()
223
+ def Ratelimiter(IP_Adrs:str,Cleaning=False,AutoUpdate=False,
224
+ CooldownTime=20,AllowedFreq=8,CleaningFreq=80,ResetTime=8,UpdateFreq=8,Filename=None,FolderPath=None)->int:
225
+ global __Rl
226
+ if (Filename is not None) and (FolderPath is not None):
227
+ __Rl = __RateLimiter(filename=Filename, folder=FolderPath)
228
+
229
+ return (__Rl.API_RL(IP_Adrs=IP_Adrs,Cleaning=Cleaning,AutoUpdate=AutoUpdate,
230
+ CooldownTime=CooldownTime,AllowedFreq=AllowedFreq,CleaningFreq=CleaningFreq,ResetTime=ResetTime,UpdateFreq=UpdateFreq))
231
+
232
+
233
+
234
+ if __name__=="__main__":
235
+
236
+
237
+ v=Ratelimiter("127.0.0.2",Cleaning=0,CleaningFreq=1,CooldownTime=7)
238
+ print(v)
239
+
240
+
241
+
242
+
243
+
244
+
245
+
@@ -0,0 +1,142 @@
1
+ import time
2
+ import threading
3
+
4
+
5
+
6
+ class __RateLimiter:
7
+
8
+ def __init__(self):
9
+ self.thread_running = False
10
+ self.Data=dict()
11
+ self.stop_event = threading.Event()
12
+ self.lock = threading.RLock()
13
+
14
+ pass
15
+ def API_RL(self,IP_Adrs:str,Cleaning=False,CleaningFreq=80,CooldownTime=80,AllowedFreq=10,ResetTime=100)->int:
16
+ # if isinstance(ipaddress,str):
17
+ # self.ip=int(ipaddress.ip_address(IP_Adrs))
18
+ # else:print("Here")
19
+ self.Metrics={
20
+ "CooldownTime":CooldownTime,
21
+ "AllowedFreq":AllowedFreq}
22
+ if (Cleaning ) and not self.thread_running:
23
+ self.thread_running=True
24
+ BackgroundThread=threading.Thread(
25
+
26
+ target=self.__BackgroundWorker,
27
+ args=(CleaningFreq,ResetTime,),
28
+ daemon=True)
29
+ BackgroundThread.start()
30
+
31
+ return self.__Validator(ip=IP_Adrs)
32
+ def __Validator(self,ip:int)->int:
33
+
34
+ currenttime=int(time.time())
35
+ error=False
36
+ flag=1
37
+ with self.lock:
38
+ try:
39
+ lastseen=currenttime-self.Data[ip]["LastSeenTime"]
40
+ except KeyError:
41
+
42
+ self.Data[ip]={
43
+ # "WaitTime":0,
44
+ "WaitStamp":0,
45
+ "LastSeenTime":currenttime,
46
+ "Visits":1
47
+ }
48
+ error=True
49
+ self.Data[ip]["LastSeenTime"]=currenttime
50
+ if error is False:
51
+ if lastseen>self.Metrics["CooldownTime"]:
52
+ # self.Data[ip]["WaitTime"]=0
53
+ self.Data[ip]["WaitStamp"]=0
54
+ self.Data[ip]["Visits"]=1
55
+ flag= 1
56
+ else:
57
+ self.Data[ip],flag= self.__RecentVists(Data=self.Data[ip],CrnTime=currenttime)
58
+
59
+ return (flag or error)
60
+ def __RecentVists(self,Data,CrnTime)->tuple: #Solution For DeadLock
61
+
62
+
63
+ if Data["Visits"]<=self.Metrics["AllowedFreq"]:
64
+ Data["WaitStamp"]=0
65
+ Data["Visits"]+=1
66
+ return (Data,1)
67
+ # timetowait=0
68
+ if Data["WaitStamp"]==0:
69
+ Data["WaitStamp"]=CrnTime+self.Metrics["CooldownTime"]
70
+ Data["Visits"]+=1
71
+ return (Data,self.Metrics["CooldownTime"])
72
+ else:
73
+ timetosend=Data["WaitStamp"]-CrnTime
74
+ Data["Visits"]+=1
75
+ if timetosend<=0:
76
+ Data["WaitStamp"]=0
77
+ Data["Visits"]=0
78
+ timetosend=1
79
+
80
+ return (Data,timetosend)
81
+ def __BackgroundWorker(self,ClnFrq=100,restlimit=100):
82
+
83
+ self.thread_running=True
84
+ self.Data={}
85
+
86
+ while not self.stop_event.is_set():
87
+ # print("Inside the Loop")
88
+ # self.stop_event.wait(ClnFrq)
89
+ if self.stop_event.is_set():
90
+ break
91
+
92
+ with self.lock:
93
+ # print("Inside the Lock")
94
+ currenttime=int(time.time())
95
+ # Keystodelete=[]
96
+ changes=False
97
+ keys=list(self.Data.keys())
98
+ for ip in keys:
99
+ # print("Checking IPs")
100
+ # print("Key here")
101
+ if(currenttime-self.Data[ip]["LastSeenTime"]>restlimit):
102
+ # print("BYEEE")
103
+ # Keystodelete.append(ip)
104
+ # print("Found IP")
105
+ changes=True
106
+ try:
107
+ del self.Data[ip]
108
+ except Exception as e:
109
+ print(e)
110
+ if changes is True:
111
+ # print("Bye IPPPP")
112
+ self.Data=self.Data
113
+ changes=False
114
+
115
+ # print("Sleep")
116
+
117
+ # print("Loop End")
118
+ time.sleep(ClnFrq)
119
+ self.thread_running = False
120
+
121
+
122
+ __Rl=__RateLimiter()
123
+ def Ratelimiter(IP_Adrs:str,Cleaning=False,CleaningFreq=80,AllowedFreq=10,ResetTime=100)->int:
124
+ global __Rl
125
+
126
+
127
+ return (__Rl.API_RL(IP_Adrs=IP_Adrs,Cleaning=Cleaning,CleaningFreq=CleaningFreq,AllowedFreq=AllowedFreq,ResetTime=ResetTime))
128
+
129
+
130
+
131
+ if __name__=="__main__":
132
+
133
+
134
+ v=Ratelimiter("127.0.0.2",Cleaning=0,CleaningFreq=1)
135
+ print(v)
136
+
137
+
138
+
139
+
140
+
141
+
142
+
@@ -0,0 +1,258 @@
1
+ import os
2
+ import sys
3
+ from pathlib import Path
4
+ import json
5
+ import time
6
+ import threading
7
+ import copy
8
+ import sqlite3
9
+
10
+
11
+ class __RateLimiter:
12
+ __isfileopen :bool = False
13
+ __samplefile = Path(sys.modules['__main__'].__file__).stem
14
+ def __init__(self,filename=__samplefile,filetype="db",folder=None):
15
+ self.thread_running = False
16
+ self.filename=filename
17
+ self.filetype=filetype
18
+ self.Data=dict()
19
+ self.CurrentFile=None
20
+ self.lock = threading.RLock()
21
+ self.stop_event = threading.Event()
22
+ self.fullpath=os.path.join(folder or "", f"{filename}.{filetype}")
23
+ self.cursor=None
24
+ self.connection=None
25
+ self.Validopen=self.__Fileopener()
26
+ self.AutoUpdate=False
27
+ self.Changes=set()
28
+ pass
29
+ def API_RL(self,IP_Adrs:str,Cleaning=False,AutoUpdate=False,
30
+ CooldownTime=20,AllowedFreq=8,CleaningFreq=80,ResetTime=8,
31
+ UpdateFreq=80)->int:
32
+ # if isinstance(ipaddress,str):
33
+ # self.ip=int(ipaddress.ip_address(IP_Adrs))
34
+ # else:print("Here")
35
+ # self.ip=IP_Adrs
36
+ self.AutoUpdate=AutoUpdate
37
+ self.Metrics={
38
+ "CooldownTime":CooldownTime,
39
+ "AllowedFreq":AllowedFreq,
40
+
41
+ }
42
+ # background_thread = threading.Thread(target=self.hehe, args=(start_val,), daemon=True)
43
+ if (Cleaning or (AutoUpdate)) and not self.thread_running:
44
+ try:
45
+ self.thread_running=True
46
+ BackgroundThread=threading.Thread(
47
+
48
+ target=self.__BackgroundWorker,
49
+ args=(CleaningFreq,ResetTime,UpdateFreq,Cleaning,),
50
+ daemon=False)
51
+ BackgroundThread.start()
52
+ except Exception as e:
53
+ print("Erro reached")
54
+ Cleaning=False
55
+ AutoUpdate=False
56
+ return self.__Validator(ip=IP_Adrs)
57
+ def __Fileopener(self)->int:
58
+ if getattr(self, "__isfileopen", False):
59
+ return 0
60
+ data=dict()
61
+ fullpath=self.fullpath
62
+ ##Sql implementation here now
63
+ try:
64
+ self.CurrentFile=f"{self.fullpath}"
65
+ self.__isfileopen=True
66
+ self.connection=sqlite3.connect(self.CurrentFile,check_same_thread=False,
67
+ timeout=10.0)
68
+ self.cursor=self.connection.cursor()
69
+ self.cursor.execute('''
70
+ create table if not exists UserIps(
71
+ IP Text primary key,
72
+ Data TEXT
73
+ )''') #Table is created
74
+ self.cursor.execute("SELECT IP, Data FROM UserIps")#Get the data
75
+ self.Data={row[0]: json.loads(row[1]) for row in self.cursor.fetchall()}
76
+ self.__isfileopen=False
77
+ self.connection.commit()
78
+
79
+ except Exception as e:
80
+ print(e)
81
+ return 0
82
+ return 1
83
+ def __Filedumper(self,operation=0,Data=None,update=1)->int:
84
+ # print(Data)
85
+ # if msg is not None: print(msg[0])
86
+ if (update & self.AutoUpdate) :
87
+ return 1
88
+ try:
89
+ with self.lock:
90
+ # if msg is not None: print(msg[1])
91
+ fullpath=self.fullpath
92
+ flag=0
93
+
94
+ if Data is None:
95
+ Data={}
96
+
97
+ try:
98
+ #Write the data
99
+ toupdate=list()
100
+ while self.Changes:
101
+ values=self.Changes.pop()
102
+ datatodump=json.dumps(self.Data[values])
103
+ toupdate.append((values,datatodump))
104
+ if toupdate:
105
+ self.cursor.executemany("INSERT OR REPLACE INTO UserIps (IP, Data) VALUES (?, ?)", toupdate)
106
+ self.connection.commit()
107
+ flag=1
108
+
109
+ except Exception as e:
110
+ try :
111
+ with open("Log.txt",'a') as file:
112
+ record=f"{time.time()}:Error is {e}\n"
113
+ file.write(record)
114
+
115
+ flag=0
116
+ except Exception as err:
117
+ flag=0
118
+
119
+ if operation==1:
120
+ self.CurrentFile.close()
121
+ self.__isfileopen=False
122
+ return 1
123
+ else:
124
+ self.__isfileopen=True
125
+ return flag
126
+ except Exception as e:
127
+ with open("Log.txt",'a') as file:
128
+ record=f"{time.time()}:Error is {e}\n"
129
+ file.write(record)
130
+
131
+
132
+
133
+ pass
134
+ def __Validator(self,ip:int)->int:
135
+
136
+ currenttime=int(time.time())
137
+ error=False
138
+ flag=1
139
+ with self.lock:
140
+ try:
141
+ lastseen=currenttime-self.Data[ip]["LastSeenTime"]
142
+ except KeyError:
143
+
144
+ self.Data[ip]={
145
+ # "WaitTime":0,
146
+ "WaitStamp":0,
147
+ "LastSeenTime":currenttime,
148
+ "Visits":1
149
+ }
150
+ error=True
151
+ self.Data[ip]["LastSeenTime"]=currenttime
152
+ if error is False:
153
+ if lastseen>self.Metrics["CooldownTime"]:
154
+ # self.Data[ip]["WaitTime"]=0
155
+ self.Data[ip]["WaitStamp"]=0
156
+ self.Data[ip]["Visits"]=1
157
+ flag= 1
158
+ else:
159
+ Datacopy=copy.deepcopy(self.Data)
160
+ self.Data[ip],flag= self.__RecentVists(Data=Datacopy[ip],CrnTime=currenttime)
161
+
162
+ self.__Filedumper(Data=self.Data)
163
+ self.Changes.add(ip)
164
+ return (flag or error)
165
+ def __RecentVists(self,Data,CrnTime)->tuple: #Solution For DeadLock
166
+
167
+
168
+ if Data["Visits"]<=self.Metrics["AllowedFreq"]:
169
+ Data["WaitStamp"]=0
170
+ Data["Visits"]+=1
171
+ return (Data,1)
172
+ # timetowait=0
173
+ if Data["WaitStamp"]==0:
174
+ Data["WaitStamp"]=CrnTime+self.Metrics["CooldownTime"]
175
+ Data["Visits"]+=1
176
+ return (Data,self.Metrics["CooldownTime"])
177
+ else:
178
+ timetosend=Data["WaitStamp"]-CrnTime
179
+ Data["Visits"]+=1
180
+ if timetosend<=0:
181
+ Data["WaitStamp"]=0
182
+ Data["Visits"]=0
183
+ timetosend=1
184
+
185
+ return (Data,timetosend)
186
+ def __BackgroundWorker(self,ClnFrq=100,restlimit=80,UpdateFreq=80,Cleaning=False):
187
+
188
+ self.thread_running=True
189
+ CleanData={}
190
+ Slptime=self.__Timecalculator(ClnFrq,UpdateFreq)
191
+ while not self.stop_event.is_set():
192
+ # print("Inside the Loop")
193
+ # self.stop_event.wait(ClnFrq)
194
+ if self.stop_event.is_set():
195
+ break
196
+ #here write about the clenaing freq
197
+ self.__Filedumper(Data=self.Data,update=0)
198
+ if Cleaning:
199
+
200
+ currenttime=int(time.time())
201
+ try:
202
+ query="""
203
+ DELETE FROM UserIps
204
+ WHERE ? - json_extract(Data, '$.LastSeenTime') > ?
205
+ RETURNING IP
206
+ """
207
+ self.cursor.execute(query,(currenttime,restlimit))
208
+ deleted_rows = self.cursor.fetchall()
209
+ if deleted_rows:
210
+ self.connection.commit()
211
+ with self.lock:
212
+ for row in deleted_rows:
213
+ deleted_ip=row[0]
214
+ self.Data.pop(deleted_ip,None)
215
+ # except sqlite3.OperationalError as e:
216
+ # #"""THis is the no coloum found error wont be that expsenive and also it will spoil the log.txt so
217
+ # # it is better now to log this error
218
+ # pass
219
+ except Exception as e:
220
+ with open("Log.txt",'a') as file:
221
+ record=f"{time.time()}:Error is {e}\n"
222
+ file.write(record)
223
+ # print("Sleep")
224
+
225
+ # print("Loop End")
226
+ time.sleep(Slptime)
227
+ self.thread_running = False
228
+ def __Timecalculator(self,CleaningFrq,UpdateFreq)->int:
229
+ #Need to create a proper time calcuator here
230
+ return CleaningFrq
231
+
232
+ __Rl=__RateLimiter()
233
+ def Ratelimiter(IP_Adrs:str,Cleaning=False,AutoUpdate=False,
234
+ CooldownTime=20,AllowedFreq=8,CleaningFreq=80,ResetTime=8,UpdateFreq=8,Filename=None,FolderPath=None)->int:
235
+ global __Rl
236
+ if (Filename is not None) and (FolderPath is not None):
237
+ __Rl = __RateLimiter(filename=Filename, folder=FolderPath)
238
+
239
+ # def API_RL(self,IP_Adrs:str,Cleaning=False,AutoUpdate=False,
240
+ # CooldownTime=20,AllowedFreq=8,CleaningFreq=80,ResetTime=8,
241
+ # UpdateFreq=80)
242
+
243
+ return (__Rl.API_RL(IP_Adrs=IP_Adrs,Cleaning=Cleaning,AutoUpdate=AutoUpdate,
244
+ CooldownTime=CooldownTime,AllowedFreq=AllowedFreq,CleaningFreq=CleaningFreq,ResetTime=ResetTime,UpdateFreq=UpdateFreq))
245
+
246
+
247
+ if __name__=="__main__":
248
+
249
+
250
+ v=Ratelimiter("127.0.0.2",Cleaning=0,CleaningFreq=1,CooldownTime=7)
251
+ print(v)
252
+
253
+
254
+
255
+
256
+
257
+
258
+
@@ -0,0 +1,3 @@
1
+ from .ratelimiter import ratelimiter
2
+
3
+ __all__=["ratelimiter"]
@@ -0,0 +1,38 @@
1
+ from pathlib import Path
2
+ import sys
3
+ __isfileopen :bool = False
4
+ ___samplefile = Path(sys.modules['__main__'].__file__).stem
5
+ def ratelimiter(IP_Adrs:str,Cleaning=False,AutoUpdate=False,
6
+ CooldownTime=20,AllowedFreq=8,CleaningFreq=80,ResetTime=8,UpdateFreq=8,Filename=___samplefile,FolderPath=None,Format=None)->int:
7
+ if Format is None:
8
+ from .ARL_nostorage import Ratelimiter as Rl
9
+ return Rl(IP_Adrs=IP_Adrs,Cleaning=Cleaning,CleaningFreq=CleaningFreq,AllowedFreq=AllowedFreq,ResetTime=ResetTime)
10
+ if (isinstance(Format,str) is False):
11
+ raise TypeError("Only String Option is Availabe for Format Option")
12
+ elif (Format.lower())=="json":
13
+ from .ARL_json import Ratelimiter as Rl
14
+ return Rl(
15
+ IP_Adrs=IP_Adrs,Cleaning=Cleaning,AutoUpdate=AutoUpdate,
16
+ CooldownTime=CooldownTime,AllowedFreq=AllowedFreq,CleaningFreq=CleaningFreq,ResetTime=ResetTime,
17
+ UpdateFreq=UpdateFreq,Filename=Filename,FolderPath=FolderPath)
18
+ elif Format.lower()=="sql" :
19
+ from .ARL_sql import Ratelimiter as Rl
20
+ return Rl(
21
+ IP_Adrs=IP_Adrs,Cleaning=Cleaning,AutoUpdate=AutoUpdate,
22
+ CooldownTime=CooldownTime,AllowedFreq=AllowedFreq,CleaningFreq=CleaningFreq,ResetTime=ResetTime,
23
+ UpdateFreq=UpdateFreq,Filename=Filename,FolderPath=FolderPath)
24
+ else :
25
+ raise TypeError("""
26
+ Format Extensions available here
27
+ is not valid please provide a valid extension,
28
+ Availabe : Sql,json,Nostorage
29
+ Format : json,sql ,(Python (None))
30
+ """)
31
+
32
+ if __name__=="__main__":
33
+ options=[None,"json","sql"]
34
+ ip="111"
35
+
36
+ for i in options:
37
+ ARL(Format=i,IP_Adrs=ip)
38
+ print(f"Format:{i}")
@@ -0,0 +1,55 @@
1
+ Metadata-Version: 2.4
2
+ Name: apirlpy
3
+ Version: 0.0.4
4
+ Summary: A Python Based API RateLimiter
5
+ Author-email: Shaik Naseer John Ahmed <sknaseer.fez@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Naseer-fez/Apirl-py
8
+ Project-URL: Issues, https://github.com/Naseer-fez/Apirl-py/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.4
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE.txt
14
+ Dynamic: license-file
15
+
16
+ ## How to Run
17
+ ```python
18
+ from apirlpy import ratelimiter as APIRL
19
+ #Import the Function
20
+
21
+ # Checking rate limit for a specific IP
22
+ CoolDown=APIRL(IP_Adrs="127.0.0.1", #IP address ofthe Client
23
+ #_______________________________________________________________
24
+ Cleaning=False, #To Enable the Ip Address Cleaning
25
+ CleaningFreq=80,# The Frequency at which the cleaning shoudl occur
26
+ AutoUpdate=False, #If you want to update the Data only after a certain time to reduce I/O operations
27
+ UpdateFreq=70, #(value in sec) of auto update
28
+ CooldownTime=20, #Amount of time differnce the Clients last vist shoudld
29
+ # exced for the IP address cleaner to remove the data
30
+ # *** FOR JSON AND SQL Format only ***
31
+ #If passed to "no stograge" option then this arguments will not be passed to the ratelimiter
32
+ #_______________________________________________________________
33
+ AllowedFreq=8, #The Number of Fair attempts the Client can send the request to the server
34
+ ResetTime=8, # After the Client is denied the access , how much time
35
+ #they need to wait to get the access again
36
+ Filename="Test", # The file name to store the data
37
+ Format="sql", # The File extenstion type which the ratelimiter is going to use to store the data
38
+ # Extensions available are : sql to use sqllite , json to use json format and "None" for No storage option
39
+ # If nothing is mentioned then the data will not be stored
40
+ FolderPath=None # The path at which u want to store this results
41
+
42
+ )
43
+ if CoolDown == 1: #The Client can access the API
44
+ print( "You are a Valid user")
45
+ else: # the Request is denied
46
+ print( f"You need to wait {CoolDown}")
47
+ ```
48
+ **This is a test code **
49
+ ### The Code Return
50
+ - 1 : Means the Client Can Access the API
51
+ - other than 1: Means the Client Need to wait for the access of the API
52
+ ___
53
+
54
+
55
+
@@ -0,0 +1,12 @@
1
+ LICENSE.txt
2
+ README.md
3
+ pyproject.toml
4
+ src/apirlpy/ARL_json.py
5
+ src/apirlpy/ARL_nostorage.py
6
+ src/apirlpy/ARL_sql.py
7
+ src/apirlpy/__init__.py
8
+ src/apirlpy/ratelimiter.py
9
+ src/apirlpy.egg-info/PKG-INFO
10
+ src/apirlpy.egg-info/SOURCES.txt
11
+ src/apirlpy.egg-info/dependency_links.txt
12
+ src/apirlpy.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ apirlpy