ducky-python-module 1.0.0__tar.gz → 1.0.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ducky-python-module
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: Ducky Python module
5
5
  Author: Tom
6
6
  License: Creative Commons Attribution-NonCommercial 4.0 International Public
@@ -345,7 +345,7 @@ Requires-Dist: numpy>=1.26.0
345
345
  Dynamic: license-file
346
346
 
347
347
  # Ducky-Python-Module
348
- A collection of tools for python
348
+ A collection of QOL and specialised tools for python
349
349
  Still in progress and will be releasing fully soon
350
350
  Credit:
351
351
  Turtle library has been used and modified. I did not make it and I did include the text from the original library explaining rights to the Turtle program which I am not allowed to remove.
@@ -1,5 +1,5 @@
1
1
  # Ducky-Python-Module
2
- A collection of tools for python
2
+ A collection of QOL and specialised tools for python
3
3
  Still in progress and will be releasing fully soon
4
4
  Credit:
5
5
  Turtle library has been used and modified. I did not make it and I did include the text from the original library explaining rights to the Turtle program which I am not allowed to remove.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ducky-python-module"
7
- version = "1.0.0"
7
+ version = "1.0.1" #changed version number already. Last pypi release uses 1.0.0
8
8
  description = "Ducky Python module"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -0,0 +1,2 @@
1
+ from .main import *
2
+ from .backend import set_backend
@@ -0,0 +1,16 @@
1
+ import numpy as _numpy
2
+ _backend = _numpy
3
+ def set_backend(name: str):
4
+ global _backend
5
+ if name == "numpy":
6
+ import numpy
7
+ _backend = numpy
8
+ elif name == "cupy":
9
+ import cupy
10
+ _backend = cupy
11
+
12
+ else:
13
+ raise ValueError("Backend must be numpy or cupy")
14
+
15
+ def backend():
16
+ return _backend
@@ -1,16 +1,16 @@
1
- import random, time, os, nltk, tkinter as TK, types, math, inspect, sys, requests, json, numpy, ast, logging
1
+ import random, time, os, nltk, tkinter as TK, types, math, inspect, sys, requests, json, ast, logging, abc
2
2
  from os.path import isfile, split, join
3
3
  from copy import deepcopy
4
4
  from tkinter import simpledialog
5
5
  from turtle import Turtle
6
6
  from nltk.tokenize import word_tokenize
7
+ from ducky.backend import backend
7
8
  from nltk.stem import LancasterStemmer
8
- logging.basicConfig(level=logging.INFO)
9
9
 
10
10
  #matrix functions
11
11
 
12
12
  def matrix(x,y,fill=0):
13
- mat = [[fill for i in range(x)] for a in range(y)]
13
+ mat = [[fill for _ in range(x)] for _ in range(y)]
14
14
  return mat
15
15
 
16
16
  def draw(mat,x,y,val=1,errors=True): #draws a certain value to all specified positions
@@ -72,7 +72,34 @@ def render(mat):
72
72
  print("------------------------------------------------------")
73
73
 
74
74
  #my functions
75
-
75
+
76
+ def download_nltk():
77
+ nltk.download('punkt', quiet=True)
78
+
79
+ def download_to_path(url, path, file_name, quiet=False):
80
+ try:
81
+ r = requests.get(url)
82
+ r.raise_for_status()
83
+
84
+ with open(f"{path}/{file_name}", "wb") as f:
85
+ f.write(r.content)
86
+
87
+ except requests.exceptions.RequestException as e:
88
+ if not quiet:
89
+ print("Download failed:", e)
90
+
91
+ def low_ram_download(url,path,file_name,quiet=False):
92
+ try:
93
+ with requests.get(url, stream=True) as r:
94
+ r.raise_for_status()
95
+ with open(f"{path}/{file_name}", "wb") as f:
96
+ for chunk in r.iter_content(chunk_size=8192):
97
+ if chunk:
98
+ f.write(chunk)
99
+ except requests.exceptions.RequestException as e:
100
+ if not quiet:
101
+ print("Download failed:", e)
102
+
76
103
  def rand_int(minimum,maximum):
77
104
  return random.randint(minimum,maximum)
78
105
 
@@ -91,7 +118,7 @@ def current_minute():
91
118
  def current_second():
92
119
  return time.localtime()[5]
93
120
 
94
- def tokenize(string):
121
+ def tokenise(string):
95
122
  tokens = word_tokenize(string)
