or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

case-insensitive.mdimmutable-proxies.mdindex.mdmutable-multidict.mdstring-types-utilities.md

string-types-utilities.mddocs/

0

# String Types and Utilities

1

2

Multidict provides specialized string types and utility functions to support case-insensitive operations and multidict management. These components enable advanced functionality like version tracking and custom string handling.

3

4

## Capabilities

5

6

### Case-Insensitive String Type

7

8

The `istr` class provides case-insensitive string functionality for use with multidict keys and general string operations.

9

10

```python { .api }

11

class istr(str):

12

"""

13

Case-insensitive string subclass.

14

15

Behaves like regular strings but compares equal regardless of case.

16

Used internally by CIMultiDict and CIMultiDictProxy for key handling.

17

"""

18

19

def __new__(cls, val=''):

20

"""

21

Create a case-insensitive string.

22

23

Parameters:

24

- val: String value to convert (can be str, bytes, or other objects)

25

26

Returns:

27

New istr instance

28

"""

29

30

# Special attributes for internal use

31

__is_istr__: bool = True

32

__istr_identity__: Optional[str] = None

33

```

34

35

Usage examples:

36

37

```python

38

from multidict import istr

39

40

# Create case-insensitive strings

41

s1 = istr('Content-Type')

42

s2 = istr('content-type')

43

s3 = istr('CONTENT-TYPE')

44

45

# All compare equal regardless of case

46

print(s1 == s2) # True

47

print(s2 == s3) # True

48

print(s1 == s3) # True

49

50

# But regular string comparison still case-sensitive

51

print(str(s1) == str(s2)) # False (different case)

52

53

# Original case is preserved

54

print(str(s1)) # 'Content-Type'

55

print(str(s2)) # 'content-type'

56

57

# Can be used with regular strings too

58

print(s1 == 'content-type') # True

59

print(s1 == 'CONTENT-TYPE') # True

60

```

61

62

### String Type Detection

63

64

Check if a string is a case-insensitive string type.

65

66

```python

67

from multidict import istr

68

69

regular_str = 'Hello'

70

case_insensitive_str = istr('Hello')

71

72

# Check string type

73

print(hasattr(regular_str, '__is_istr__')) # False

74

print(hasattr(case_insensitive_str, '__is_istr__')) # True

75

print(case_insensitive_str.__is_istr__) # True

76

77

# Type checking

78

print(isinstance(case_insensitive_str, str)) # True (is a string)

79

print(isinstance(case_insensitive_str, istr)) # True (is istr)

80

print(type(case_insensitive_str).__name__) # 'istr'

81

```

82

83

### Manual Case-Insensitive Operations

84

85

Using `istr` for custom case-insensitive operations outside of multidict.

86

87

```python

88

from multidict import istr

89

90

# Case-insensitive set operations

91

headers_seen = set()

92

for header_name in ['Content-Type', 'content-length', 'ACCEPT']:

93

istr_header = istr(header_name)

94

if istr_header not in headers_seen:

95

headers_seen.add(istr_header)

96

print(f"New header: {header_name}")

97

98

# Case-insensitive dictionary keys (manual approach)

99

class CaseInsensitiveDict(dict):

100

def __setitem__(self, key, value):

101

super().__setitem__(istr(key), value)

102

103

def __getitem__(self, key):

104

return super().__getitem__(istr(key))

105

106

def __contains__(self, key):

107

return super().__contains__(istr(key))

108

109

ci_dict = CaseInsensitiveDict()

110

ci_dict['Content-Type'] = 'text/html'

111

print(ci_dict['content-type']) # 'text/html'

112

```

113

114

### Version Tracking Utility

115

116

The `getversion` function provides access to internal version numbers for change detection and optimization.

117

118

```python { .api }

119

def getversion(md: Union[MultiDict[object], MultiDictProxy[object]]) -> int:

120

"""

121

Get the internal version number of a multidict.

122

123

Parameters:

124

- md: MultiDict or MultiDictProxy instance

125

126

Returns:

127

Integer version number that increments with each modification

128

129

Raises:

130

TypeError if parameter is not a multidict or proxy

131

132

The version number is used internally for view invalidation and can be

133

useful for caching and change detection in applications.

134

"""

135

```

136

137

Usage examples:

138

139

