13
13
import hashlib
14
14
import io
15
15
import os
16
- import socket
17
16
import sqlite3
18
17
import sys
19
18
import threading
20
19
import time
20
+ import uuid
21
21
from dataclasses import dataclass
22
22
from functools import cached_property
23
23
from pathlib import Path
@@ -49,6 +49,17 @@ class CLISessionDatabaseConnection:
49
49
timestamp INTEGER NOT NULL
50
50
)
51
51
"""
52
+ _CREATE_HOST_ID_TABLE = """
53
+ CREATE TABLE IF NOT EXISTS host_id (
54
+ key INTEGER PRIMARY KEY,
55
+ id TEXT UNIQUE NOT NULL
56
+ )
57
+ """
58
+ _INSERT_HOST_ID = """
59
+ INSERT OR IGNORE INTO host_id (
60
+ key, id
61
+ ) VALUES (?, ?)
62
+ """
52
63
_ENABLE_WAL = 'PRAGMA journal_mode=WAL'
53
64
54
65
def __init__ (self , connection = None ):
@@ -69,12 +80,28 @@ def execute(self, query, *parameters):
69
80
return sqlite3 .Cursor (self ._connection )
70
81
71
82
def _ensure_database_setup (self ):
72
- self ._create_record_table ()
83
+ self ._create_session_table ()
84
+ self ._create_host_id_table ()
85
+ self ._ensure_host_id ()
73
86
self ._try_to_enable_wal ()
74
87
75
- def _create_record_table (self ):
88
+ def _create_session_table (self ):
76
89
self .execute (self ._CREATE_TABLE )
77
90
91
+ def _create_host_id_table (self ):
92
+ self .execute (self ._CREATE_HOST_ID_TABLE )
93
+
94
+ def _ensure_host_id (self ):
95
+ self .execute (
96
+ self ._INSERT_HOST_ID ,
97
+ # Hardcode `0` as primary key to ensure
98
+ # there's only ever 1 host id in the table.
99
+ (
100
+ 0 ,
101
+ str (uuid .uuid4 ()),
102
+ ),
103
+ )
104
+
78
105
def _try_to_enable_wal (self ):
79
106
try :
80
107
self .execute (self ._ENABLE_WAL )
@@ -111,6 +138,11 @@ class CLISessionDatabaseReader:
111
138
FROM session
112
139
WHERE key = ?
113
140
"""
141
+ _READ_HOST_ID = """
142
+ SELECT id
143
+ FROM host_id
144
+ WHERE key = 0
145
+ """
114
146
115
147
def __init__ (self , connection ):
116
148
self ._connection = connection
@@ -122,6 +154,10 @@ def read(self, key):
122
154
return
123
155
return CLISessionData (* result )
124
156
157
+ def read_host_id (self ):
158
+ cursor = self ._connection .execute (self ._READ_HOST_ID )
159
+ return cursor .fetchone ()[0 ]
160
+
125
161
126
162
class CLISessionDatabaseSweeper :
127
163
_DELETE_RECORDS = """
@@ -142,11 +178,11 @@ def sweep(self, timestamp):
142
178
143
179
144
180
class CLISessionGenerator :
145
- def generate_session_id (self , hostname , tty , timestamp ):
146
- return self ._generate_md5_hash (hostname , tty , timestamp )
181
+ def generate_session_id (self , host_id , tty , timestamp ):
182
+ return self ._generate_md5_hash (host_id , tty , timestamp )
147
183
148
- def generate_cache_key (self , hostname , tty ):
149
- return self ._generate_md5_hash (hostname , tty )
184
+ def generate_cache_key (self , host_id , tty ):
185
+ return self ._generate_md5_hash (host_id , tty )
150
186
151
187
def _generate_md5_hash (self , * args ):
152
188
str_to_hash = ""
@@ -167,12 +203,12 @@ def __init__(self, generator, writer, reader, sweeper):
167
203
168
204
@cached_property
169
205
def cache_key (self ):
170
- return self ._generator .generate_cache_key (self ._hostname , self ._tty )
206
+ return self ._generator .generate_cache_key (self ._host_id , self ._tty )
171
207
172
208
@cached_property
173
209
def _session_id (self ):
174
210
return self ._generator .generate_session_id (
175
- self ._hostname , self ._tty , self ._timestamp
211
+ self ._host_id , self ._tty , self ._timestamp
176
212
)
177
213
178
214
@cached_property
@@ -206,12 +242,12 @@ def _tty(self):
206
242
except (OSError , io .UnsupportedOperation ):
207
243
# Standard input was redirected to a pseudofile.
208
244
# This can happen when running tests on IDEs or
209
- # running scripts with redirected input.
245
+ # running scripts with redirected input, etc .
210
246
return
211
247
212
248
@cached_property
213
- def _hostname (self ):
214
- return socket . gethostname ()
249
+ def _host_id (self ):
250
+ return self . _reader . read_host_id ()
215
251
216
252
@cached_property
217
253
def _timestamp (self ):
0 commit comments