96
123
  return tokens
97
124
 
@@ -100,16 +127,16 @@ def stem(string):
100
127
  return lancaster.stem(string)
101
128
 
102
129
  def array(lst):
103
- return numpy.array(lst)
130
+ return backend().array(lst)
104
131
 
105
132
  def dot_product(mat1,mat2):
106
- return numpy.dot(mat1,mat2)
133
+ return backend().dot(mat1,mat2)
107
134
 
108
135
  def scale_matrices(mat,val):
109
136
  return mat*val
110
137
 
111
138
  def average_matrices(mat):
112
- return numpy.average(mat)
139
+ return backend().average(mat)
113
140
 
114
141
  def calc(param):
115
142
  try:
@@ -397,7 +424,7 @@ class Chatbot:
397
424
 
398
425
  def reply(self,reply_input):
399
426
  score = [0, 0, 0, 0, 0]
400
- tokens = tokenize(reply_input)
427
+ tokens = tokenise(reply_input)
401
428
  stemmed_words = []
402
429
  for token in tokens:
403
430
  stemmed_words.append((stem(token)))
@@ -433,85 +460,48 @@ class Chatbot:
433
460
  return None
434
461
 
435
462
 
436
- #AI TEMPLATE
463
+ #AI class templates
437
464
 
438
- import mnist
439
- import numpy
440
- import abc
441
-
442
- class ActivationFunction:
443
- tanh = 0
444
- sigmoid = 1
445
- softmax = 2
446
- @staticmethod
447
- def __tanh(x: numpy.ndarray) -> numpy.ndarray:
448
- return numpy.tanh(x)
449
-
450
- @staticmethod
451
- def __tanh_derivative(x: numpy.ndarray) -> numpy.ndarray:
452
- return 1 - numpy.tanh(x) ** 2
453
465
 
454
- @staticmethod
455
- def use_tanh():
456
- return ActivationFunction.tanh, ActivationFunction.__tanh, ActivationFunction.__tanh_derivative
466
+ import abc
467
+ import random
457
468
 
458
- @staticmethod
459
- def __sigmoid(x):
460
- return 1 / (1 + numpy.exp(-x))
469
+ import ducky
470
+ import numpy
471
+ import numpy
461
472
 
462
- @staticmethod
463
- def __sigmoid_derivative(x):
464
- s = 1 / (1 + numpy.exp(-x))
465
- return s * (1 - s)
466
473
 
467
- @staticmethod
468
- def use_sigmoid():
469
- return ActivationFunction.sigmoid, ActivationFunction.__sigmoid, ActivationFunction.__sigmoid_derivative
474
+ class ActivationFunctions:
475
+ TANH = 0
470
476
 
471
477
  @staticmethod
472
- def __softmax(x):
473
- x = numpy.array(x)
474
- e_x = numpy.exp(x - numpy.max(x))
475
- return e_x / e_x.sum(axis=-1, keepdims=True)
478
+ def __tanh(x: numpy.ndarray):
479
+ return numpy.tanh(x)
476
480
 
477
481
  @staticmethod
478
- def __softmax_derivative(s):
479
- s = s.reshape(-1, 1)
480
- return numpy.diagflat(s) - numpy.dot(s, s.T)
482
+ def __tanh_derived(x: numpy.ndarray):
483
+ return 1 - (numpy.tanh(x) ** 2)
481
484
 
482
485
  @staticmethod
483
- def use_softmax():
484
- return ActivationFunction.softmax, ActivationFunction.__softmax, ActivationFunction.__softmax_derivative
486
+ def use_tanh():
487
+ return ActivationFunctions.TANH, ActivationFunctions.__tanh, ActivationFunctions.__tanh_derived
485
488
 
486
489
 
487
490
  class CostFunctions:
488
- mse = 0
489
- cross_entropy = 1
490
- @staticmethod
491
- def __mean_squared_error(actual: numpy.ndarray, predicted: numpy.ndarray):
492
- return numpy.mean(numpy.power((actual - predicted), 2))
493
-
494
- @staticmethod
495
- def __mean_squared_error_derivative(actual: numpy.ndarray, predicted: numpy.ndarray) -> numpy.ndarray:
496
- return 2 * (predicted - actual)/actual.size
491
+ MSE = 0
497
492
 
498
493
  @staticmethod
