Coverage for pass_import/managers/csv.py: 100%
51 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#
6import csv
7import os
9from pass_import.core import Cap, register_managers
10from pass_import.errors import FormatError, PMError
11from pass_import.formats.csv import CSV
12from pass_import.manager import PasswordExporter
15class GenericCSV(CSV, PasswordExporter):
16 """Importer & Exporter in generic CSV format.
18 :usage:
19 You should use the --cols option to map columns to credential attributes.
20 The recognized column names by pass-import are the following:
21 'title', 'password', 'login', 'email', 'url', 'comments',
22 'otpauth', 'group'
23 ``title`` and ``group`` field are used to generate the password
24 path. If you have otp data, they should be named as ``otpauth``.
25 These are the *standard* field names. You can add any other field
26 you want.
28 """
29 cap = Cap.IMPORT | Cap.EXPORT
30 name = 'csv'
31 himport = "pass import csv file.csv --cols 'url,login,,password'"
32 writer = None
34 # Import method
36 def parse(self):
37 """Parse Generic CSV file."""
38 self.file.readline()
39 if ',' in self.cols:
40 self.fieldnames = self.cols.split(',')
41 else:
42 raise FormatError("no columns to map to credential attributes.")
43 super().parse()
45 # Export methods
47 def exist(self):
48 """Nothing to do."""
49 return True
51 def clean(self, cmdclean, convert):
52 """Clean data for export in CSV a file."""
53 super().clean(cmdclean, convert)
54 fieldnames = set()
55 for entry in self.data:
56 path = entry.pop('path', '')
57 entry['group'] = os.path.join(self.root, os.path.dirname(path))
58 entry['title'] = os.path.basename(path)
59 fieldnames.update(set(entry.keys()))
61 if not self.all:
62 fieldnames = self.keyslist
63 for entry in self.data:
64 for key in fieldnames:
65 if key not in entry:
66 entry[key] = ''
67 self.writer = csv.DictWriter(self.file,
68 fieldnames=sorted(fieldnames),
69 restval='',
70 extrasaction='raise')
71 self.writer.writeheader()
73 def insert(self, entry):
74 """Insert a password entry into a CSV file.
76 :param dict entry: The password entry to insert.
78 If ``all`` is true, all the entry values are printed.
79 Otherwise, only the key present in ``keyslist`` are
80 printed following the order from this list. The title, path, and group
81 keys are ignored.
83 Binary attachment is not supported.
85 """
86 if self.all:
87 self.writer.writerow(entry)
88 else:
89 res = {}
90 for key in self.keyslist:
91 res[key] = entry.get(key, '')
92 self.writer.writerow(res)
94 # Context manager method
96 def open(self):
97 """Create/Re-create CSV exported file."""
98 if self.action is Cap.IMPORT:
99 super().open()
100 else:
101 if os.path.isfile(self.prefix):
102 if self.force:
103 self.file = open(self.prefix, 'w', encoding=self.encoding)
104 else:
105 raise PMError(f"{self.prefix} is already a file.")
106 else:
107 self.file = open(self.prefix, 'w', encoding=self.encoding)
110register_managers(GenericCSV)