Module: Pygments::Popen

Extended by:
Popen
Includes:
POSIX::Spawn
Included in:
Pygments, Popen
Defined in:
lib/pygments/popen.rb

Instance Method Summary collapse

Instance Method Details

#alive?Boolean

Check for a @pid variable, and then hit ‘kill -0` with the pid to check if the pid is still in the process table. If this function gives us an ENOENT or ESRCH, we can also safely return false (no process to worry about). Defensively, if EPERM is raised, in a odd/rare dying process situation (e.g., mentos is checking on the pid of a dead process and the pid has already been re-used) we’ll want to raise that as a more informative Mentos exception.

Returns true if the child is alive.

Returns:

  • (Boolean)


87
88
89
90
91
92
93
94
# File 'lib/pygments/popen.rb', line 87

def alive?
  return true if @pid && Process.kill(0, @pid)
  false
rescue Errno::ENOENT, Errno::ESRCH
  false
rescue Errno::EPERM
  raise MentosError, "EPERM checking if child process is alive."
end

#css(klass = '', opts = {}) ⇒ Object

Public: Return css for highlighted code



161
162
163
164
165
166
167
# File 'lib/pygments/popen.rb', line 161

def css(klass='', opts={})
  if klass.is_a?(Hash)
    opts = klass
    klass = ''
  end
  mentos(:css, ['html', klass], opts)
end

#filtersObject

Public: Return an array of all available filters



151
152
153
# File 'lib/pygments/popen.rb', line 151

def filters
  mentos(:get_all_filters)
end

#formattersObject

Public: Get an array of available Pygments formatters

Returns an array of formatters.



99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/pygments/popen.rb', line 99

def formatters
  mentos(:get_all_formatters).inject(Hash.new) do | hash, (name, desc, aliases) |
    # Remove the long-winded and repetitive 'Formatter' suffix
    name.sub!(/Formatter$/, '')
    hash[name] = {
      :name => name,
      :description => desc,
      :aliases => aliases
    }
    hash
  end
end

#highlight(code, opts = {}) ⇒ Object

Public: Highlight code.

Takes a first-position argument of the code to be highlighted, and a second-position hash of various arguments specifiying highlighting properties.



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/pygments/popen.rb', line 191

def highlight(code, opts={})
  # If the caller didn't give us any code, we have nothing to do,
  # so return right away.
  return code if code.nil? || code.empty?

  # Callers pass along options in the hash
  opts[:options] ||= {}

  # Default to utf-8 for the output encoding, if not given.
  opts[:options][:outencoding] ||= 'utf-8'

  # Get back the string from mentos and force encoding if we can
  str = mentos(:highlight, nil, opts, code)
  str.force_encoding(opts[:options][:outencoding]) if str.respond_to?(:force_encoding)
  str
end

#lexer_name_for(*args) ⇒ Object

Public: Return the name of a lexer.



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/pygments/popen.rb', line 170

def lexer_name_for(*args)
  # Pop off the last arg if it's a hash, which becomes our opts
  if args.last.is_a?(Hash)
    opts = args.pop
  else
    opts = {}
  end

  if args.last.is_a?(String)
    code = args.pop
  else
    code = nil
  end

  mentos(:lexer_name_for, args, opts, code)
end

#lexersObject

Public: Get all lexers from a serialized array. This avoids needing to spawn mentos when it’s not really needed (e.g,. one-off jobs, loading the Rails env, etc).

Should be preferred to #lexers!

Returns an array of lexers



119
120
121
122
123
124
125
126
127
# File 'lib/pygments/popen.rb', line 119

def lexers
  begin
    lexer_file = File.expand_path('../../../lexers', __FILE__)
    raw = File.open(lexer_file, "r").read
    Marshal.load(raw)
  rescue Errno::ENOENT
    raise MentosError, "Error loading lexer file. Was it created and vendored?"
  end
end

#lexers!Object

Public: Get back all available lexers from mentos itself

Returns an array of lexers



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/pygments/popen.rb', line 132

def lexers!
  mentos(:get_all_lexers).inject(Hash.new) do |hash, lxr|
    name = lxr[0]
    hash[name] = {
      :name => name,
      :aliases => lxr[1],
      :filenames => lxr[2],
      :mimetypes => lxr[3]
    }
    hash["dasm16"] = {:name=>"dasm16", :aliases=>["DASM16"], :filenames=>["*.dasm16", "*.dasm"], :mimetypes=>['text/x-dasm16']}
    hash["Puppet"] = {:name=>"Puppet", :aliases=>["puppet"], :filenames=>["*.pp"], :mimetypes=>[]}
    hash["Augeas"] = {:name=>"Augeas", :aliases=>["augeas"], :filenames=>["*.aug"], :mimetypes=>[]}
    hash["TOML"]   = {:name=>"TOML",   :aliases=>["toml"],   :filenames=>["*.toml"], :mimetypes=>[]}
    hash["Slash"]  = {:name=>"Slash",  :aliases=>["slash"],  :filenames=>["*.sl"], :mimetypes=>[]}
    hash
  end
end

#python_binaryObject

Detect a suitable Python binary to use. We can’t just use ‘python2` because apparently some old versions of Debian only have `python` or something like that.