```python

140

from multidict import MultiDict, MultiDictProxy, getversion

141

142

# Track changes with version numbers

143

headers = MultiDict([('Accept', 'text/html')])

144

initial_version = getversion(headers)

145

print(f"Initial version: {initial_version}")

146

147

# Version increments with modifications

148

headers.add('User-Agent', 'MyApp/1.0')

149

after_add_version = getversion(headers)

150

print(f"After add: {after_add_version}") # Higher number

151

152

headers['Accept'] = 'application/json'

153

after_set_version = getversion(headers)

154

print(f"After set: {after_set_version}") # Even higher

155

156

# Proxy has same version as underlying multidict

157

proxy = MultiDictProxy(headers)

158

print(f"Proxy version: {getversion(proxy)}") # Same as headers

159

160

# Use for change detection

161

cached_version = getversion(headers)

162

cached_data = headers.copy()

163

164

# Later... check if data changed

165

if getversion(headers) != cached_version:

166

print("Headers changed, refresh cache")

167

cached_data = headers.copy()

168

cached_version = getversion(headers)

169

```

170

171

### Version-Based Caching Pattern

172

173

Common pattern for implementing efficient caching with version tracking.

174

175

```python

176

from multidict import MultiDict, getversion

177

178

class CachedHeaderProcessor:

179

def __init__(self):

180

self._cache = {}

181

self._cache_versions = {}

182

183

def process_headers(self, headers: MultiDict) -> str:

184

"""

185

Process headers with caching based on version tracking.

186

187

Parameters:

188

- headers: MultiDict to process

189

190

Returns:

191

Processed header string

192

"""

193

headers_id = id(headers)

194

current_version = getversion(headers)

195

196

# Check if we have cached result for this version

197

if (headers_id in self._cache and

198

self._cache_versions.get(headers_id) == current_version):

199

return self._cache[headers_id]

200

201

# Process headers (expensive operation)

202

result = self._expensive_processing(headers)

203

204

# Cache result with version

205

self._cache[headers_id] = result

206

self._cache_versions[headers_id] = current_version

207

208

return result

209

210

def _expensive_processing(self, headers: MultiDict) -> str:

211

# Simulate expensive processing

212

processed_items = []

213

for key in headers:

214

values = headers.getall(key)

215

processed_items.append(f"{key}: {', '.join(values)}")

216

return '\n'.join(processed_items)

217

218

# Usage

219

processor = CachedHeaderProcessor()

220

headers = MultiDict([('Accept', 'text/html'), ('User-Agent', 'MyApp/1.0')])

221

222

# First call - processes and caches

223

result1 = processor.process_headers(headers) # Expensive processing

224

225

# Second call - returns cached result

226

result2 = processor.process_headers(headers) # Fast cached lookup

227

228

# Modify headers - version changes

229

headers.add('Accept', 'application/json')

230

231

# Third call - detects change and reprocesses

232

result3 = processor.process_headers(headers) # Expensive processing again

233

```

234

235

### Deprecated upstr Alias

236

237

The `upstr` alias is maintained for backward compatibility but is deprecated.

238

239

```python { .api }

240

upstr = istr # Deprecated alias for istr

241

```

242

243

Usage guidance:

244

245

```python

246

from multidict import istr, upstr

247

248

# Preferred - use istr directly

249

preferred = istr('Content-Type')

250

251

# Deprecated - upstr is an alias for istr

252

deprecated = upstr('Content-Type') # Same as istr('Content-Type')

253

254

# Both work the same way

255

print(preferred == deprecated) # True

256

257

# But use istr in new code

258

recommended_usage = istr('header-name')

259

```

260

261

### Integration with Standard Library

262

263

`istr` integrates seamlessly with Python's standard library while maintaining case-insensitive behavior.

264

265

```python

266

from multidict import istr

267

import re

268

269

# Works with regular expressions

270

pattern = re.compile(r'content-.*', re.IGNORECASE)

271

header = istr('Content-Type')

272

273

# String methods work normally

274

print(header.lower()) # 'content-type'

275

print(header.upper()) # 'CONTENT-TYPE'

276

print(header.title()) # 'Content-Type'

277

278

# But comparisons are still case-insensitive

279

print(header == 'CONTENT-TYPE') # True

280

281

# Hashing is case-insensitive

282

header_set = {istr('Content-Type'), istr('content-type')}

283

print(len(header_set)) # 1 (treated as same key)

284

285

# But regular strings hash differently

286

mixed_set = {'Content-Type', 'content-type', istr('CONTENT-TYPE')}

287

print(len(mixed_set)) # 2 (regular strings hash differently)

288

```