499
- def use_mean_squared_error():
500
- return CostFunctions.mse, CostFunctions.__mean_squared_error, CostFunctions.__mean_squared_error_derivative
494
+ def __mse(actual: numpy.ndarray, prediction: numpy.ndarray):
495
+ return numpy.mean(numpy.power(actual - prediction, 2))
501
496
 
502
497
  @staticmethod
503
- def __categorical_cross_entropy(actual: numpy.ndarray, predicted: numpy.ndarray):
504
- epsilon = 1e-12
505
- predicted = numpy.clip(predicted, epsilon, 1. - epsilon)
506
- return -numpy.mean(numpy.sum(actual * numpy.log(predicted), axis=1))
498
+ def __mse_derived(actual: numpy.ndarray, prediction: numpy.ndarray):
499
+ return (2 * (prediction - actual)) / actual.size
507
500
 
508
501
  @staticmethod
509
- def __categorical_cross_entropy_derivative(actual: numpy.ndarray, predicted: numpy.ndarray):
510
- return (predicted - actual) / actual.shape[0]
502
+ def use_mse():
503
+ return CostFunctions.MSE, CostFunctions.__mse, CostFunctions.__mse_derived
511
504
 
512
- @staticmethod
513
- def use_categorical_cross_entropy():
514
- return CostFunctions.cross_entropy, CostFunctions.__categorical_cross_entropy, CostFunctions.__categorical_cross_entropy_derivative
515
505
 
516
506
  class Layer(abc.ABC):
517
507
  def __init__(self):
@@ -519,133 +509,240 @@ class Layer(abc.ABC):
519
509
  self.output = None
520
510
 
521
511
  @abc.abstractmethod
522
- def feed_forward(self, inputs: numpy.ndarray) -> numpy.ndarray:
512
+ def feed_forward(self, input_values: numpy.ndarray):
523
513
  pass
524
514
 
525
515
  @abc.abstractmethod
526
- def back_propagate(self, error: numpy.ndarray, learning_rate: float) -> numpy.ndarray:
516
+ def back_propagate(self, output_error: numpy.ndarray, learning_rate: float):
527
517
  pass
528
518
 
519
+ @abc.abstractmethod
520
+ def copy(self):
521
+ pass
522
+
523
+
529
524
  class FullyConnectedLayer(Layer):
530
525
  def __init__(self, input_size: int, output_size: int):
531
- super().__init__()
526
+ Layer.__init__(self)
532
527
  random_bound = 1.0 / numpy.sqrt(input_size)
533
528
  self.weights = numpy.random.uniform(-random_bound, random_bound, (input_size, output_size))
534
529
  self.bias = numpy.zeros((1, output_size))
530
+ self.input_size = input_size
531
+ self.output_size = output_size
535
532
 
536
- def feed_forward(self, inputs: numpy.ndarray) -> numpy.ndarray:
537
- self.input = inputs
538
- self.output = numpy.dot(inputs, self.weights) + self.bias
533
+ def feed_forward(self, input_values):
534
+ self.input = input_values
535
+ self.output = numpy.dot(self.input, self.weights) + self.bias
539
536
  return self.output
540
537
 
541
-
542
- def back_propagate(self, error: numpy.ndarray, learning_rate: float) -> numpy.ndarray:
543
- input_error = numpy.dot(error, self.weights.T)
544
- weights_error = numpy.dot(self.input.T, error)
545
- self.weights -= learning_rate * weights_error
546
- self.bias -= learning_rate * numpy.mean(error, axis=0, keepdims=True)
538
+ def back_propagate(self, output_error, learning_rate):
539
+ input_error = numpy.dot(output_error, self.weights.T)
540
+ weights_error = numpy.dot(self.input.T, output_error)
541
+ self.weights -= (learning_rate * weights_error)
542
+ self.bias -= (learning_rate * numpy.mean(output_error, axis=0, keepdims=True))
547
543
  return input_error
548
544
 
545
+ def copy(self):
546
+ copied_layer = FullyConnectedLayer(self.input_size, self.output_size)
547
+ weights = self.weights.copy()
548
+ bias = self.bias.copy()
549
+ copied_layer.weights = weights
550
+ copied_layer.bias = bias
551
+ return copied_layer
552
+
549
553
 
550
554
  class ActivationLayer(Layer):