47
48
49
50
51
52
# File 'lib/pygments/popen.rb', line 47

def python_binary
  @python_binary ||= begin
    `which python2`
    $?.success? ? "python2" : "python"
  end
end

#start(pygments_path = File.expand_path('../../../vendor/pygments-main/', __FILE__)) ⇒ Object

Get things started by opening a pipe to mentos (the freshmaker), a Python process that talks to the Pygments library. We’ll talk back and forth across this pipe.



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/pygments/popen.rb', line 22

def start(pygments_path = File.expand_path('../../../vendor/pygments-main/', __FILE__))
  is_windows = RUBY_PLATFORM =~ /mswin|mingw/
  begin
    @log = Logger.new(ENV['MENTOS_LOG'] ||= is_windows ? 'NUL:' : '/dev/null')
    @log.level = Logger::INFO
    @log.datetime_format = "%Y-%m-%d %H:%M "
  rescue
    @log = Logger.new(is_windows ? 'NUL:' : '/dev/null')
  end

  ENV['PYGMENTS_PATH'] = pygments_path

  # Make sure we kill off the child when we're done
  at_exit { stop "Exiting" }

  # A pipe to the mentos python process. #popen4 gives us
  # the pid and three IO objects to write and read.
  script = "#{python_binary} #{File.expand_path('../mentos.py', __FILE__)}"
  @pid, @in, @out, @err = popen4(script)
  @log.info "[#{Time.now.iso8601}] Starting pid #{@pid.to_s} with fd #{@out.to_i.to_s}."
end

#stop(reason) ⇒ Object

Stop the child process by issuing a kill -9.

We then call waitpid() with the pid, which waits for that particular child and reaps it.

kill() can set errno to ESRCH if, for some reason, the file is gone; regardless the final outcome of this method will be to set our @pid variable to nil.

Technically, kill() can also fail with EPERM or EINVAL (wherein the signal isn’t sent); but we have permissions, and we’re not doing anything invalid here.



66
67
68
69
70
71
72
73
74
75
76
# File 'lib/pygments/popen.rb', line 66

def stop(reason)
  if @pid
    begin
      Process.kill('KILL', @pid)
      Process.waitpid(@pid)
    rescue Errno::ESRCH, Errno::ECHILD
    end
  end
  @log.info "[#{Time.now.iso8601}] Killing pid: #{@pid.to_s}. Reason: #{reason}"
  @pid = nil
end

#stylesObject

Public: Return an array of all available styles



156
157
158
# File 'lib/pygments/popen.rb', line 156

def styles
  mentos(:get_all_styles)
end