Coverage for pass_import/__init__.py: 96%
78 statements
« prev ^ index » next coverage.py v7.4.3, created at 2024-02-26 12:11 +0000
« prev ^ index » next coverage.py v7.4.3, created at 2024-02-26 12:11 +0000
1# -*- encoding: utf-8 -*-
2# pass import - Passwords importer swiss army knife
3# Copyright (C) 2017-2024 Alexandre PUJOL <alexandre@pujol.io>.
4#
5"""Passwords importer swiss army knife."""
7from collections import OrderedDict, defaultdict
8from typing import Callable, List, Dict, Union, Generator
10import pass_import.decrypters # noqa
11import pass_import.formats # noqa
12import pass_import.managers # noqa
13from pass_import.__about__ import (__author__, __copyright__, __email__,
14 __license__, __summary__, __title__,
15 __uri__, __version__)
16from pass_import.core import Cap, get_detecters, get_managers
18__all__ = [
19 '__title__', '__summary__', '__uri__', '__version__', '__author__',
20 '__email__', '__license__', '__copyright__'
21]
24class ManagerError(Exception):
25 """Errors related to managers' management. Most likely a bug if raised."""
28class Managers(set):
29 """Provide an interface to manage the managers' classes easily."""
31 def __init__(self):
32 super().__init__(get_managers())
34 def classes(self, cap=Cap.IMPORT, frmt=None) -> Generator:
35 """Generate the classes of pm with capabilities and format."""
36 ignore = {'csv'}
37 for pm in self:
38 if cap in pm.cap:
39 if frmt:
40 if pm.name in ignore:
41 continue
42 if pm.format == frmt:
43 yield pm
44 else:
45 yield pm
47 def get(self, name, frmt='', version='', cap=Cap.IMPORT
48 ) -> Union[Callable, None]:
49 """Return a manager class from its classname or its format."""
50 default = None
51 for pm in self.classes(cap):
52 # If name is a classname, return the class
53 if pm.__name__ == name:
54 return pm
56 # If name is a password manager name, check its metadata
57 if pm.name == name:
58 if not default:
59 default = pm
60 if pm.format == frmt and pm.version == version:
61 return pm
63 if default:
64 return default
65 raise ManagerError(f'Unknown password manager: {name}')
67 def clsnames(self, cap=Cap.IMPORT) -> List[str]:
68 """Return the sorted list of password managers classes name."""
69 names = set()
70 for pm in self.classes(cap):
71 names.add(pm.__name__)
72 return sorted(names)
74 def names(self, cap=Cap.IMPORT) -> List[str]:
75 """Return the sorted list of password managers name."""
76 names = set()
77 for pm in self.classes(cap):
78 names.add(pm.name)
79 return sorted(names)
81 def matrix(self, cap=Cap.IMPORT) -> Dict[str, List[Callable]]:
82 """Return a dict of ordered managers classes and formats.
84 :return dict matrix:
85 { name: [pm_1, pm_2, ..., pm_n] } such as pm1 is the dedault pm and
86 the other pm are ordered by they format.
87 """
88 umatrix = defaultdict(list) # unordered matrix
89 for pm in self.classes(cap):
90 umatrix[pm.name].append(pm)
92 matrix = defaultdict(list)
93 for name in umatrix:
94 formats = []
95 default = None
96 for pm in umatrix[name]:
97 if pm.default:
98 default = pm
99 else:
100 formats.append(pm)
102 formats.sort(key=lambda x: x.format)
103 formats.insert(0, default)
104 matrix[name] = formats
105 return matrix
108class Detecters(OrderedDict):
109 """An ordered dictionary of the password managers format supported.
111 This format dictionary is ordered to take care of the following
112 requirements:
114 - Most common format first
115 - Parent format first. Eg: ``XML`` before ``HTML``,
116 ``JSON`` before ``YAML``...
118 """
119 orders = {
120 Cap.FORMAT: [
121 'csv', 'xml', 'json', 'kdbx', 'yaml', '1pif', 'html', 'keychain'
122 ],
123 Cap.DECRYPT: []
124 }
126 def __init__(self, cap=Cap.FORMAT):
127 self.cap = cap
128 if self.cap not in Cap.FORMAT | Cap.DECRYPT:
129 raise ManagerError('Capability not supported')
131 cls = get_detecters()
132 detecters = OrderedDict()
133 for frmt in self.orders[cap]:
134 for pm in cls:
135 if pm.format == frmt and cap in pm.cap:
136 detecters[pm.format] = pm
138 for pm in cls:
139 if pm.format not in self.orders[cap] and cap in pm.cap:
140 detecters[pm.format] = pm
141 super().__init__(detecters)