551
- def __init__(self, ID, activation_function, activation_derivative):
552
- super().__init__()
553
- self.ID = ID
554
- self.activation_function = activation_function
555
- self.activation_derivative = activation_derivative
556
-
557
- def feed_forward(self, inputs: numpy.ndarray) -> numpy.ndarray:
558
- self.input = inputs
559
- self.output = self.activation_function(inputs)
555
+ def __init__(self, activation_id, activation, activation_derived):
556
+ Layer.__init__(self)
557
+ self.activation_id = activation_id
558
+ self.activation = activation
559
+ self.activation_derived = activation_derived
560
+
561
+ def feed_forward(self, input_values):
562
+ self.input = input_values
563
+ self.output = self.activation(self.input)
560
564
  return self.output
561
565
 
562
- def back_propagate(self, error: numpy.ndarray, learning_rate: float) -> numpy.ndarray:
563
- if self.ID == ActivationFunction.softmax:
564
- return error
565
- else:
566
- return self.activation_derivative(self.input) * error
566
+ def back_propagate(self, output_error, learning_rate):
567
+ return self.activation_derived(self.input) * output_error
568
+
569
+ def copy(self):
570
+ copied_layer = ActivationLayer(self.activation_id, self.activation, self.activation_derived)
571
+ return copied_layer
572
+
567
573
 
568
574
  class NeuralNetwork:
569
- def __init__(self, ID, cost_function, cost_derivative):
570
- self.ID = ID
575
+ def __init__(self, cost_function_id, cost_function, cost_function_derived):
571
576
  self.layers = []
577
+ self.cost_function_id = cost_function_id
572
578
  self.cost_function = cost_function
573
- self.cost_derivative = cost_derivative
579
+ self.cost_function_derived = cost_function_derived
574
580
 
575
- def add_layer(self, layer: Layer):
581
+ def add_layer(self, layer):
576
582
  self.layers.append(layer)
577
583
 
578
- def predict(self, inputs: numpy.ndarray) -> numpy.ndarray:
584
+ def predict(self, input_data):
585
+ output = input_data
579
586
  for layer in self.layers:
580
- inputs = layer.feed_forward(inputs)
581
- return inputs
587
+ output = layer.feed_forward(output)
588
+ return output
582
589
 
583
- def train(self, inputs: numpy.ndarray, outputs: numpy.ndarray, n_epochs: int, batch_size: int, learning_rate: float):
590
+ def train(self, train_input, train_output, n_epochs, batch_size, learning_rate):
584
591
  for epoch in range(n_epochs):
585
592
  batch_index = 0
586
- while batch_index < inputs.shape[0]:
587
- if batch_index + batch_size < inputs.shape[0]:
588
- output = inputs[batch_index:batch_index + batch_size, ]
589
- actual = outputs[batch_index:batch_index + batch_size, ]
593
+ while batch_index < train_input.shape[0]:
594
+ if batch_index + batch_size < train_input.shape[0]:
595
+ output = train_input[batch_index:batch_index + batch_size, ]
596
+ actual = train_output[batch_index:batch_index + batch_size, ]
590
597
  else:
591
- output = inputs[batch_index:, ]
592
- actual = outputs[batch_index:, ]
593
- output = self.predict(output)
598
+ output = train_input[batch_index:, ]
599
+ actual = train_output[batch_index:, ]
600
+ for layer in self.layers:
601
+ output = layer.feed_forward(output)
594
602
  cost = self.cost_function(actual, output)
595
- error = self.cost_derivative(actual, output)
603
+ error = self.cost_function_derived(actual, output)
596
604
  for layer in reversed(self.layers):
597
605
  error = layer.back_propagate(error, learning_rate)
598
- print(f"Epoch: {epoch+1}/{n_epochs} | Batch: {batch_index}/{inputs.shape[0]} | Cost: {cost}")
606
+ print(f"Epoch {epoch + 1}/{n_epochs} | Batch = {(batch_index + 1) * batch_size} | Cost = {cost}")
599
607
  batch_index += batch_size
600
608
 
601
- def save(self, path: str):
602
- with open(f"{path}.nn", "w") as save_file:
603
- save_file.write(f"{self.ID}\n")
609
+ def save(self, file_path):
610
+ with open(f"{file_path}.nn", "w") as save_file:
611
+ save_file.write(f"{self.cost_function_id}\n")
604
612
  for index, layer in enumerate(self.layers):
605
613
  if isinstance(layer, FullyConnectedLayer):
606
- save_file.write(f"F|{index}|{layer.weights.shape[0]}|{layer.weights.shape[1]}\n")
607
- numpy.save(f"{path}_{index}_w", layer.weights)
608
- numpy.save(f"{path}_{index}_b", layer.bias)
614
+ save_file.write(f"{index}|F|{layer.weights.shape}|{layer.bias.shape}\n")
615
+ numpy.save(f"{file_path}_{index}_w", layer.weights)
616
+ numpy.save(f"{file_path}_{index}_b", layer.bias)
609
617
  elif isinstance(layer, ActivationLayer):
610
- if layer.ID == ActivationFunction.tanh:
611
- save_file.write(f"A|{index}|{ActivationFunction.tanh}\n")
612
- elif layer.ID == ActivationFunction.sigmoid:
613
- save_file.write(f"A|{index}|{ActivationFunction.sigmoid}\n")
614
- elif layer.ID == ActivationFunction.softmax:
615
- save_file.write(f"A|{index}|{ActivationFunction.softmax}\n")
616
- else:
617
- save_file.write(f"A|{index}|{ActivationFunction.tanh}\n")
618
+ save_file.write(f"{index}|A|{layer.activation_id}\n")
619
+
618
620
  @staticmethod
619
- def load(path: str):
620
- with open(f"{path}.nn", "r") as file:
621
- lines = file.readlines()
621
+ def load_from_file(file_path):
622
+ with open(f"{file_path}.nn", "r") as load_file:
623
+ lines = load_file.readlines()
622
624
  cost_function_id = int(lines[0])
623
- if cost_function_id == CostFunctions.mse:
624
- neural_network = NeuralNetwork(*CostFunctions.use_mean_squared_error())
625
+ if cost_function_id == CostFunctions.MSE:
626
+ neural_network = NeuralNetwork(*CostFunctions.use_mse())
625
627
  else:
626
- neural_network = NeuralNetwork(*CostFunctions.use_mean_squared_error())
628
+ neural_network = NeuralNetwork(*CostFunctions.use_mse())
627
629
  for line in lines[1:]:
628
630
  if line != "":
629
631
  parts = line.split("|")
630
- if parts[0] == "F":
631
- weights = numpy.load(f"{path}_{parts[1]}_w.npy")
632
- bias = numpy.load(f"{path}_{parts[1]}_b.npy")
632
+ if parts[1] == "F":
633
+ weights = numpy.load(f"{file_path}_{int(parts[0])}_w.npy")
634
+ bias = numpy.load(f"{file_path}_{int(parts[0])}_b.npy")
633
635
  layer = FullyConnectedLayer(weights.shape[0], weights.shape[1])
634
636
  layer.weights = weights
635
637
  layer.bias = bias
636
638
  neural_network.add_layer(layer)
637
- elif parts[0] == "A":
639
+ elif parts[1] == "A":
638
640
  activation_id = int(parts[2])
639
- if activation_id == ActivationFunction.tanh:
640
- neural_network.add_layer(ActivationLayer(*ActivationFunction.use_tanh()))
641
- elif activation_id == ActivationFunction.sigmoid:
642
- neural_network.add_layer(ActivationLayer(*ActivationFunction.use_sigmoid()))
643
- elif activation_id == ActivationFunction.softmax:
644
- neural_network.add_layer(ActivationLayer(*ActivationFunction.use_softmax()))
641
+ if activation_id == ActivationFunctions.TANH:
642
+ neural_network.add_layer(ActivationLayer(*ActivationFunctions.use_tanh()))
645
643
  else:
646
- neural_network.add_layer(ActivationLayer(*ActivationFunction.use_tanh()))
644
+ neural_network.add_layer(ActivationLayer(*ActivationFunctions.use_tanh()))
647
645
  return neural_network
648
646
 
647
+ def copy_network(self):
648
+ copied_network = NeuralNetwork(self.cost_function_id, self.cost_function, self.cost_function_derived)
649
+ for layer in self.layers:
650
+ copied_network.add_layer(layer.copy())
651
+ return copied_network
652
+
653
+
654
+ class Agent:
655
+ def __init__(self):
656
+ self.gamma = 0.99
657
+
658
+
659
+ class QTable:
660
+ def __init__(self, state, actions):
661
+ self.table = numpy.zeros((state,actions))
662
+ self.actions = actions
663
+ self.state = state
664
+
665
+ def update_table(self, state_row, action, value):
666
+ self.table[state_row, action] += value
667
+
668
+ def get_action(self, state_row):
669
+ return numpy.argmax(self.table[state_row])
670
+
671
+
672
+ class QTableAgent(Agent):
673
+ def __init__(self, table, num_possible_values):
674
+ Agent.__init__(self)
675
+ self.table = table
676
+ self.num_possible_values = num_possible_values
677
+ self.epsilon = 1
678
+
679
+ def state_to_row(self,state):
680
+ return int("".join([str(s) for s in state.astype(int)]), self.num_possible_values)
681
+
682
+ def predict(self, state):
683
+ self.epsilon *= 0.99
684
+ if random.random() > self.epsilon:
685
+ return self.table.get_action(self.state_to_row(state))
686
+ else:
687
+ return random.randint(0,self.table.actions-1)
688
+
689
+ def train(self, state, action, reward):
690
+ self.table.update_table(self.state_to_row(state), action, reward)
691
+
692
+
693
+ class DeepQAgent(Agent):
694
+ def __init__(self, layer_sizes):
695
+ Agent.__init__(self)
696
+ self.memory = Memory()
697
+ self.counter = 0
698
+ try:
699
+ self.neural_network = NeuralNetwork.load_from_file("deepq-nn")
700
+ except FileNotFoundError:
701
+ self.neural_network = NeuralNetwork(*CostFunctions.use_mse())
702
+ for i, size in enumerate(layer_sizes[:-1]):
703
+ self.neural_network.add_layer(FullyConnectedLayer(size, layer_sizes[i+1]))
704
+ self.neural_network.add_layer(ActivationLayer(*ActivationFunctions.use_tanh()))
705
+
706
+ def predict(self, state):
707
+ return int(self.neural_network.predict(state)[0].argmax())
708
+
709
+ def train(self, discount_rate):
710
+ training_input_data = []
711
+ training_output_data = []
712
+ for i in range(len(self.memory.states)):
713
+ prediction = self.neural_network.predict(self.memory.states[i])
714
+ prediction[0] += self.memory.rewards[i - 1]
715
+ if i > 0:
716
+ prediction[0] += self.memory.rewards[i-1] * discount_rate
717
+ training_input_data = numpy.array(training_input_data)
718
+ training_output_data = numpy.array(training_output_data)
719
+ self.neural_network.train(training_input_data, training_output_data, n_epochs=500, batch_size=10, learning_rate=0.1)
720
+ if self.counter >= 10:
721
+ self.neural_network.save("deepq-nn")
722
+ self.counter = 0
723
+ else:
724
+ self.counter += 1
725
+
726
+ def update_memory(self, state, action, reward):
727
+ self.memory.update(state, action, reward)
728
+
729
+
730
+ class Memory:
731
+ def __init__(self):
732
+ self.states = []
733
+ self.actions = []
734
+ self.rewards = []
735
+
736
+ def update(self, state, action, reward):
737
+ self.states.append(state)
738
+ self.actions.append(action)
739
+ self.rewards.append(reward)
740
+
741
+ def clear(self):
742
+ self.states = []
743
+ self.actions = []
744
+ self.rewards = []
745
+
649
746
 
650
747
  #turtlel
651
748
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ducky-python-module
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: Ducky Python module
5
5
  Author: Tom
6
6
  License: Creative Commons Attribution-NonCommercial 4.0 International Public
@@ -345,7 +345,7 @@ Requires-Dist: numpy>=1.26.0
345
345
  Dynamic: license-file
346
346
 
347
347
  # Ducky-Python-Module
348
- A collection of tools for python
348
+ A collection of QOL and specialised tools for python
349
349
  Still in progress and will be releasing fully soon
350
350
  Credit:
351
351
  Turtle library has been used and modified. I did not make it and I did include the text from the original library explaining rights to the Turtle program which I am not allowed to remove.
@@ -2,6 +2,7 @@ LICENSE.txt
2
2
  README.md
3
3
  pyproject.toml
4
4
  src/ducky/__init__.py
5
+ src/ducky/backend.py
5
6
  src/ducky/main.py
6
7
  src/ducky_python_module.egg-info/PKG-INFO
7
8
  src/ducky_python_module.egg-info/SOURCES.txt
@@ -1,7 +0,0 @@
1
- from .main import *
2
-
3
- try:
4
- import nltk
5
- nltk.download('punkt', quiet=True)
6
- except Exception:
7
- pass